-
-
Notifications
You must be signed in to change notification settings - Fork 174
WIP Freightcom new REST API #766
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
Closed
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
70ca42f
Add support for Freightcom API key configuration.
jacobshilitz 2fb3b06
Remove unused Freightcom sample files and schemas.
jacobshilitz 00ffabd
Add new Freightcom JSON schemas and update code generation
jacobshilitz 1c9e42e
Implement Freightcom Rest rate API
jacobshilitz 2cb671b
Implement Freightcom Ship and Cancel REST API
jacobshilitz 714a749
Remove username and password from Freightcom settings.
jacobshilitz d11bf1d
Update Freightcom connection configs and payment ID handling
jacobshilitz 52efee7
Refactor payment method handling in Freightcom integration
jacobshilitz 9537628
Refactor Freightcom provider function names and responses.
jacobshilitz b7a6a0b
Fix Freightcom tests
jacobshilitz 92be0a7
Fix Freightcom proxy to show the right Capabilities
jacobshilitz 09300ac
Remove not needed changes to db
jacobshilitz 4cbd256
Refactor payment method handling in Freightcom integration
jacobshilitz 2820f60
Remove debug print and adjust Freightcom gateway config structure.
jacobshilitz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,12 +1,21 @@ | ||
| SCHEMAS=./vendor/schemas | ||
| SCHEMAS=./schemas | ||
| LIB_MODULES=./karrio/schemas/freightcom | ||
| echo `pwd` | ||
| find "${LIB_MODULES}" -name "*.py" -exec rm -r {} \; | ||
| touch "${LIB_MODULES}/__init__.py" | ||
|
|
||
| generateDS --no-namespace-defs -o "${LIB_MODULES}/quote_request.py" $SCHEMAS/quote_request.xsd | ||
| generateDS --no-namespace-defs -o "${LIB_MODULES}/quote_reply.py" $SCHEMAS/quote_reply.xsd | ||
| generateDS --no-namespace-defs -o "${LIB_MODULES}/shipping_request.py" $SCHEMAS/shipping_request.xsd | ||
| generateDS --no-namespace-defs -o "${LIB_MODULES}/shipping_reply.py" $SCHEMAS/shipping_reply.xsd | ||
| generateDS --no-namespace-defs -o "${LIB_MODULES}/error.py" $SCHEMAS/error.xsd | ||
| generateDS --no-namespace-defs -o "${LIB_MODULES}/shipment_cancel_request.py" $SCHEMAS/shipment_cancel_request.xsd | ||
| generateDS --no-namespace-defs -o "${LIB_MODULES}/shipment_cancel_reply.py" $SCHEMAS/shipment_cancel_reply.xsd | ||
| quicktype() { | ||
| echo "Generating $1..." | ||
| docker run -it --rm --name quicktype -v $PWD:/app -e SCHEMAS=/app/schemas -e LIB_MODULES=/app/karrio/schemas/freightcom \ | ||
| karrio/tools /quicktype/script/quicktype --no-uuids --no-date-times --no-enums --src-lang json --lang jstruct \ | ||
| --all-properties-optional --type-as-suffix $@ | ||
| } | ||
|
|
||
|
|
||
| quicktype --src="${SCHEMAS}/rate_request.json" --out="${LIB_MODULES}/rate_request.py" | ||
| quicktype --src="${SCHEMAS}/rate_response.json" --out="${LIB_MODULES}/rate_response.py" | ||
| quicktype --src="${SCHEMAS}/error_response.json" --out="${LIB_MODULES}/error_response.py" | ||
| quicktype --src="${SCHEMAS}/shipping_request.json" --out="${LIB_MODULES}/shipping_request.py" | ||
| quicktype --src="${SCHEMAS}/shipping_response.json" --out="${LIB_MODULES}/shipping_response.py" | ||
| quicktype --src="${SCHEMAS}/pickup_request.json" --out="${LIB_MODULES}/pickup_request.py" | ||
| quicktype --src="${SCHEMAS}/tracking_response.json" --out="${LIB_MODULES}/tracking_response.py" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
128 changes: 98 additions & 30 deletions
128
modules/connectors/freightcom/karrio/mappers/freightcom/proxy.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,38 +1,106 @@ | ||
| from karrio.core.utils import request as http, XP | ||
| from karrio.api.proxy import Proxy as BaseProxy | ||
| """Karrio Freightcom client proxy.""" | ||
|
|
||
| import time | ||
| import karrio.lib as lib | ||
| import karrio.api.proxy as proxy | ||
| from karrio.mappers.freightcom.settings import Settings | ||
| from karrio.core.utils.serializable import Serializable, Deserializable | ||
|
|
||
| MAX_RETRIES = 10 | ||
| POLL_INTERVAL = 2 # seconds | ||
|
|
||
| class Proxy(BaseProxy): | ||
| class Proxy(proxy.Proxy): | ||
| settings: Settings | ||
|
|
||
| def get_rates(self, request: Serializable) -> Deserializable: | ||
| response = http( | ||
| url=self.settings.server_url, | ||
| data=request.serialize(), | ||
| trace=self.trace_as("xml"), | ||
| method="POST", | ||
| headers={"Content-Type": "application/xml"}, | ||
| def get_rates(self, request: lib.Serializable) -> lib.Deserializable: | ||
| # Step 1: Submit rate request and get quote ID | ||
| response = self._send_request( | ||
| path="/rate", request=lib.Serializable(request.value, lib.to_json) | ||
| ) | ||
|
|
||
| rate_id = lib.to_dict(response).get('request_id') | ||
| if not rate_id: | ||
| return lib.Deserializable(response, lib.to_dict) | ||
|
|
||
| # Step 2: Poll for rate results | ||
| for _ in range(MAX_RETRIES): | ||
| status_res = self._send_request( | ||
| path=f"/rate/{rate_id}", | ||
| method="GET" | ||
| ) | ||
|
|
||
| status = lib.to_dict(status_res).get('status', {}).get('done', False) | ||
|
|
||
| if status: # Quote is complete | ||
| return lib.Deserializable(status_res, lib.to_dict) | ||
|
|
||
| time.sleep(POLL_INTERVAL) | ||
|
|
||
| # If we exceed max retries | ||
| return lib.Deserializable({ | ||
| 'message': 'Rate calculation timed out' | ||
| }, lib.to_dict) | ||
|
|
||
| def create_shipment(self, request: lib.Serializable) -> lib.Deserializable: | ||
|
|
||
| response = self._send_request( | ||
| path="/shipment", request=lib.Serializable(request.value, lib.to_json) | ||
| ) | ||
|
|
||
| shipment_id = lib.to_dict(response).get('id') | ||
| if not shipment_id: | ||
| return lib.Deserializable(response, lib.to_dict) | ||
|
|
||
|
|
||
| # Step 2: retry because api return empty bytes if done to fast | ||
| time.sleep(1) | ||
| for _ in range(MAX_RETRIES): | ||
|
|
||
| shipment_response = self._send_request(path=f"/shipment/{shipment_id}", method="GET") | ||
| shipment_res = lib.failsafe(lambda :lib.to_dict(shipment_response)) or lib.decode(shipment_response) | ||
|
|
||
| if shipment_res: # is complete | ||
| return lib.Deserializable(shipment_res, lib.to_dict, request.ctx) | ||
|
|
||
| time.sleep(POLL_INTERVAL) | ||
|
|
||
| # If we exceed max retries | ||
| return lib.Deserializable({ | ||
| 'message': 'timed out' | ||
| }, lib.to_dict) | ||
|
|
||
|
|
||
| def get_tracking(self, request: lib.Serializable) -> lib.Deserializable[str]: | ||
| response = self._send_request(path=f"/shipment/{request.serialize()}/tracking-events") | ||
|
|
||
| return lib.Deserializable(response, lib.to_dict) | ||
|
|
||
| def _get_payments_methods(self) -> lib.Deserializable[str]: | ||
| response = self._send_request( | ||
| path="/finance/payment-methods", | ||
| method="GET" | ||
| ) | ||
| return Deserializable(response, XP.to_xml) | ||
|
|
||
| def create_shipment(self, request: Serializable) -> Deserializable: | ||
| response = http( | ||
| url=self.settings.server_url, | ||
| data=request.serialize(), | ||
| trace=self.trace_as("xml"), | ||
| method="POST", | ||
| headers={"Content-Type": "application/xml"}, | ||
| return lib.Deserializable(response, lib.to_dict) | ||
|
|
||
| def cancel_shipment(self, request: lib.Serializable) -> lib.Deserializable: | ||
| response = self._send_request( | ||
| path=f"/shipment/{request.serialize()}", method="DELETE" | ||
| ) | ||
| return Deserializable(response, XP.to_xml) | ||
|
|
||
| def cancel_shipment(self, request: Serializable) -> Deserializable: | ||
| response = http( | ||
| url=self.settings.server_url, | ||
| data=request.serialize(), | ||
| trace=self.trace_as("xml"), | ||
| method="POST", | ||
| headers={"Content-Type": "application/xml"}, | ||
| return lib.Deserializable(response if any(response) else "{}", lib.to_dict) | ||
|
|
||
| def _send_request( | ||
| self, path: str, request: lib.Serializable = None, method: str = "POST" | ||
| ) -> str: | ||
|
|
||
| data: dict = dict(data=request.serialize()) if request is not None else dict() | ||
| return lib.request( | ||
| **{ | ||
| "url": f"{self.settings.server_url}{path}", | ||
| "trace": self.trace_as("json"), | ||
| "method": method, | ||
| "headers": { | ||
| "Content-Type": "application/json", | ||
| "Authorization": self.settings.api_key, | ||
| }, | ||
| **data, | ||
| } | ||
| ) | ||
| return Deserializable(response, XP.to_xml) | ||
10 changes: 5 additions & 5 deletions
10
modules/connectors/freightcom/karrio/mappers/freightcom/settings.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 12 additions & 5 deletions
17
modules/connectors/freightcom/karrio/providers/freightcom/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,13 @@ | ||
| from karrio.providers.freightcom.quote import parse_quote_reply, quote_request | ||
| from karrio.providers.freightcom.shipping import ( | ||
| parse_shipping_reply, | ||
| shipping_request, | ||
|
|
||
| from karrio.providers.freightcom.rate import parse_rate_response, rate_request | ||
| from karrio.providers.freightcom.shipment import ( | ||
| parse_shipment_response, | ||
| shipment_request, | ||
| parse_shipment_cancel_response, | ||
| shipment_cancel_request, | ||
| ) | ||
| from karrio.providers.freightcom.void_shipment import shipment_cancel_request, parse_shipment_cancel_reply | ||
|
|
||
| # from karrio.providers.eshipper.tracking import ( | ||
| # parse_tracking_response, | ||
| # tracking_request, | ||
| # ) |
55 changes: 22 additions & 33 deletions
55
modules/connectors/freightcom/karrio/providers/freightcom/error.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,40 +1,29 @@ | ||
| from typing import List | ||
| from karrio.schemas.freightcom.error import ErrorType | ||
| from karrio.schemas.freightcom.quote_reply import CarrierErrorMessageType | ||
| from karrio.core.models import Message | ||
| from karrio.core.utils import Element, XP | ||
| from karrio.providers.freightcom.utils import Settings | ||
| import typing | ||
| import karrio.core.models as models | ||
| import karrio.providers.freightcom.utils as provider_utils | ||
|
|
||
| def parse_error_response( | ||
| response: dict, | ||
| settings: provider_utils.Settings, | ||
| **kwargs, | ||
| ) -> typing.List[models.Message]: | ||
| responses = response if isinstance(response, list) else [response] | ||
|
|
||
| def parse_error_response(response: Element, settings: Settings) -> List[Message]: | ||
| errors = XP.find("Error", response, ErrorType) | ||
| carrier_errors = XP.find("CarrierErrorMessage", response, CarrierErrorMessageType) | ||
| errors = [ | ||
| *[_ for _ in responses if _.get("message")], | ||
| ] | ||
|
|
||
| return [ | ||
| *[_extract_error(er, settings) for er in errors if er.Message != ""], | ||
| *[ | ||
| _extract_carrier_error(er, settings) | ||
| for er in carrier_errors | ||
| if er.errorMessage0 != "" | ||
| ], | ||
| models.Message( | ||
| carrier_id=settings.carrier_id, | ||
| carrier_name=settings.carrier_name, | ||
| message=error.get("message"), | ||
| details={ | ||
| **kwargs, | ||
| **(error.get('data', {})) | ||
| }, | ||
| ) | ||
| for error in errors | ||
| ] | ||
|
|
||
|
|
||
| def _extract_carrier_error( | ||
| error: CarrierErrorMessageType, settings: Settings | ||
| ) -> Message: | ||
| return Message( | ||
| code="CarrierErrorMessage", | ||
| carrier_name=settings.carrier_name, | ||
| carrier_id=settings.carrier_id, | ||
| message=error.errorMessage0, | ||
| ) | ||
|
|
||
|
|
||
| def _extract_error(error: ErrorType, settings: Settings) -> Message: | ||
| return Message( | ||
| code="Error", | ||
| carrier_name=settings.carrier_name, | ||
| carrier_id=settings.carrier_id, | ||
| message=error.Message, | ||
| ) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.