Esempio n. 1
0
    def get_customer(self, overrides=None):
        """
        Fetches a single service member's orders
        """
        # If id was provided, get that specific one. Else get any stored one.
        object_id = overrides.get("id") if overrides else None
        order = self.get_stored(ORDER, object_id)
        if order is None:
            logger.info("Skipping get customer no order is stored yet")
            return

        url, request_kwargs = self.request_preparer.prep_ghc_request(
            endpoint=f"/customer/{order['customerID']}",
            endpoint_name="/customer/{customerId}",
        )

        with self.rest(method="GET", url=url, **request_kwargs) as resp:
            resp: RestResponseContextManager

            log_response_info(response=resp)

            if resp.status_code == HTTPStatus.OK:
                self.add_stored(CUSTOMER, resp.js)
            else:
                resp.failure("Unable get customer.")

                log_response_failure(response=resp)
Esempio n. 2
0
    def get_moves_queue(self, overrides=None):
        """
        Fetches a list of paginated moves
        """
        # Use the default queue sort for now, adding a filter to return service counseling completed
        # moves as well
        url, request_kwargs = self.request_preparer.prep_ghc_request(
            endpoint="/queues/counseling?page=1&perPage=50&sort=submittedAt&order=asc&status=NEEDS SERVICE COUNSELING,SERVICE COUNSELING COMPLETED",
            endpoint_name="/queues/counseling",
        )

        with self.rest(method="GET", url=url, **request_kwargs) as resp:
            resp: RestResponseContextManager

            log_response_info(response=resp)

            if resp.status_code == HTTPStatus.OK:
                moves: JSONObject = resp.js

                # these are not full move models and will be those in needs counseling status
                self.add_stored(MOVE_TASK_ORDER, moves["queueMoves"])
            else:
                resp.failure("Unable to get moves queue.")

                log_response_failure(response=resp)
Esempio n. 3
0
    def get_user_info(self) -> Optional[JSONObject]:
        """
        Gets the user info for the currently logged-in user.
        :return: logged-in user, or None if there is an error
        """
        url, request_kwargs = self.request_preparer.prep_internal_request(
            endpoint="/users/logged_in")

        with self.rest(method="GET", url=url, **request_kwargs) as resp:
            resp: RestResponseContextManager

            log_response_info(response=resp)

            if resp.status_code == HTTPStatus.OK:
                user: JSONObject = resp.js

                logger.info(f"ℹ️ User email: {user['email']}")

                return user
            else:
                resp.failure("Unable to get logged-in user.")

                log_response_failure(response=resp)

                return
Esempio n. 4
0
    def get_service_items(self, overrides=None):
        """
        Fetches all of the mto service items for a given move id
        """
        # If id was provided, get that specific one. Else get any stored one.
        object_id = overrides.get("id") if overrides else None
        move = self.get_stored(MOVE_TASK_ORDER, object_id)
        if move is None:
            logger.info("Skipping get service items no moves are stored yet")
            return

        url, request_kwargs = self.request_preparer.prep_ghc_request(
            endpoint=f"/move_task_orders/{move['id']}/mto_service_items",
            endpoint_name="/move_task_orders/{moveId}/mto_service_items",
        )

        with self.rest(method="GET", url=url, **request_kwargs) as resp:
            resp: RestResponseContextManager

            log_response_info(response=resp)

            if resp.status_code == HTTPStatus.OK:
                self.add_stored(MTO_SERVICE_ITEM, resp.js)
            else:
                resp.failure("Unable to get service items.")

                log_response_failure(response=resp)
Esempio n. 5
0
    def get_orders(self, overrides=None):
        """
        Fetches a single service member's orders
        """
        # If id was provided, get that specific one. Else get any stored one.
        object_id = overrides.get("id") if overrides else None
        move = self.get_stored(MOVE_TASK_ORDER, object_id)
        if move is None:
            logger.info("Skipping get orders no move is stored yet")
            return

        orders_id = move.get("ordersId")
        if orders_id is None:
            move = self.get_move({"id": move["id"]})

        url, request_kwargs = self.request_preparer.prep_ghc_request(
            endpoint=f"/orders/{move['ordersId']}",
            endpoint_name="/orders/{orderId}",
        )

        with self.rest(method="GET", url=url, **request_kwargs) as resp:
            resp: RestResponseContextManager

            log_response_info(response=resp)

            if resp.status_code == HTTPStatus.OK:
                self.add_stored(ORDER, resp.js)
            else:
                resp.failure("Unable to get orders.")

                log_response_failure(response=resp)
Esempio n. 6
0
    def get_moves_queue(self, overrides=None):
        """
        Fetches a list of paginated moves
        """
        url, request_kwargs = self.request_preparer.prep_ghc_request(
            endpoint="/queues/moves?page=1&perPage=50&sort=status&order=asc",
            endpoint_name="/queues/moves",
        )

        with self.rest(method="GET", url=url, **request_kwargs) as resp:
            resp: RestResponseContextManager

            log_response_info(response=resp)

            if resp.status_code == HTTPStatus.OK:
                moves: JSONObject = resp.js

                self.add_stored(MOVE_TASK_ORDER, moves["queueMoves"])

                # destination duty locations don't have to be in the office user's GBLOC
                destination_duty_location_ids = [move["destinationDutyLocation"]["id"] for move in moves["queueMoves"]]

                self.default_mto_ids["destinationDutyLocationID"].update(destination_duty_location_ids)
            else:
                resp.failure("Unable to get moves queue.")

                log_response_failure(response=resp)
Esempio n. 7
0
    def get_move(self, overrides=None):
        """
        Fetches a single move
        """
        # If id was provided, get that specific one. Else get any stored one.
        object_id = overrides.get("id") if overrides else None
        move = self.get_stored(MOVE_TASK_ORDER, object_id)
        if move is None:
            logger.info("Skipping get move none are stored yet")
            return

        url, request_kwargs = self.request_preparer.prep_ghc_request(
            endpoint=f"/move/{move['locator']}",
            endpoint_name="/move/{locator}",
        )

        with self.rest(method="GET", url=url, **request_kwargs) as resp:
            resp: RestResponseContextManager

            log_response_info(response=resp)

            if resp.status_code == HTTPStatus.OK:
                new_mto: JSONObject = resp.js

                self.add_stored(MOVE_TASK_ORDER, new_mto)

                return new_mto
            else:
                resp.failure("Unable to get move.")

                log_response_failure(response=resp)
Esempio n. 8
0
    def update_orders(self, overrides=None):
        """
        Updates an existing order of a move as a TOO
        :return:
        """
        object_id = overrides.get("id") if overrides else None
        order = self.get_stored(ORDER, object_id)
        order = self.get_orders() if order is None else order
        if order is None:
            logger.info("skipping update order, no moves exist yet")
            return

        move = self.get_stored(MOVE_TASK_ORDER, order["moveTaskOrderID"])
        if move and move["status"] == "NEEDS SERVICE COUNSELING":
            logger.info("Skipping update orders as TOO, move is still in services counseling")
            return

        # require all optional fields otherwise nullable fields will be omitted
        payload = fake_data_generator.generate_fake_request_data(
            api_key=APIKey.OFFICE,
            path="/orders/{orderID}",
            method="patch",
            require_all=True,
        )

        if self.default_mto_ids.get("originDutyLocationID"):
            payload["originDutyLocationId"] = random.choice(list(self.default_mto_ids["originDutyLocationID"]))
        else:
            payload["originDutyLocationId"] = order["originDutyLocation"]["id"]

        if self.default_mto_ids.get("destinationDutyLocationID"):
            payload["newDutyLocationId"] = random.choice(list(self.default_mto_ids["destinationDutyLocationID"]))
        else:
            payload["newDutyLocationId"] = order["destinationDutyLocation"]["id"]

        url, request_kwargs = self.request_preparer.prep_ghc_request(
            endpoint=f"/orders/{order['id']}",
            endpoint_name="/orders/{orderId}",
        )

        request_kwargs["headers"]["If-Match"] = order["eTag"]

        with self.rest(method="PATCH", url=url, data=json.dumps(payload), **request_kwargs) as resp:
            resp: RestResponseContextManager

            log_response_info(response=resp)

            if resp.status_code == HTTPStatus.OK:
                new_order: JSONObject = resp.js

                self.add_stored(ORDER, new_order)
                self.add_stored(CUSTOMER, new_order["customer"])
            else:
                resp.failure("Unable to update orders.")

                log_response_failure(response=resp)
Esempio n. 9
0
    def test_includes_reason_in_log(self, mock_logger: MagicMock) -> None:
        mock_response = create_autospec(Response)
        mock_response.status_code = 500
        mock_response.reason = "Server Error"

        log_response_info(response=mock_response)

        mock_logger.info.assert_called_once()

        assert mock_response.reason in mock_logger.info.call_args[0][0]
Esempio n. 10
0
    def test_includes_status_code_in_log(self, mock_logger: MagicMock) -> None:
        mock_response = create_autospec(Response)
        mock_response.status_code = 200
        mock_response.reason = ""

        log_response_info(response=mock_response)

        mock_logger.info.assert_called_once()

        assert str(
            mock_response.status_code) in mock_logger.info.call_args[0][0]
Esempio n. 11
0
    def test_defaults_to_include_calling_function_name_in_log(
            self, mock_logger: MagicMock) -> None:
        mock_response = create_autospec(Response)
        mock_response.status_code = 200
        mock_response.reason = ""

        log_response_info(response=mock_response)

        mock_logger.info.assert_called_once()

        assert "test_defaults_to_include_calling_function_name_in_log" in mock_logger.info.call_args[
            0][0]
Esempio n. 12
0
    def test_can_override_task_name_in_log(self,
                                           mock_logger: MagicMock) -> None:
        mock_response = create_autospec(Response)
        mock_response.status_code = 200
        mock_response.reason = ""

        task_name = "my_task"
        log_response_info(response=mock_response, task_name=task_name)

        mock_logger.info.assert_called_once()

        assert task_name in mock_logger.info.call_args[0][0]
        assert "test_can_override_task_name_in_log" not in mock_logger.info.call_args[
            0][0]
Esempio n. 13
0
    def update_orders(self, overrides=None):
        """
        Updates an existing order of a move as a Services Counselor.
        :return:
        """
        object_id = overrides.get("id") if overrides else None
        order = self.get_stored(ORDER, object_id)
        order = self.get_orders() if order is None else order
        if order is None:
            logger.info("skipping update order, no moves exist yet")
            return

        payload = fake_data_generator.generate_fake_request_data(
            api_key=APIKey.OFFICE,
            path="/counseling/orders/{orderID}",
            method="patch",
        )

        payload = {key: payload[key] for key in ["issueDate", "reportByDate", "ordersType"]}
        if self.default_mto_ids.get("originDutyLocationID"):
            payload["originDutyLocationId"] = random.choice(list(self.default_mto_ids["originDutyLocationID"]))
        else:
            payload["originDutyLocationId"] = order["originDutyLocation"]["id"]

        if self.default_mto_ids.get("destinationDutyLocationID"):
            payload["newDutyLocationId"] = random.choice(list(self.default_mto_ids["destinationDutyLocationID"]))
        else:
            payload["newDutyLocationId"] = order["destinationDutyLocation"]["id"]

        # The request may result in validation errors if the underlying move is no longer in needs counseling status
        url, request_kwargs = self.request_preparer.prep_ghc_request(
            endpoint=f"/counseling/orders/{order['id']}",
            endpoint_name="/counseling/orders/{orderId}",
        )

        request_kwargs["headers"]["If-Match"] = order["eTag"]

        with self.rest(method="PATCH", url=url, data=json.dumps(payload), **request_kwargs) as resp:
            resp: RestResponseContextManager

            log_response_info(response=resp)

            if resp.status_code == HTTPStatus.OK:
                self.add_stored(ORDER, resp.js)
            else:
                resp.failure("Unable to update orders.")

                log_response_failure(response=resp)
Esempio n. 14
0
    def update_allowance(self, overrides=None):
        """
        Updates the existing entitlements of an order as a TOO
        :return:
        """
        object_id = overrides.get("id") if overrides else None
        order = self.get_stored(ORDER, object_id)
        order = self.get_orders() if order is None else order
        if order is None:
            logger.info("skipping update allowance, no moves exist yet")
            return

        payload = fake_data_generator.generate_fake_request_data(
            api_key=APIKey.OFFICE,
            path="/orders/{orderID}/allowances",
            method="patch",
        )

        # update allowances handler expects the orders eTag because it also updates parents order fields
        url, request_kwargs = self.request_preparer.prep_ghc_request(
            endpoint=f"/orders/{order['id']}/allowances",
            endpoint_name="/orders/{orderId}/allowances",
        )

        request_kwargs["headers"]["If-Match"] = order["eTag"]

        with self.rest(method="PATCH", url=url, data=json.dumps(payload), **request_kwargs) as resp:
            resp: RestResponseContextManager

            log_response_info(response=resp)

            if resp.status_code == HTTPStatus.OK:
                new_order: JSONObject = resp.js

                self.add_stored(ORDER, new_order)

                self.add_stored(CUSTOMER, new_order["customer"])
            else:
                resp.failure("Unable to update allowance.")

                log_response_failure(response=resp)
Esempio n. 15
0
    def update_customer(self, overrides=None):
        """
        Updates the existing service member of an order as a Services Counselor.
        :return:
        """
        object_id = overrides.get("id") if overrides else None
        customer = self.get_stored(CUSTOMER, object_id) or self.get_customer()
        if customer is None:
            logger.info("skipping update customer, no orders exist yet")
            return

        if not customer.get("current_address"):
            logger.info("Skipping update customer as no current address exists")
            return

        payload = fake_data_generator.generate_fake_request_data(
            api_key=APIKey.OFFICE,
            path="/customer/{customerID}",
            method="patch",
            overrides={"current_address": {"id": customer["current_address"]["id"]}},
            require_all=True,
        )

        url, request_kwargs = self.request_preparer.prep_ghc_request(
            endpoint=f"/customer/{customer['id']}",
            endpoint_name="/customer/{customerID}",
        )

        request_kwargs["headers"]["If-Match"] = customer["eTag"]

        with self.rest(method="PATCH", url=url, data=json.dumps(payload), **request_kwargs) as resp:
            resp: RestResponseContextManager

            log_response_info(response=resp)

            if resp.status_code == HTTPStatus.OK:
                self.add_stored(CUSTOMER, resp.js)
            else:
                resp.failure("Unable to update customer.")

                log_response_failure(response=resp)
Esempio n. 16
0
    def get_orders(self, overrides=None):
        """
        Fetches a single service member's orders
        """
        # If id was provided, get that specific one. Else get any stored one.
        object_id = overrides.get("id") if overrides else None
        move = self.get_stored(MOVE_TASK_ORDER, object_id)
        if move is None:
            logger.info("Skipping get orders no move is stored yet")
            return

        orders_id = move.get("ordersId")
        if orders_id is None:
            move = self.get_move({"id": move["id"]})

        url, request_kwargs = self.request_preparer.prep_ghc_request(
            endpoint=f"/orders/{move['ordersId']}",
            endpoint_name="/orders/{orderId}",
        )

        with self.rest(method="GET", url=url, **request_kwargs) as resp:
            resp: RestResponseContextManager

            log_response_info(response=resp)

            if resp.status_code == HTTPStatus.OK:
                order: JSONObject = resp.js

                self.add_stored(ORDER, order)

                # the origin duty location is not in the queue response and we can't use the destination
                # because they could be outside of the office user's GBLOC
                self.default_mto_ids["originDutyLocationID"].add(order["originDutyLocation"]["id"])
            else:
                resp.failure("Unable to get orders.")

                log_response_failure(response=resp)
Esempio n. 17
0
    def onboard_customer(self) -> None:
        """
        Goes through the basic on-boarding flow for a customer.

        We're going to be re-using the same endpoint for several requests in this workflow.
        Because of that, we won't use the api fake data generator because we would end up having
        to delete a lot of the data it gave back in order to simulate the step-by-step process
        that on-boarding actually takes. Instead, we'll use the lower-level `MilMoveData` which is
        what the api fake data generate uses internally because it gives us more control. It's
        essentially a wrapper around faker, but we'll still use it to remain more consistent in
        the faker helper functions we use for different data types.

        Another thing to note is that because we'll be hitting a lot of endpoints in this load test,
        we'll pass task_name values to the logging functions whenever we hit an endpoint to make it
        easier to see in the logs what is happening.
        """
        user = self.get_user_info()

        if user is None:
            return  # get_user_info will log the error

        service_member_id = user["service_member"]["id"]

        milmove_faker = MilMoveData()

        # First let's create the profile.
        payload = {
            "id": service_member_id,
            "affiliation": "ARMY",
            "edipi": "1234567890",
            "rank": "E_1",
        }

        sm_url, sm_request_kwargs = self.request_preparer.prep_internal_request(
            endpoint=f"/service_members/{service_member_id}",
            endpoint_name="/service_members/{serviceMemberId}",
        )

        with self.rest(method="PATCH",
                       url=sm_url,
                       data=json.dumps(payload),
                       **sm_request_kwargs) as resp:
            resp: RestResponseContextManager

            task_name = "create_user_profile"

            log_response_info(response=resp, task_name=task_name)

            if resp.status_code != HTTPStatus.OK:
                resp.failure("Unable to create customer profile.")

                log_response_failure(response=resp, task_name=task_name)

                return

        # Now we can add name info. The url and request_kwargs will actually be the same, so we only
        # need to prepare the payload.

        suffix = random.choice([
            milmove_faker.get_fake_data_for_type(data_type=DataType.SUFFIX), ""
        ])

        payload = {
            "id":
            service_member_id,
            "first_name":
            milmove_faker.get_fake_data_for_type(
                data_type=DataType.FIRST_NAME),
            "middle_name":
            milmove_faker.get_fake_data_for_type(
                data_type=DataType.MIDDLE_NAME),
            "last_name":
            milmove_faker.get_fake_data_for_type(data_type=DataType.LAST_NAME),
            "suffix":
            suffix,
        }

        with self.rest(method="PATCH",
                       url=sm_url,
                       data=json.dumps(payload),
                       **sm_request_kwargs) as resp:
            resp: RestResponseContextManager

            task_name = "add_name_info"

            log_response_info(response=resp, task_name=task_name)

            if resp.status_code != HTTPStatus.OK:
                resp.failure("Unable to add customer name.")

                log_response_failure(response=resp, task_name=task_name)

                return

        # Now we can add contact info. Again, url and request_kwargs will be the same, so only need
        # a payload.

        payload = {
            "id":
            service_member_id,
            "telephone":
            milmove_faker.get_fake_data_for_type(data_type=DataType.PHONE),
            "secondary_telephone":
            milmove_faker.get_fake_data_for_type(data_type=DataType.PHONE),
            "personal_email":
            milmove_faker.get_fake_data_for_type(data_type=DataType.EMAIL),
            "email_is_preferred":
            milmove_faker.get_fake_data_for_type(data_type=DataType.BOOLEAN),
            "phone_is_preferred":
            milmove_faker.get_fake_data_for_type(data_type=DataType.BOOLEAN),
        }

        with self.rest(method="PATCH",
                       url=sm_url,
                       data=json.dumps(payload),
                       **sm_request_kwargs) as resp:
            resp: RestResponseContextManager

            task_name = "add_contact_info"

            log_response_info(response=resp, task_name=task_name)

            if resp.status_code != HTTPStatus.OK:
                resp.failure("Unable to add customer contact info.")

                log_response_failure(response=resp, task_name=task_name)

                return

        # Next we need a duty location

        duty_location_url, duty_location_request_kwargs = self.request_preparer.prep_internal_request(
            endpoint="/duty_locations?search=fort")

        with self.rest(method="GET",
                       url=duty_location_url,
                       **duty_location_request_kwargs) as resp:
            resp: RestResponseContextManager

            task_name = "search_duty_locations"

            log_response_info(response=resp, task_name=task_name)

            if resp.status_code == HTTPStatus.OK:
                duty_locations: JSONArray = resp.js
            else:
                resp.failure("Unable to find duty locations.")

                log_response_failure(response=resp, task_name=task_name)

                return

        # Don't care too much about which one for this load test, so just grabbing the first one.
        origin_duty_location = duty_locations[0]

        payload = {
            "id": service_member_id,
            "current_location_id": origin_duty_location["id"]
        }

        with self.rest(method="PATCH",
                       url=sm_url,
                       data=json.dumps(payload),
                       **sm_request_kwargs) as resp:
            resp: RestResponseContextManager

            task_name = "add_current_duty_location"

            log_response_info(response=resp, task_name=task_name)

            if resp.status_code != HTTPStatus.OK:
                resp.failure("Unable to add current duty location.")

                log_response_failure(response=resp, task_name=task_name)

                return

        # Next we'll input the current mailing address. We could use a fully random one, or base it
        # in part on where the duty location is, which we'll do.

        # First, we'll get the address of the duty location.
        address_url, address_request_kwargs = self.request_preparer.prep_internal_request(
            endpoint=f"/addresses/{origin_duty_location['address_id']}",
            endpoint_name="/addresses/{addressId}",
        )

        with self.rest(method="GET", url=address_url,
                       **address_request_kwargs) as resp:
            resp: RestResponseContextManager

            task_name = "get_duty_location_address"

            log_response_info(response=resp, task_name=task_name)

            if resp.status_code == HTTPStatus.OK:
                duty_location_address: JSONObject = resp.js
            else:
                resp.failure("Unable to find duty location address.")

                log_response_failure(response=resp, task_name=task_name)

                return

        # Zip code is validated as part of on-boarding.

        zip_code_url, zip_code_request_kwargs = self.request_preparer.prep_internal_request(
            endpoint=
            f"/rate_engine_postal_codes/{duty_location_address['postalCode']}?postal_code_type=origin",
            endpoint_name="/rate_engine_postal_codes/{postal_code}",
        )

        with self.rest(method="GET",
                       url=zip_code_url,
                       **zip_code_request_kwargs) as resp:
            resp: RestResponseContextManager

            task_name = "validate_zip_code"

            log_response_info(response=resp, task_name=task_name)

            if resp.status_code != HTTPStatus.OK:
                resp.failure("Unable to validate zip code.")

                log_response_failure(response=resp, task_name=task_name)

                return

        # Now we can add the current mailing address.

        payload = {
            "id": service_member_id,
            "residential_address": {
                "streetAddress1":
                milmove_faker.get_fake_data_for_type(
                    data_type=DataType.STREET_ADDRESS),
                "city":
                duty_location_address["city"],
                "state":
                duty_location_address["state"],
                "postalCode":
                duty_location_address["postalCode"],
            },
        }

        with self.rest(method="PATCH",
                       url=sm_url,
                       data=json.dumps(payload),
                       **sm_request_kwargs) as resp:
            resp: RestResponseContextManager

            task_name = "add_current_mailing_address"

            log_response_info(response=resp, task_name=task_name)

            if resp.status_code != HTTPStatus.OK:
                resp.failure("Unable to add current mailing address.")

                log_response_failure(response=resp, task_name=task_name)

                return

        # Next is the backup mailing address

        # We'll use mostly the same address as the current one, just changing out the street address
        payload["residential_address"][
            "streetAddress1"] = milmove_faker.get_fake_data_for_type(
                data_type=DataType.STREET_ADDRESS)

        with self.rest(method="PATCH",
                       url=sm_url,
                       data=json.dumps(payload),
                       **sm_request_kwargs) as resp:
            resp: RestResponseContextManager

            task_name = "add_backup_mailing_address"

            log_response_info(response=resp, task_name=task_name)

            if resp.status_code != HTTPStatus.OK:
                resp.failure("Unable to add backup mailing address.")

                log_response_failure(response=resp, task_name=task_name)

                return

        # Finally, we'll add the backup contact information.

        backup_contact_url, backup_contact_request_kwargs = self.request_preparer.prep_internal_request(
            endpoint=f"/service_members/{service_member_id}/backup_contacts",
            endpoint_name="/service_members/{serviceMemberId}/backup_contacts",
        )

        backup_contact_name = f"{milmove_faker.get_fake_data_for_type(data_type=DataType.FIRST_NAME)} {milmove_faker.get_fake_data_for_type(data_type=DataType.LAST_NAME)}"

        payload = {
            "name":
            backup_contact_name,
            "email":
            milmove_faker.get_fake_data_for_type(data_type=DataType.EMAIL),
            "telephone":
            milmove_faker.get_fake_data_for_type(data_type=DataType.PHONE),
            "permission":
            "NONE",
        }

        with self.rest(method="POST",
                       url=backup_contact_url,
                       data=json.dumps(payload),
                       **backup_contact_request_kwargs) as resp:
            resp: RestResponseContextManager

            task_name = "add_backup_contact"

            log_response_info(response=resp, task_name=task_name)

            if resp.status_code != HTTPStatus.CREATED:
                resp.failure("Unable to add backup contact info.")

                log_response_failure(response=resp, task_name=task_name)

                return