├── .editorconfig ├── .gitignore ├── .npmignore ├── .publishignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── __tests__ ├── .gitignore ├── e2e.test.js ├── test_aftership.js ├── test_courier.js ├── test_estimated_delivery_date.js ├── test_last_checkpoint.js ├── test_notification.js ├── test_tracking.js └── test_util.js ├── example ├── courier.js ├── last_checkpoint.js ├── notification.js └── tracking.js ├── jest.config.js ├── package.json ├── scripts └── ci │ ├── run.sh │ └── setup.sh ├── src ├── endpoint │ ├── courier_endpoint.ts │ ├── estimated_delivery_date_endpoint.ts │ ├── last_checkpoint_endpoint.ts │ ├── notification_endpoint.ts │ └── tracking_endpoint.ts ├── error │ ├── error.ts │ └── error_enum.ts ├── implementation │ ├── courier.ts │ ├── estimated_delivery_date.ts │ ├── last_checkpoint.ts │ ├── notification.ts │ └── tracking.ts ├── index.ts ├── lib │ ├── api_request.ts │ ├── auth_enum.ts │ ├── signature.ts │ ├── util.ts │ └── version.ts └── model │ ├── aftership_option.ts │ ├── aftership_response.ts │ ├── checkpoint │ └── checkpoint.ts │ ├── courier │ ├── courier.ts │ ├── courier_detect_list.ts │ ├── courier_detect_request.ts │ └── courier_list.ts │ ├── estimated_delivery_date │ ├── address.ts │ ├── estimated_delivery_date.ts │ ├── estimated_delivery_date_batch_predict_params.ts │ ├── estimated_pickup.ts │ └── weight.ts │ ├── last_checkpoint │ └── last_checkpoint.ts │ ├── notification │ ├── notification.ts │ └── notification_request.ts │ ├── rate_limit.ts │ └── tracking │ ├── custom_estimated_delivery_date.ts │ ├── delivery_status.ts │ ├── delivery_type.ts │ ├── first_estimated_delivery.ts │ ├── latest_estimated_delivery.ts │ ├── mark_as_complated_param.ts │ ├── multi_trackings_query_params.ts │ ├── next_courier.ts │ ├── single_tracking_param.ts │ ├── tracking.ts │ ├── tracking_create_params.ts │ ├── tracking_list.ts │ ├── tracking_query_params.ts │ └── tracking_update_params.ts ├── tsconfig.json ├── tslint.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # For more information about the properties used in 2 | # this file, please see the EditorConfig documentation: 3 | # http://editorconfig.org/ 4 | 5 | root = true 6 | 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | indent_style = space 11 | indent_size = 2 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | 18 | [{*.yml,package.json}] 19 | indent_size = 2 20 | # The indent style for `*.yml` and `package.json` must be space 21 | # http://www.yaml.org/spec/1.2/spec.html#id2777534 22 | # https://github.com/npm/npm/issues/4718 23 | indent_style = space 24 | 25 | [*.ts] 26 | quote_type = single 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------------ 2 | # Custom files for this repo 3 | # ------------------------------------------------------------------------------ 4 | 5 | 6 | 7 | # v1.0.6 2016-01-06 8 | coverage/ 9 | 10 | 11 | 12 | # =========================== DO NOT touch below v1.0.5 2015-12-14 =============================== 13 | 14 | 15 | # ------------------------------------------------------------------------------ 16 | # for aftership-publish making use of https://github.com/inikulin/publish-please 17 | # ------------------------------------------------------------------------------ 18 | .publishrc 19 | 20 | 21 | 22 | # =========================== DO NOT touch below v1.0.4 2015-05-06 =============================== 23 | 24 | 25 | # =============== log ignore 26 | builtAssets 27 | 28 | /tmp/* 29 | /builtAssets/* 30 | *.log 31 | 32 | 33 | # =========================== DO NOT touch below v1.0.3 =============================== 34 | 35 | 36 | node_modules 37 | 38 | # =============== RubyMine.gitignore 39 | # =============== PhPStorm.gitignore 40 | .idea 41 | 42 | 43 | # =============== OSX.gitignore 44 | .DS_Store 45 | .AppleDouble 46 | .LSOverride 47 | 48 | # Thumbnails 49 | ._* 50 | 51 | # =============== # Windows image file caches 52 | Thumbs.db 53 | ehthumbs.db 54 | 55 | # Folder config file 56 | Desktop.ini 57 | 58 | # Recycle Bin used on file shares 59 | $RECYCLE.BIN/ 60 | 61 | # Except the .htpassed 62 | !.htpasswd 63 | !.nodemonignore 64 | 65 | npm-debug.log 66 | 67 | dump.rdb 68 | 69 | 70 | 71 | # FROM https://github.com/niftylettuce/node-email-templates/blob/master/.gitignore 72 | 73 | # ------------------------------------------------------------------------------ 74 | # Common Node files and folders 75 | # https://github.com/github/gitignore/blob/master/Node.gitignore 76 | # ------------------------------------------------------------------------------ 77 | 78 | lib-cov 79 | *.seed 80 | *.log 81 | *.csv 82 | *.dat 83 | *.out 84 | *.pid 85 | *.gz 86 | 87 | pids 88 | logs 89 | results 90 | 91 | node_modules 92 | npm-debug.log 93 | package-lock.json 94 | 95 | 96 | # ------------------------------------------------------------------------------ 97 | # Nodemon 98 | # ------------------------------------------------------------------------------ 99 | 100 | .monitor 101 | 102 | 103 | # ------------------------------------------------------------------------------ 104 | # JSHint 105 | # ------------------------------------------------------------------------------ 106 | 107 | jshint 108 | 109 | 110 | # ------------------------------------------------------------------------------ 111 | # Numerous always-ignore extensions 112 | # ------------------------------------------------------------------------------ 113 | 114 | *.diff 115 | *.err 116 | *.orig 117 | *.rej 118 | *.swo 119 | *.swp 120 | *.vi 121 | *~ 122 | *.sass-cache 123 | 124 | 125 | # ------------------------------------------------------------------------------ 126 | # Files from other repository control systems 127 | # ------------------------------------------------------------------------------ 128 | 129 | .hg 130 | .svn 131 | .CVS 132 | 133 | 134 | # ------------------------------------------------------------------------------ 135 | # PHPStorm, RubyMine 136 | # ------------------------------------------------------------------------------ 137 | 138 | .idea 139 | 140 | 141 | # ------------------------------------------------------------------------------ 142 | # OS X and other IDE's folder attributes 143 | # ------------------------------------------------------------------------------ 144 | 145 | .DS_Store 146 | Thumbs.db 147 | .cache 148 | .project 149 | .settings 150 | .tmproj 151 | *.esproj 152 | nbproject 153 | *.sublime-project 154 | *.sublime-workspace 155 | 156 | 157 | # ------------------------------------------------------------------------------ 158 | # Dreamweaver added files 159 | # ------------------------------------------------------------------------------ 160 | 161 | _notes 162 | dwsync.xml 163 | 164 | 165 | # ------------------------------------------------------------------------------ 166 | # Komodo 167 | # ------------------------------------------------------------------------------ 168 | 169 | *.komodoproject 170 | .komodotools 171 | 172 | dist 173 | 174 | .nyc_output 175 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # v1.0.4 2015-05-06 2 | 3 | 4 | # ------------------------------------------------------------------------------ 5 | # Add your custom excludes below 6 | # ------------------------------------------------------------------------------ 7 | .nyc_output/ 8 | .npmrc 9 | .vscode/ 10 | .tern-project 11 | lib/ 12 | src/ 13 | script/ 14 | example/ 15 | __tests__/ 16 | test/ 17 | .babelrc 18 | .eslintignore 19 | .flowconfig 20 | .gitignore 21 | .publishrc 22 | .stylelintrc 23 | example.config.json 24 | gulfile.babel.js 25 | jest.config.json 26 | litmus-support-clients.js 27 | flow-typed 28 | webpack.config.js 29 | .eslintrc 30 | 31 | 32 | # =============== log ignore builtAssets 33 | 34 | /tmp/* 35 | /builtAssets/* 36 | *.log 37 | .nyc_output/ 38 | coverage/ 39 | 40 | # =========================== DO NOT touch below v1.0.3 =============================== 41 | 42 | 43 | node_modules 44 | 45 | # =============== RubyMine.gitignore 46 | # =============== PhPStorm.gitignore 47 | .idea 48 | 49 | 50 | # =============== OSX.gitignore 51 | .DS_Store 52 | .AppleDouble 53 | .LSOverride 54 | 55 | # Thumbnails 56 | ._* 57 | 58 | # =============== # Windows image file caches 59 | Thumbs.db 60 | ehthumbs.db 61 | 62 | # Folder config file 63 | Desktop.ini 64 | 65 | # Recycle Bin used on file shares 66 | $RECYCLE.BIN/ 67 | 68 | # Except the .htpassed 69 | !.htpasswd 70 | !.nodemonignore 71 | 72 | npm-debug.log 73 | 74 | dump.rdb 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | # FROM https://github.com/niftylettuce/node-email-templates/blob/master/.gitignore 84 | 85 | # ------------------------------------------------------------------------------ 86 | # Common Node files and folders 87 | # https://github.com/github/gitignore/blob/master/Node.gitignore 88 | # ------------------------------------------------------------------------------ 89 | 90 | lib-cov 91 | *.seed 92 | *.log 93 | *.csv 94 | *.dat 95 | *.out 96 | *.pid 97 | *.gz 98 | 99 | pids 100 | log 101 | results 102 | 103 | node_modules 104 | npm-debug.log 105 | 106 | 107 | # ------------------------------------------------------------------------------ 108 | # Nodemon 109 | # ------------------------------------------------------------------------------ 110 | 111 | .monitor 112 | 113 | 114 | # ------------------------------------------------------------------------------ 115 | # JSHint 116 | # ------------------------------------------------------------------------------ 117 | 118 | jshint 119 | 120 | 121 | # ------------------------------------------------------------------------------ 122 | # Numerous always-ignore extensions 123 | # ------------------------------------------------------------------------------ 124 | 125 | *.diff 126 | *.err 127 | *.orig 128 | *.rej 129 | *.swo 130 | *.swp 131 | *.vi 132 | *~ 133 | *.sass-cache 134 | 135 | 136 | # ------------------------------------------------------------------------------ 137 | # Files from other repository control systems 138 | # ------------------------------------------------------------------------------ 139 | 140 | .hg 141 | .svn 142 | .CVS 143 | 144 | 145 | # ------------------------------------------------------------------------------ 146 | # PHPStorm, RubyMine 147 | # ------------------------------------------------------------------------------ 148 | 149 | .idea 150 | 151 | 152 | # ------------------------------------------------------------------------------ 153 | # OS X and other IDE's folder attributes 154 | # ------------------------------------------------------------------------------ 155 | 156 | .DS_Store 157 | Thumbs.db 158 | .cache 159 | .project 160 | .settings 161 | .tmproj 162 | *.esproj 163 | nbproject 164 | *.sublime-project 165 | *.sublime-workspace 166 | 167 | 168 | # ------------------------------------------------------------------------------ 169 | # Dreamweaver added files 170 | # ------------------------------------------------------------------------------ 171 | 172 | _notes 173 | dwsync.xml 174 | 175 | 176 | # ------------------------------------------------------------------------------ 177 | # Komodo 178 | # ------------------------------------------------------------------------------ 179 | 180 | *.komodoproject 181 | .komodotools 182 | bower_components 183 | 184 | config/examples.json 185 | config.json 186 | 187 | # ------------------------------------------------------------------------------ 188 | # customized scripts 189 | # ------------------------------------------------------------------------------ 190 | scripts 191 | -------------------------------------------------------------------------------- /.publishignore: -------------------------------------------------------------------------------- 1 | # This files will be used by AfterShip as a source of .npmignore by concatenating .gitignore with this file 2 | 3 | .editorconfig 4 | .eslintrc 5 | .publishignore 6 | .travis.yml 7 | grunt 8 | grunt-config 9 | grunt-tasks 10 | gulpfile.js 11 | Gruntfile.js 12 | log 13 | scripts/ci 14 | test 15 | example 16 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## [8.1.0] 2023-12-21 8 | ### Added 9 | - supported AES auth 10 | - supported 2023-10 api version 11 | 12 | ## [8.0.1] 2023-12-19 13 | ### Fixed 14 | - updated dependencies version 15 | 16 | ## [8.0.0] 2023-12-18 17 | ### Breaking Changes 18 | - #98 fix AftershipError.code type not number 19 | - upgrade axios library to 1.6.0 version 20 | - upgrade jest library to 29.0.0 version 21 | 22 | ## [7.0.6] 2023-05-18 23 | ### Added 24 | - tracking object add new fileds 25 | - origin_state 26 | - origin_city 27 | - origin_postal_code 28 | - origin_raw_location 29 | - destination_state 30 | - destination_city 31 | - destination_postal_code 32 | - custom_estimated_delivery_date 33 | - first_estimated_delivery 34 | - shipment_tags 35 | - courier_connection_id 36 | - next_couriers 37 | - create tracking add new params 38 | - origin_state 39 | - origin_city 40 | - origin_postal_code 41 | - origin_raw_location 42 | - destination_state 43 | - destination_city 44 | - destination_postal_code 45 | - destination_raw_location 46 | - shipment_tags 47 | - courier_connection_id 48 | - next_couriers 49 | - update tracking add new params 50 | - slug 51 | - tracking_account_number 52 | - tracking_key 53 | - tracking_ship_date 54 | - origin_country_iso3 55 | - origin_state 56 | - origin_city 57 | - origin_postal_code 58 | - origin_raw_location 59 | - destination_country_iso3 60 | - destination_state 61 | - destination_city 62 | - destination_postal_code 63 | - destination_raw_location 64 | - courier detect add new params 65 | - tracking_account_number 66 | - tracking_origin_country 67 | - tracking_state 68 | - slug_group 69 | - origin_country_iso3 70 | - destination_country_iso3 71 | 72 | ## [7.0.5] 2022-07-20 73 | ### Added 74 | - add API endpoint `predict-batch` 75 | - update tracking fields 76 | 77 | ## [7.0.4] 2022-05-11 78 | ### Added 79 | - update tracking fields 80 | ### Fixed 81 | - fix #80 82 | 83 | ## [7.0.3] 2022-02-11 84 | ### Fixed 85 | - #74 fix the tracking interface 86 | 87 | ## [7.0.2] 2021-10-26 88 | ### Fixed 89 | - #69 Fixed Axios sending empty body issue 90 | 91 | ## [7.0.1] 2021-07-30 92 | ### Fixed 93 | - fix list trackings interface 94 | 95 | ## [7.0.0] 2021-02-09 96 | ### Breaking Changes 97 | - DeliveryStatus Enum change to string instead of integer 98 | 99 | ## [6.1.0] 2020-07-29 100 | ### Added 101 | - Added support for markAsCompleted() of tracking resource 102 | - Updated package dependencies 103 | 104 | ## [6.0.0] 2020-04-24 105 | ### Added 106 | - Support latest features in v4 API 107 | - Support TypeScript 108 | - Support IntelliSense, bring more convenient to consumers 109 | - The SDK is now isomorphic 110 | 111 | Compatibility 112 | - Node >=10.0 113 | 114 | ### Breaking Changes 115 | - Don't support `callback` anymore, please use `Promise` instead. 116 | - Removed `auto retry` feature, consumers need to retry the request by themselves. 117 | - Removed `call` method 118 | - Remove `meta` in SDK `response` object. 119 | 120 | ## [5.0.0] 2016-01-13 121 | - Update to 5.0.0, use Context-less interface, go to README.md for more 122 | 123 | [7.0.5]: https://github.com/AfterShip/aftership-sdk-nodejs/compare/7.0.4...7.0.5 124 | [7.0.4]: https://github.com/AfterShip/aftership-sdk-nodejs/compare/7.0.3...7.0.4 125 | [7.0.3]: https://github.com/AfterShip/aftership-sdk-nodejs/compare/7.0.2...7.0.3 126 | [7.0.2]: https://github.com/AfterShip/aftership-sdk-nodejs/compare/7.0.1...7.0.2 127 | [7.0.1]: https://github.com/AfterShip/aftership-sdk-nodejs/compare/7.0.0...7.0.1 128 | [7.0.0]: https://github.com/AfterShip/aftership-sdk-nodejs/compare/6.1.0...7.0.0 129 | [6.1.0]: https://github.com/AfterShip/aftership-sdk-nodejs/compare/6.0.0...6.1.0 130 | [6.0.0]: https://github.com/AfterShip/aftership-sdk-nodejs/compare/5.0.0...6.0.0 131 | [5.0.0]: https://github.com/AfterShip/aftership-sdk-nodejs/releases/tag/5.0.0 132 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## How to contribute to the AfterShip SDK for Node.js 4 | 5 | There are many ways that you can contribute to the AfterShip SDK project: 6 | 7 | - Submit a bug 8 | - Submit a code fix for a bug 9 | - Submit additions or modifications to the documentation 10 | - Submit a feature request 11 | 12 | All code submissions will be reviewed and tested by the team, and those that meet a high bar for both quality and design/roadmap appropriateness will be merged into the source. Be sure to follow the existing structure when adding new files/folders. 13 | 14 | All code should follow `ESLint` with Airbnb style and error-free when run through `npm run lint`. 15 | 16 | If you encounter any bugs with the library please file an issue in the [Issues](https://github.com/AfterShip/aftership-sdk-nodejs/issues) section of the project. 17 | 18 | ## Things to keep in mind when contributing 19 | 20 | Some guidance for when you make a contribution: 21 | 22 | - Add/update unit tests and code as required by your change 23 | - Make sure you run all the unit tests before you create a [Pull Request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests). 24 | - Run end-to-end tests or simple sample code to make sure the lib works in an end-to-end scenario. 25 | 26 | ## Big contributions 27 | 28 | If your contribution is significantly big it is better to first check with the project developers in order to make sure the change aligns with the long term plans. This can be done simply by submitting a question via the GitHub Issues section. 29 | 30 | ## Setting up your environment 31 | 32 | Want to get started hacking on the code? Super! Follow these instructions to get up and running. 33 | 34 | First, make sure you have the prerequisites installed and available on your `$PATH`: 35 | 36 | - Git 37 | - Node 10.x or higher 38 | - Yarn 39 | 40 | Next, get the code: 41 | 42 | 1. Fork this repo 43 | 2. Clone your fork locally (`git clone https://github.com//aftership-sdk-nodejs.git`) 44 | 3. Open a terminal and move into your local copy (`cd aftership-sdk-nodejs`) 45 | 4. Install and link all dependencies (`yarn`) 46 | 47 | ### Building 48 | 49 | Run `npm run build` 50 | 51 | ### Testing 52 | 53 | Run `npm run test` 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 AfterShip 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecation notice: New Version Available 2 | 3 | This version of the SDK has been deprecated and replaced with the newly reconstructed SDK. 4 | 5 | For the latest API features and improved integration, please visit our updated repository at https://github.com/AfterShip/tracking-sdk-nodejs and follow the provided instructions. 6 | 7 | # aftership-sdk-node 8 | 9 | [![node](https://img.shields.io/node/v/aftership.svg)](https://nodejs.org/en/) 10 | [![npm](https://img.shields.io/npm/v/aftership.svg)](https://www.npmjs.com/package/aftership) 11 | [![npm](https://img.shields.io/npm/dm/aftership.svg)](https://www.npmjs.com/package/aftership) 12 | [![npm](https://img.shields.io/npm/l/aftership.svg)](https://www.npmjs.com/package/aftership) 13 | 14 | node.js SDK for AfterShip API 15 | 16 | ## Key features 17 | 18 | - The SDK is now isomorphic, you can use it on both client and server side. 19 | - Support IntelliSense in IDE 20 | - Support TypeScript 21 | 22 | 23 | ## Installation 24 | ``` 25 | npm install aftership 26 | ``` 27 | 28 | ## Quick Start 29 | 30 | ```javascript 31 | const { AfterShip } = require('aftership'); 32 | const aftership = new AfterShip('YOUR_API_KEY'); 33 | 34 | aftership.courier.listAllCouriers() 35 | .then(result => console.log(result)) 36 | .catch(err => console.log(err)); 37 | 38 | 39 | /** Console 40 | { total: 335, 41 | couriers: 42 | [ ... ] 43 | } 44 | */ 45 | ``` 46 | 47 | ## Test 48 | ``` 49 | npm run test 50 | ``` 51 | 52 | ## Table of contents 53 | 54 | - [Constructor(api_key[, options])](#constructorapi_key-options) 55 | - [Endpoints](#endpoints) 56 | - [Rate Limiter](#rate-limiter) 57 | - [Error Handling](#error-handling) 58 | - [Examples](#examples) 59 | - [/couriers](#couriers) 60 | - [/trackings](#trackings) 61 | - [/last_checkpoint](#last_checkpoint) 62 | - [/notifications](#notifications) 63 | - [Migrations](#migrations) 64 | - [Help](#help) 65 | - [Contributing](#contributing) 66 | 67 | 68 | ## Constructor(api_key[, options]) 69 | 70 | Create AfterShip instance with options 71 | 72 | - `api_key` - **Require** - your Aftership API key 73 | - `options` - **Optional** - object of request options 74 | - `endpoint` - *string*, AfterShip endpoint, default 'https://api.aftership.com/tracking/2023-10' 75 | - `user_agent_prefix` - *string*, prefix of User-Agent in headers, default 'aftership-sdk-nodejs' 76 | - `auth_type` -number, if authentication type is AES, use `AuthType.Aes`, default `AuthType.ApiKey`. 77 | - `api_secret` -string, if authentication type is AES, it is required. 78 | 79 | 80 | Example: 81 | ```javascript 82 | // Construct with options 83 | const aftership = new AfterShip('YOUR_API_KEY', { 84 | auth_type: AuthType.Aes, api_secret:'YOUR_API_SECRET' 85 | }); 86 | ``` 87 | 88 | ## Endpoints 89 | 90 | The AfterShip instance has the following properties which are exactly the same as the API endpoins: 91 | 92 | - `courier` - Get a list of our supported couriers. 93 | - `tracking` - Create trackings, update trackings, and get tracking results. 94 | - `last_checkpoint` - Get tracking information of the last checkpoint of a tracking. 95 | - `notification` - Get, add or remove contacts (sms or email) to be notified when the status of a tracking has changed. 96 | 97 | Make request in a specific endpoint 98 | 99 | ```javascript 100 | // GET /trackings/:slug/:tracking_number 101 | aftership.tracking 102 | .getTracking({ 103 | slug: "ups", 104 | tracking_number: "1234567890", 105 | }) 106 | .then(result => console.log(result)) 107 | .catch(err => console.log(err)); 108 | ``` 109 | 110 | 111 | ## Rate Limiter 112 | 113 | See the [Rate Limit](https://www.aftership.com/docs/aftership/quickstart/rate-limit) to understand the AfterShip rate limit policy. 114 | 115 | You can get the recent rate limit by `aftership.rate_limit`. Initially all value are `null`. 116 | ```javascript 117 | const { AfterShip } = require('aftership'); 118 | const aftership = new AfterShip('YOUR_API_KEY'); 119 | 120 | console.log(aftership.rate_limit); 121 | 122 | // console output 123 | // { reset: null, limit: null, remaining: null } 124 | ``` 125 | After making an API call, it will be set. 126 | ```javascript 127 | aftership.courier.listCouriers() 128 | .then(result => { 129 | console.log(result); 130 | console.log(aftership.rate_limit); 131 | }) 132 | .catch(err => console.log(err)); 133 | 134 | // console output 135 | // { limit: 600, remaining: 599, reset: 1453281417 } 136 | ``` 137 | 138 | 139 | ## Error Handling 140 | 141 | There are 3 kinds of error 142 | - SDK Error 143 | - Request Error 144 | - API Error 145 | 146 | Error object of this SDK contain fields: 147 | - `type` - **Require** - type of the error, **please handle each error by this field** 148 | - `message` - **Optional** - detail message of the error 149 | - `data` - **Optional** - data lead to the error 150 | - `code` - **Optional** - error code for API Error 151 | - `response_body` - **Optional** - response body of API Error 152 | 153 | > Please handle each error by its `type`, since it is a require field 154 | 155 | ### SDK Error 156 | 157 | Error return by the SDK instance, mostly invalid param type when calling `constructor` or `endpoint method` 158 | `error.type` is one of [error_enum](https://github.com/AfterShip/aftership-sdk-nodejs/blob/master/src/error/error_enum.js) 159 | **Throw** by the SDK instance 160 | 161 | ```js 162 | const { AfterShip } = require('aftership'); 163 | const aftership = new AfterShip('YOUR_API_KEY'); 164 | 165 | // GET /notifications/:slug/:tracking_number 166 | aftership.notification.getNotification() 167 | .then(result => console.log(result)) 168 | .catch(err => console.log(err)); 169 | 170 | /* 171 | { Error: HandlerError: You must specify the id or slug and tracking number 172 | type: 'HandlerError', 173 | code: '', 174 | data: undefined, 175 | responseBody: '' } 176 | */ 177 | ``` 178 | 179 | ### Request Error 180 | 181 | Error return by the `request` module 182 | `error.type` could be `ETIMEDOUT`, `ECONNRESET`, etc. 183 | **Catch** by promise 184 | 185 | ```js 186 | const { AfterShip } = require('aftership'); 187 | const aftership = new AfterShip('YOUR_API_KEY'); 188 | 189 | aftership.courier.listCouriers() 190 | .then(result => console.log(result)) 191 | .catch(err => console.log(err)); 192 | /* 193 | { Error: getaddrinfo ENOTFOUND api.aftership.com api.aftership.com:443 194 | type: 'ENOTFOUND', 195 | code: 'ENOTFOUND', 196 | ... } 197 | */ 198 | ``` 199 | 200 | ### API Error 201 | 202 | Error return by the Aftership API 203 | `error.type` should be same as [request error](https://www.aftership.com/docs/aftership/quickstart/request-errors#http-status-code-summary). 204 | **Catch** by promise 205 | 206 | ```js 207 | const { AfterShip } = require('aftership'); 208 | const aftership = new AfterShip('INVALID_API_KEY'); 209 | 210 | aftership.courier.listCouriers() 211 | .then(result => console.log(result)) 212 | .catch(err => console.log(err)); 213 | /* 214 | { [Error: Invalid API key.] 215 | type: 'Unauthorized', 216 | message: 'Invalid API key.', 217 | code: 401, 218 | data: {}, 219 | response_body: '{"meta":{"code":401,"message":"Invalid API key.","type":"Unauthorized"},"data":{}}' } 220 | */ 221 | ``` 222 | 223 | ## Examples 224 | ### /couriers 225 | **GET** /couriers 226 | 227 | ```javascript 228 | aftership.courier.listCouriers() 229 | .then(result => console.log(result)) 230 | .catch(e => console.log(e)); 231 | ``` 232 | 233 | **GET** /couriers/all 234 | 235 | ```javascript 236 | aftership.courier.listAllCouriers() 237 | .then(result => console.log(result)) 238 | .catch(e => console.log(e)); 239 | ``` 240 | 241 | **POST** /couriers/detect 242 | 243 | ```javascript 244 | const payload = { 245 | 'tracking': { 246 | 'tracking_number': '906587618687', 247 | 'tracking_postal_code': 'DA15BU', 248 | 'tracking_ship_date': '20131231', 249 | 'tracking_account_number': '1234567890', 250 | 'slug': ['dhl', 'ups', 'fedex'] 251 | } 252 | }; 253 | aftership.courier.detectCouriers(payload) 254 | .then(result => console.log(result)) 255 | .catch(e => console.log(e)); 256 | ``` 257 | 258 | ### /trackings 259 | 260 | **POST** /trackings 261 | 262 | ```javascript 263 | const payload = { 264 | 'tracking': { 265 | 'slug': 'dhl', 266 | 'tracking_number': '123456789', 267 | 'title': 'Title Name', 268 | 'smses': [ 269 | '+18555072509', 270 | '+18555072501' 271 | ], 272 | 'emails': [ 273 | 'email@yourdomain.com', 274 | 'another_email@yourdomain.com' 275 | ], 276 | 'order_id': 'ID 1234', 277 | 'order_id_path': 'http://www.aftership.com/order_id=1234', 278 | 'custom_fields': { 279 | 'product_name': 'iPhone Case', 280 | 'product_price': 'USD19.99' 281 | } 282 | } 283 | }; 284 | aftership.tracking 285 | .createTracking(payload) 286 | .then((result) => console.log(result)) 287 | .catch((e) => console.log(e)); 288 | ``` 289 | 290 | **DELETE** /trackings/:slug/:tracking_number 291 | 292 | ```javascript 293 | aftership.tracking 294 | .deleteTracking({ 295 | slug: "ups", 296 | tracking_number: "1234567890", 297 | }) 298 | .then((result) => console.log(result)) 299 | .catch((e) => console.log(e)); 300 | ``` 301 | 302 | **GET** /trackings 303 | 304 | ```javascript 305 | const query = { 306 | slug: 'dhl,ups,usps' 307 | }; 308 | aftership.tracking 309 | .listTrackings(query) 310 | .then((result) => console.log(result)) 311 | .catch((e) => console.log(e)); 312 | ``` 313 | 314 | **GET** /trackings/:slug/:tracking_number 315 | 316 | ```javascript 317 | aftership.tracking 318 | .getTracking({ 319 | slug: "ups", 320 | tracking_number: "1234567890", 321 | }) 322 | .then((result) => console.log(result)) 323 | .catch((e) => console.log(e)); 324 | ``` 325 | 326 | Tip: You can also add `optional_parameters` to `/:slug/:tracking_number` 327 | 328 | ```javascript 329 | // GET /trackings/:slug/:tracking_number?tracking_postal_code=:postal_code&tracking_ship_date=:ship_date 330 | aftership.tracking 331 | .getTracking({ 332 | slug: "ups", 333 | tracking_number: "1234567890", 334 | optional_parameters: { 335 | tracking_postal_code: "1234", 336 | tracking_ship_date: "20200423", 337 | } 338 | }) 339 | .then((result) => console.log(result)) 340 | .catch((e) => console.log(e)); 341 | ``` 342 | 343 | 344 | > Pro Tip: You can always use /:id to replace /:slug/:tracking_number. 345 | ```javascript 346 | // GET /trackings/:id 347 | aftership.tracking 348 | .getTracking({ 349 | id: "1234567890", 350 | }) 351 | .then((result) => console.log(result)) 352 | .catch((e) => console.log(e)); 353 | ``` 354 | 355 | **PUT** /trackings/:slug/:tracking_number 356 | 357 | ```javascript 358 | const payload = { 359 | tracking: { 360 | title: "New Title", 361 | }, 362 | }; 363 | aftership.tracking 364 | .updateTracking({ 365 | slug: "ups", 366 | tracking_number: "1234567890", 367 | }, payload) 368 | .then((result) => console.log(result)) 369 | .catch((e) => console.log(e)); 370 | ``` 371 | 372 | **POST** /trackings/:slug/:tracking_number/retrack 373 | 374 | ```javascript 375 | aftership.tracking 376 | .retrack({ 377 | slug: "ups", 378 | tracking_number: "1234567890", 379 | }) 380 | .then((result) => console.log(result)) 381 | .catch((e) => console.log(e)); 382 | ``` 383 | 384 | **POST** /trackings/:slug/:tracking_number/mark-as-completed 385 | 386 | ```javascript 387 | aftership.tracking 388 | .markAsCompleted({ 389 | slug: "ups", 390 | tracking_number: "1234567890", 391 | }, { 392 | reason: "DELIVERED" 393 | }) 394 | .then((result) => console.log(result)) 395 | .catch((e) => console.log(e)); 396 | ``` 397 | 398 | ### /last_checkpoint 399 | 400 | **GET** /last_checkpoint/:slug/:tracking_number 401 | 402 | ```javascript 403 | aftership.last_checkpoint.getLastCheckpoint({ 404 | slug: 'ups', 405 | tracking_number: '1234567890', 406 | }) 407 | .then(result => console.log(result)) 408 | .catch(e => console.log(e)); 409 | ``` 410 | 411 | ### /notifications 412 | 413 | **GET** /notifications/:slug/:tracking_number 414 | 415 | ```javascript 416 | aftership.notification.getNotification({ 417 | slug: 'ups', 418 | tracking_number: '1234567890', 419 | }) 420 | .then(result => console.log(result)) 421 | .catch(e => console.log(e)); 422 | ``` 423 | 424 | **POST** /notifications/:slug/:tracking_number/add 425 | 426 | ```javascript 427 | const payload = { 428 | 'notification': { 429 | 'emails': ['user1@gmail.com','user2@gmail.com','invalid EMail @ Gmail. com'], 430 | 'smses': ['+85291239123', '+85261236123', 'Invalid Mobile Phone Number'] 431 | } 432 | }; 433 | aftership.notification.addNotification({ 434 | slug: 'ups', 435 | tracking_number: '1234567890', 436 | }, payload) 437 | .then(result => console.log(result)) 438 | .catch(e => console.log(e)); 439 | ``` 440 | 441 | **POST** /notifications/:slug/:tracking_number/remove 442 | 443 | ```javascript 444 | const payload = { 445 | 'notification': { 446 | 'emails': ['user1@gmail.com','user2@gmail.com','invalid EMail @ Gmail. com'], 447 | 'smses': ['+85291239123', '+85261236123', 'Invalid Mobile Phone Number'] 448 | } 449 | }; 450 | aftership.notification.removeNotification({ 451 | slug: 'ups', 452 | tracking_number: '1234567890', 453 | }, payload) 454 | .then(result => console.log(result)) 455 | .catch(e => console.log(e)); 456 | ``` 457 | 458 | ## Migrations 459 | ```javascript 460 | // v5 (old version) 461 | const Aftership = require('aftership')('YOUR_API_KEY'); 462 | 463 | Aftership.call('GET', '/couriers/all').then(function (result) { 464 | console.log(result); 465 | }).catch(function (err) { 466 | console.log(err); 467 | }); 468 | 469 | // v6 (new version) 470 | const { AfterShip } = require('aftership'); 471 | const aftership = new AfterShip('YOUR_API_KEY'); 472 | 473 | aftership.courier.listAllCouriers() 474 | .then(result => console.log(result)) 475 | .catch(err => console.log(err)); 476 | ``` 477 | 478 | ## Help 479 | If you get stuck, we're here to help. The following are the best ways to get assistance working through your issue: 480 | 481 | - [Issue Tracker](https://github.com/AfterShip/aftership-sdk-nodejs/issues) for questions, feature requests, bug reports and general discussion related to this package. Try searching before you create a new issue. 482 | - [Slack AfterShip-SDKs](https://aftership-sdk-slackin.herokuapp.com/): a Slack community, you can find the maintainers and users of this package in #aftership-sdks. 483 | 484 | ## Contributing 485 | For details on contributing to this repository, see the [contributing guide](https://github.com/AfterShip/aftership-sdk-nodejs/blob/master/CONTRIBUTING.md). 486 | 487 | ## License 488 | Copyright (c) 2016-2020 AfterShip 489 | 490 | Licensed under the MIT license. 491 | 492 | 493 | [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FAfterShip%2Faftership-sdk-nodejs.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FAfterShip%2Faftership-sdk-nodejs?ref=badge_large) 494 | -------------------------------------------------------------------------------- /__tests__/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AfterShip/aftership-sdk-nodejs/0b9a228857770413a793adaea95cfd43adecc53b/__tests__/.gitignore -------------------------------------------------------------------------------- /__tests__/e2e.test.js: -------------------------------------------------------------------------------- 1 | // const { AfterShip } = require('../dist'); 2 | // const aftership = new AfterShip(''); 3 | 4 | describe.skip('trackings', () => { 5 | describe('createTrackings', () => { 6 | test('create tracking successfully', async () => { 7 | const payload = { 8 | slug: 'usps', 9 | tracking_number: (new Date()).valueOf().toString(), 10 | title: 'Title Name', 11 | smses: ['+18555072509', '+18555072501'], 12 | emails: ['email@yourdomain.com', 'another_email@yourdomain.com'], 13 | order_id: 'ID 1234', 14 | order_id_path: 'http://www.aftership.com/order_id=1234', 15 | custom_fields: { 16 | product_name: 'iPhone Case', 17 | product_price: 'USD19.99', 18 | }, 19 | language: 'en', 20 | order_promised_delivery_date: '2019-05-20', 21 | delivery_type: 'pickup_at_store', 22 | pickup_location: 'Flagship Store', 23 | pickup_note: 'Reach out to our staffs when you arrive our stores for shipment pickup', 24 | tracking_destination_country: 'HKG' 25 | }; 26 | 27 | const newTracking = await aftership.tracking.createTracking({ 28 | tracking: payload 29 | }); 30 | 31 | const tracking = newTracking.tracking; 32 | expect(tracking.slug).toBe(payload.slug); 33 | expect(tracking.tracking_number).toBe(payload.tracking_number); 34 | expect(tracking.tracking_destination_country).toBe(payload.tracking_destination_country); 35 | }); 36 | 37 | test('create tracking bad format', async () => { 38 | const payload = { 39 | slug: 'dhl', 40 | tracking_number: '62541256321' 41 | }; 42 | 43 | try { 44 | await aftership.tracking.createTracking({ 45 | tracking: payload 46 | }); 47 | } catch (e) { 48 | expect(e.type).toBe('BadRequest'); 49 | expect(e.code).toBe(4017); 50 | expect(e.data.tracking).toEqual(payload); 51 | expect(JSON.parse(e.responseBody).meta.code).toEqual(4017); 52 | } 53 | }); 54 | }); 55 | describe('listTrackings', () => { 56 | test('listTrackings successfully', async () => { 57 | const results = await aftership.tracking.listTrackings(); 58 | expect(typeof results.page).toBe('number'); 59 | expect(Array.isArray(results.trackings)).toBeTruthy(); 60 | }); 61 | }); 62 | describe('getTracking', () => { 63 | test('simple getTracking', async () => { 64 | const query = { 65 | slug: 'usps', 66 | tracking_number: '300000000017' 67 | }; 68 | const result = await aftership.tracking.getTracking(query); 69 | expect(result.tracking.slug).toBe(query.slug); 70 | expect(result.tracking.tracking_number).toBe(query.tracking_number); 71 | }); 72 | 73 | test('simple getTracking', async () => { 74 | const query = { 75 | id: 'nzg2id4lm0yz1k9cgfv4s02m' 76 | }; 77 | const result = await aftership.tracking.getTracking(query); 78 | expect(result.tracking.id).toBe(query.id); 79 | }); 80 | 81 | test('getTracking error HandlerError', async () => { 82 | const query = { 83 | tracking_number: '300000000017' 84 | }; 85 | 86 | try { 87 | await aftership.tracking.getTracking(query); 88 | } catch (e) { 89 | expect(e.type).toBe('HandlerError'); 90 | } 91 | }); 92 | 93 | test('getTracking error BadRequest', async () => { 94 | const query = { 95 | slug: 'postnl', 96 | tracking_number: '300000000017' 97 | }; 98 | 99 | try { 100 | await aftership.tracking.getTracking(query); 101 | } catch (e) { 102 | expect(e.type).toBe('BadRequest'); 103 | expect(e.code).toBe(4009); 104 | expect(JSON.parse(e.responseBody).meta.code).toEqual(4009); 105 | } 106 | }); 107 | 108 | test('getTracking test optional parameter', async () => { 109 | const query = { 110 | slug: 'postnl', 111 | tracking_number: '132485736098', 112 | optional_parameters: { 113 | tracking_postal_code: '12345' 114 | } 115 | }; 116 | 117 | const result = await aftership.tracking.getTracking(query); 118 | expect(result.tracking.slug).toBe(query.slug); 119 | expect(result.tracking.tracking_number).toBe(query.tracking_number); 120 | expect(result.tracking.tracking_postal_code).toBe(query.optional_parameters.tracking_postal_code); 121 | }); 122 | }); 123 | describe('updateTracking', () => { 124 | test('updateTracking successfully', async () => { 125 | const query = { 126 | slug: 'usps', 127 | tracking_number: '300000000017' 128 | }; 129 | const payload = { 130 | tracking: { 131 | title: (new Date()).valueOf().toString() 132 | } 133 | }; 134 | const result = await aftership.tracking.updateTracking(query, payload); 135 | expect(result.tracking.slug).toBe(query.slug); 136 | expect(result.tracking.tracking_number).toBe(query.tracking_number); 137 | expect(result.tracking.title).toBe(payload.tracking.title); 138 | }); 139 | 140 | test('updateTracking failed', async () => { 141 | const query = { 142 | slug: 'usps' 143 | }; 144 | const payload = { 145 | tracking: { 146 | title: (new Date()).valueOf().toString() 147 | } 148 | }; 149 | try { 150 | await aftership.tracking.updateTracking(query, payload); 151 | } catch (e) { 152 | expect(e.type).toBe('HandlerError'); 153 | } 154 | }); 155 | }); 156 | }); 157 | describe.skip('couriers', () => { 158 | describe('listCouriers', () => { 159 | test('listCouriers successfully', async () => { 160 | const couriers = await aftership.courier.listCouriers(); 161 | expect(typeof couriers.total).toBe('number'); 162 | expect(Array.isArray(couriers.couriers)).toBeTruthy(); 163 | }); 164 | }); 165 | describe('listAllCouriers', () => { 166 | test('listAllCouriers successfully', async () => { 167 | const couriers = await aftership.courier.listAllCouriers(); 168 | expect(typeof couriers.total).toBe('number'); 169 | expect(Array.isArray(couriers.couriers)).toBeTruthy(); 170 | }); 171 | }); 172 | describe('detectCouriers', () => { 173 | test('detectCouriers successfully', async () => { 174 | const payload = { 175 | tracking: { 176 | tracking_number: '123412341234' 177 | } 178 | }; 179 | const couriers = await aftership.courier.detectCouriers(payload); 180 | expect(typeof couriers.total).toBe('number'); 181 | expect(Array.isArray(couriers.couriers)).toBeTruthy(); 182 | }); 183 | test('detectCouriers with limited slug group successfully', async () => { 184 | const payload = { 185 | tracking: { 186 | tracking_number: '123412341234', 187 | slug: [ 188 | 'fedex', 189 | 'australia-post-api' 190 | ] 191 | } 192 | }; 193 | const couriers = await aftership.courier.detectCouriers(payload); 194 | expect(typeof couriers.total).toBe('number'); 195 | expect(Array.isArray(couriers.couriers)).toBeTruthy(); 196 | }); 197 | }); 198 | }); 199 | describe.skip('last_checkpoint', () => { 200 | describe('getLastCheckpoint', () => { 201 | test('get lastCheckpoint successfully', async () => { 202 | const query = { 203 | slug: 'usps', 204 | tracking_number: '300000000017', 205 | }; 206 | const result = await aftership.last_checkpoint.getLastCheckpoint(query); 207 | expect(typeof result.id).toBe('string'); 208 | expect(result.tracking_number).toBe(query.tracking_number); 209 | expect(result.slug).toBe(query.slug); 210 | expect(typeof result.checkpoint).toBe('object'); 211 | }); 212 | }); 213 | }); 214 | describe.skip('notification', () => { 215 | describe('getNotification', () => { 216 | test('get getNotification successfully', async () => { 217 | const query = { 218 | slug: 'usps', 219 | tracking_number: '300000000017', 220 | }; 221 | const result = await aftership.notification.getNotification(query); 222 | expect(typeof result.notification).toBe('object'); 223 | }); 224 | }); 225 | describe('addNotification', () => { 226 | test('get addNotification successfully', async () => { 227 | const query = { 228 | slug: 'usps', 229 | tracking_number: '300000000017', 230 | }; 231 | const payload = { 232 | notification: { 233 | emails: [ 234 | 'abc@abc.com' 235 | ], 236 | smses: [ 237 | '+15553750' 238 | ] 239 | } 240 | }; 241 | const result = await aftership.notification.addNotification(query, payload); 242 | expect(typeof result.notification).toBe('object'); 243 | expect(result.notification.emails.indexOf(payload.notification.emails[0])).toBeGreaterThan(-1); 244 | }); 245 | }); 246 | describe('removeNotification', () => { 247 | test('get removeNotification successfully', async () => { 248 | const query = { 249 | slug: 'usps', 250 | tracking_number: '300000000017', 251 | }; 252 | const payload = { 253 | notification: { 254 | emails: [ 255 | 'abc@abc.com' 256 | ], 257 | smses: [ 258 | '+15553750' 259 | ] 260 | } 261 | }; 262 | const result = await aftership.notification.removeNotification(query, payload); 263 | expect(typeof result.notification).toBe('object'); 264 | }); 265 | }); 266 | }); 267 | describe.skip('estimated_delivery_date', () => { 268 | describe('batchPredict', () => { 269 | test('batch predict successfully', async () => { 270 | const data = { 271 | estimated_delivery_dates: [{ 272 | "slug": "fedex", 273 | "service_type_name": "FEDEX HOME DELIVERY", 274 | "origin_address": { 275 | "country": "USA", 276 | "state": "WA", 277 | "postal_code": "98108", 278 | "raw_location": "Seattle, Washington, 98108, USA, United States" 279 | }, 280 | "destination_address": { 281 | "country": "USA", 282 | "state": "CA", 283 | "postal_code": "92019", 284 | "raw_location": "El Cajon, California, 92019, USA, United States" 285 | }, 286 | "weight": { 287 | "unit": "kg", 288 | "value": 11 289 | }, 290 | "package_count": 1, 291 | "pickup_time": "2021-07-01 15:00:00" 292 | }] 293 | }; 294 | const result = await aftership.estimated_delivery_date.batchPredict(data); 295 | expect(typeof result.estimated_delivery_dates).toBe('object'); 296 | expect(result.estimated_delivery_dates.length).toEqual(1); 297 | }); 298 | }); 299 | }); 300 | -------------------------------------------------------------------------------- /__tests__/test_aftership.js: -------------------------------------------------------------------------------- 1 | var AfterShip = require("../dist/index.js").AfterShip; 2 | 3 | let api_key = "SOME_API_KEY"; 4 | 5 | describe("Test constructor", function () { 6 | describe("Test construct correct cases", function () { 7 | it("should construct with api_key correctly", function () { 8 | let aftership = new AfterShip(api_key); 9 | expect(aftership.apiKey).toEqual(api_key); 10 | expect(aftership.endpoint).toEqual("https://api.aftership.com/tracking/2023-10"); 11 | }); 12 | }); 13 | 14 | describe("Test constructor error", function () { 15 | it("should return error, if api_key is null/undefined/not string", function () { 16 | let expected_error = "ConstructorError: Invalid API key"; 17 | try { 18 | new AfterShip(); 19 | } catch (e) { 20 | expect(e.message).toEqual(expected_error); 21 | } 22 | try { 23 | new AfterShip(null); 24 | } catch (e) { 25 | expect(e.message).toEqual(expected_error); 26 | } 27 | try { 28 | new AfterShip(999); 29 | } catch (e) { 30 | expect(e.message).toEqual(expected_error); 31 | } 32 | try { 33 | new AfterShip(true); 34 | } catch (e) { 35 | expect(e.message).toEqual(expected_error); 36 | } 37 | try { 38 | new AfterShip(false); 39 | } catch (e) { 40 | expect(e.message).toEqual(expected_error); 41 | } 42 | try { 43 | new AfterShip({}); 44 | } catch (e) { 45 | expect(e.message).toEqual(expected_error); 46 | } 47 | }); 48 | 49 | it('should return error, if options is not null/undefined/object', function () { 50 | let expected_error = 'ConstructorError: Invalid Options value'; 51 | try { 52 | new AfterShip(api_key, 999); 53 | } catch (e) { 54 | expect(e.message).toEqual(expected_error); 55 | } 56 | try { 57 | new AfterShip(api_key, true); 58 | } catch (e) { 59 | expect(e.message).toEqual(expected_error); 60 | } 61 | try { 62 | new AfterShip(api_key, false); 63 | } catch (e) { 64 | expect(e.message).toEqual(expected_error); 65 | } 66 | try { 67 | new AfterShip(api_key, 'options'); 68 | } catch (e) { 69 | expect(e.message).toEqual(expected_error); 70 | } 71 | }); 72 | 73 | it('should return error, if endpoint is defined but not string', function () { 74 | let expected_error = 'ConstructorError: Invalid Endpoint value'; 75 | try { 76 | new AfterShip(api_key, {endpoint: 999}); 77 | } catch (e) { 78 | expect(e.message).toEqual(expected_error); 79 | } 80 | try { 81 | new AfterShip(api_key, {endpoint: true}); 82 | } catch (e) { 83 | expect(e.message).toEqual(expected_error); 84 | } 85 | try { 86 | new AfterShip(api_key, {endpoint: false}); 87 | } catch (e) { 88 | expect(e.message).toEqual(expected_error); 89 | } 90 | try { 91 | new AfterShip(api_key, {endpoint: {}}); 92 | } catch (e) { 93 | expect(e.message).toEqual(expected_error); 94 | } 95 | }); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /__tests__/test_courier.js: -------------------------------------------------------------------------------- 1 | var Aftership = require("../dist/index.js").AfterShip; 2 | var axios = require("axios"); 3 | var MockAdapter = require("axios-mock-adapter"); 4 | var CourierDetectRequest = require("../dist/model/courier/courier_detect_request.js").CourierDetectRequest; 5 | 6 | var aftership = new Aftership("SOME_API_KEY"); 7 | 8 | describe("Courier", function () { 9 | describe("#listCouriers()", function () { 10 | // This sets the mock adapter on the default instance 11 | var mock = new MockAdapter(axios); 12 | 13 | // Mock listCouriers 14 | mock.onGet('/couriers').reply( 15 | 200, 16 | { 17 | meta: { 18 | code: 200, 19 | }, 20 | data: { 21 | total: 2, 22 | couriers: [ 23 | { 24 | slug: "dhl", 25 | name: "DHL", 26 | phone: "+1 800 225 5345", 27 | other_name: "DHL Express", 28 | web_url: "http://www.dhl.com/", 29 | required_fields: [], 30 | optional_fields: [], 31 | }, 32 | { 33 | slug: "deutsch-post", 34 | name: "Deutsche Post Mail", 35 | phone: "+49 (0) 180 2 000221", 36 | other_name: "dpdhl", 37 | web_url: "http://www.deutschepost.de/", 38 | required_fields: ["tracking_ship_date"], 39 | optional_fields: [], 40 | }, 41 | ], 42 | }, 43 | }, 44 | { 45 | "x-ratelimit-reset": 1406096275, 46 | "x-ratelimit-limit": 10, 47 | "x-ratelimit-remaining": 9, 48 | } 49 | ); 50 | 51 | it("should return couriers and total", function (done) { 52 | aftership.courier 53 | .listCouriers() 54 | .then((result) => { 55 | const { total, couriers } = result || {}; 56 | if (couriers && couriers.length > 0 && total === couriers.length) { 57 | done(); 58 | } else { 59 | done("not found data"); 60 | } 61 | }) 62 | .catch((e) => done(e.message)); 63 | }); 64 | 65 | it("should get rate-limit", function (done) { 66 | aftership.courier 67 | .listCouriers() 68 | .then((_) => { 69 | const rateLimit = aftership.rate_limit; 70 | if (rateLimit && rateLimit.limit) { 71 | done(); 72 | } else { 73 | done("not found rate-limit"); 74 | } 75 | }) 76 | .catch((e) => done(e.message)); 77 | }); 78 | 79 | it("should get courier objects", function (done) { 80 | aftership.courier 81 | .listCouriers() 82 | .then((result) => { 83 | const { couriers } = result || {}; 84 | if (couriers && couriers.length > 0 && couriers[0].slug === 'dhl') { 85 | done(); 86 | } else { 87 | done("not found couriers"); 88 | } 89 | }) 90 | .catch((e) => done(e.message)); 91 | }); 92 | }); 93 | 94 | describe("#listAllCouriers()", function () { 95 | // This sets the mock adapter on the default instance 96 | var mock = new MockAdapter(axios); 97 | 98 | // Mock listAllCouriers 99 | mock.onGet('/couriers/all').reply( 100 | 200, 101 | { 102 | meta: { 103 | code: 200, 104 | }, 105 | data: { 106 | total: 3, 107 | couriers: [ 108 | { 109 | slug: "india-post-int", 110 | name: "India Post International", 111 | phone: "+91 1800 11 2011", 112 | other_name: "भारतीय डाक, Speed Post & eMO, EMS, IPS Web", 113 | web_url: "http://www.indiapost.gov.in/", 114 | required_fields: [], 115 | optional_fields: [], 116 | }, 117 | { 118 | slug: "italy-sda", 119 | name: "Italy SDA", 120 | phone: "+39 199 113366", 121 | other_name: "SDA Express Courier", 122 | web_url: "http://www.sda.it/", 123 | required_fields: [], 124 | optional_fields: [], 125 | }, 126 | { 127 | slug: "bpost", 128 | name: "Belgium Post", 129 | phone: "+32 2 276 22 74", 130 | other_name: "bpost, Belgian Post", 131 | web_url: "http://www.bpost.be/", 132 | required_fields: [], 133 | optional_fields: [], 134 | }, 135 | ], 136 | }, 137 | }, 138 | { 139 | "x-ratelimit-reset": 1406096275, 140 | "x-ratelimit-limit": 10, 141 | "x-ratelimit-remaining": 9, 142 | } 143 | ); 144 | 145 | it("should return couriers and total", function (done) { 146 | aftership.courier 147 | .listAllCouriers() 148 | .then((result) => { 149 | const { total, couriers } = result || {}; 150 | if (couriers && couriers.length > 0 && total === couriers.length) { 151 | done(); 152 | } else { 153 | done("not found data"); 154 | } 155 | }) 156 | .catch((e) => done(e.message)); 157 | }); 158 | 159 | it("should get courier objects", function (done) { 160 | aftership.courier 161 | .listAllCouriers() 162 | .then((result) => { 163 | const { couriers } = result || {}; 164 | if (couriers && couriers.length > 0 && couriers[0].slug === 'india-post-int') { 165 | done(); 166 | } else { 167 | done("not found couriers"); 168 | } 169 | }) 170 | .catch((e) => done(e.message)); 171 | }); 172 | }); 173 | 174 | describe("#detectCouriers()", function () { 175 | // This sets the mock adapter on the default instance 176 | var mock = new MockAdapter(axios); 177 | 178 | // Mock detectCouriers 179 | mock.onPost('/couriers/detect').reply( 180 | 200, 181 | { 182 | meta: { 183 | code: 200, 184 | }, 185 | data: { 186 | total: 2, 187 | couriers: [ 188 | { 189 | slug: "fedex", 190 | name: "FedEx", 191 | phone: "+1 800 247 4747", 192 | other_name: "Federal Express", 193 | web_url: "http://www.fedex.com/", 194 | required_fields: [], 195 | optional_fields: [], 196 | }, 197 | { 198 | slug: "dx", 199 | name: "DX", 200 | phone: "+44 0844 826 1178", 201 | other_name: "DX Freight", 202 | web_url: "https://www.thedx.co.uk/", 203 | required_fields: ["tracking_postal_code"], 204 | optional_fields: [], 205 | }, 206 | ], 207 | }, 208 | }, 209 | { 210 | "x-ratelimit-reset": 1406096275, 211 | "x-ratelimit-limit": 10, 212 | "x-ratelimit-remaining": 9, 213 | } 214 | ); 215 | 216 | const tracking = { 217 | tracking_number: '906587618687', 218 | tracking_postal_code: 'DA15BU', 219 | } 220 | const payload = new CourierDetectRequest(tracking); 221 | 222 | it("should return couriers and total", function (done) { 223 | aftership.courier 224 | .detectCouriers(payload) 225 | .then((result) => { 226 | const { total, couriers } = result || {}; 227 | if (couriers && couriers.length >= 0 && total === couriers.length) { 228 | done(); 229 | } else { 230 | done("not found data"); 231 | } 232 | }) 233 | .catch((e) => done(e.message)); 234 | }); 235 | 236 | it("should get courier objects", function (done) { 237 | aftership.courier 238 | .detectCouriers(payload) 239 | .then((result) => { 240 | const { couriers } = result || {}; 241 | if (couriers && couriers.length > 0 && couriers[0].slug === 'fedex') { 242 | done(); 243 | } else { 244 | done("not found couriers"); 245 | } 246 | }) 247 | .catch((e) => done(e.message)); 248 | }); 249 | }); 250 | }); 251 | -------------------------------------------------------------------------------- /__tests__/test_estimated_delivery_date.js: -------------------------------------------------------------------------------- 1 | var Aftership = require("../dist/index.js").AfterShip; 2 | var axios = require("axios"); 3 | var MockAdapter = require("axios-mock-adapter"); 4 | 5 | var aftership = new Aftership("SOME_API_KEY"); 6 | 7 | describe("EstimatedDeliveryDate", function () { 8 | describe("#batchPredict()", function () { 9 | var mock = new MockAdapter(axios); 10 | it("should get estimated_delivery_dates when success", async function () { 11 | mock.onPost('/estimated-delivery-date/predict-batch').reply( 12 | 200, 13 | { 14 | "meta": { 15 | "code": 200 16 | }, 17 | "data": { 18 | "estimated_delivery_dates": [ 19 | { 20 | "slug": "fedex", 21 | "service_type_name": "FEDEX HOME DELIVERY", 22 | "origin_address": { 23 | "country": "USA", 24 | "state": "WA", 25 | "postal_code": "98108", 26 | "raw_location": "Seattle, Washington, 98108, USA, United States", 27 | "city": null 28 | }, 29 | "destination_address": { 30 | "country": "USA", 31 | "state": "CA", 32 | "postal_code": "92019", 33 | "raw_location": "El Cajon, California, 92019, USA, United States", 34 | "city": null 35 | }, 36 | "weight": { 37 | "unit": "kg", 38 | "value": 11 39 | }, 40 | "package_count": 1, 41 | "pickup_time": "2021-07-01 15:00:00", 42 | "estimated_pickup": null, 43 | "estimated_delivery_date": "2021-07-05", 44 | "estimated_delivery_date_min": "2021-07-04", 45 | "estimated_delivery_date_max": "2021-07-05" 46 | } 47 | ] 48 | } 49 | } 50 | ); 51 | 52 | const data = { 53 | estimated_delivery_dates: [{ 54 | "slug": "fedex", 55 | "service_type_name": "FEDEX HOME DELIVERY", 56 | "origin_address": { 57 | "country": "USA", 58 | "state": "WA", 59 | "postal_code": "98108", 60 | "raw_location": "Seattle, Washington, 98108, USA, United States" 61 | }, 62 | "destination_address": { 63 | "country": "USA", 64 | "state": "CA", 65 | "postal_code": "92019", 66 | "raw_location": "El Cajon, California, 92019, USA, United States" 67 | }, 68 | "weight": { 69 | "unit": "kg", 70 | "value": 11 71 | }, 72 | "package_count": 1, 73 | "pickup_time": "2021-07-01 15:00:00" 74 | }] 75 | }; 76 | 77 | const result = await aftership.estimated_delivery_date.batchPredict(data); 78 | expect(typeof result.estimated_delivery_dates).toBe('object'); 79 | expect(result.estimated_delivery_dates.length).toEqual(1); 80 | }); 81 | 82 | it("should throw an error", async function () { 83 | mock.onPost('/estimated-delivery-date/predict-batch').reply( 84 | 400, 85 | { 86 | "meta": { 87 | "code": 400 88 | }, 89 | "data": { 90 | "estimated_delivery_dates": [] 91 | } 92 | } 93 | ); 94 | 95 | const data = { 96 | estimated_delivery_dates: [] 97 | }; 98 | 99 | try { 100 | await aftership.estimated_delivery_date.batchPredict(data); 101 | } catch (e) { 102 | expect(e.code).toEqual(400); 103 | } 104 | }) 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /__tests__/test_last_checkpoint.js: -------------------------------------------------------------------------------- 1 | var Aftership = require("../dist/index.js").AfterShip; 2 | var axios = require("axios"); 3 | var MockAdapter = require("axios-mock-adapter"); 4 | 5 | var aftership = new Aftership("SOME_API_KEY"); 6 | 7 | describe("LastCheckpoint", function () { 8 | describe("#getLastCheckpoint(), validate params", function () { 9 | it("should throw exception when not specify id and tracking number", async function () { 10 | let expected_error = 11 | "HandlerError: You must specify the id or slug and tracking number"; 12 | try { 13 | await aftership.last_checkpoint.getLastCheckpoint(); 14 | } catch (e) { 15 | expect(e.message).toEqual(expected_error); 16 | } 17 | }); 18 | 19 | it("should throw exception when both specify id and tracking number", async function () { 20 | let expected_error = 21 | "HandlerError: Cannot specify id and tracking number at the same time"; 22 | const param = { 23 | id: "5b74f4958776db0e00b6f5ed", 24 | slug: "ups", 25 | tracking_number: "1234567890", 26 | }; 27 | try { 28 | await aftership.last_checkpoint.getLastCheckpoint(param); 29 | } catch (e) { 30 | expect(e.message).toEqual(expected_error); 31 | } 32 | }); 33 | 34 | it("should throw exception when only specify slug", async function () { 35 | let expected_error = 36 | "HandlerError: You must specify both slug and tracking number"; 37 | const param = { 38 | slug: "ups", 39 | }; 40 | try { 41 | await aftership.last_checkpoint.getLastCheckpoint(param); 42 | } catch (e) { 43 | expect(e.message).toEqual(expected_error); 44 | } 45 | }); 46 | 47 | it("should throw exception when only specify tracking number", async function () { 48 | let expected_error = 49 | "HandlerError: You must specify both slug and tracking number"; 50 | const param = { 51 | tracking_number: "1234567890", 52 | }; 53 | try { 54 | await aftership.last_checkpoint.getLastCheckpoint(param); 55 | } catch (e) { 56 | expect(e.message).toEqual(expected_error); 57 | } 58 | }); 59 | }); 60 | 61 | describe("#getLastCheckpoint({slug, tracking_number})", function () { 62 | it("should get last checkpoint when success", function (done) { 63 | const param = { 64 | slug: "ups", 65 | tracking_number: "1234567890", 66 | }; 67 | 68 | // This sets the mock adapter on the default instance 69 | var mock = new MockAdapter(axios); 70 | mock 71 | .onGet(`/last_checkpoint/${param.slug}/${param.tracking_number}`) 72 | .reply( 73 | 200, 74 | { 75 | meta: { 76 | code: 200, 77 | }, 78 | data: { 79 | id: "5b74f4958776db0e00b6f5ed", 80 | tracking_number: "111111111111", 81 | slug: "fedex", 82 | tag: "Delivered", 83 | subtag: "Delivered_001", 84 | subtag_message: "Delivered", 85 | checkpoint: { 86 | slug: "fedex", 87 | created_at: "2018-08-16T03:50:47+00:00", 88 | checkpoint_time: "2018-08-01T13:19:47-04:00", 89 | city: "Deal", 90 | coordinates: [], 91 | country_iso3: null, 92 | country_name: null, 93 | message: 94 | "Delivered - Left at front door. Signature Service not requested.", 95 | state: "NJ", 96 | tag: "Delivered", 97 | subtag: "Delivered_001", 98 | subtag_message: "Delivered", 99 | zip: null, 100 | raw_tag: "FPX_L_RPIF", 101 | }, 102 | }, 103 | }, 104 | { 105 | "x-ratelimit-reset": 1406096275, 106 | "x-ratelimit-limit": 10, 107 | "x-ratelimit-remaining": 9, 108 | } 109 | ); 110 | 111 | aftership.last_checkpoint 112 | .getLastCheckpoint(param) 113 | .then((result) => { 114 | if ( 115 | result && 116 | result.checkpoint && 117 | result.checkpoint.slug === "fedex" 118 | ) { 119 | done(); 120 | } else { 121 | done("not found last checkpoint"); 122 | } 123 | }) 124 | .catch((e) => done(e.message)); 125 | }); 126 | 127 | it("should get error meta when no success", function (done) { 128 | const param = { 129 | slug: "ups", 130 | tracking_number: "1234567890", 131 | }; 132 | 133 | // This sets the mock adapter on the default instance 134 | var mock = new MockAdapter(axios); 135 | mock 136 | .onGet(`/last_checkpoint/${param.slug}/${param.tracking_number}`) 137 | .reply( 138 | 404, 139 | { 140 | meta: { 141 | code: 4004, 142 | type: "BadRequest", 143 | message: "Tracking does not exist.", 144 | }, 145 | data: {}, 146 | }, 147 | { 148 | "x-ratelimit-reset": 1406096275, 149 | "x-ratelimit-limit": 10, 150 | "x-ratelimit-remaining": 9, 151 | } 152 | ); 153 | 154 | aftership.last_checkpoint 155 | .getLastCheckpoint(param) 156 | .then(_ => { 157 | done("not catch the exception"); 158 | }) 159 | .catch(e => { 160 | if (e.type === "BadRequest") { 161 | done(); 162 | } else { 163 | done("not parse the error type correctly"); 164 | } 165 | }); 166 | }); 167 | }); 168 | 169 | describe("#getLastCheckpoint({id})", function () { 170 | it("should get last checkpoint when success", function (done) { 171 | const param = { 172 | id: "5b74f4958776db0e00b6f5ed", 173 | }; 174 | 175 | // This sets the mock adapter on the default instance 176 | var mock = new MockAdapter(axios); 177 | mock.onGet(`/last_checkpoint/${param.id}`).reply( 178 | 200, 179 | { 180 | meta: { 181 | code: 200, 182 | }, 183 | data: { 184 | id: "5b74f4958776db0e00b6f5ed", 185 | tracking_number: "111111111111", 186 | slug: "fedex", 187 | tag: "Delivered", 188 | subtag: "Delivered_001", 189 | subtag_message: "Delivered", 190 | checkpoint: { 191 | slug: "fedex", 192 | created_at: "2018-08-16T03:50:47+00:00", 193 | checkpoint_time: "2018-08-01T13:19:47-04:00", 194 | city: "Deal", 195 | coordinates: [], 196 | country_iso3: null, 197 | country_name: null, 198 | message: 199 | "Delivered - Left at front door. Signature Service not requested.", 200 | state: "NJ", 201 | tag: "Delivered", 202 | subtag: "Delivered_001", 203 | subtag_message: "Delivered", 204 | zip: null, 205 | raw_tag: "FPX_L_RPIF", 206 | }, 207 | }, 208 | }, 209 | { 210 | "x-ratelimit-reset": 1406096275, 211 | "x-ratelimit-limit": 10, 212 | "x-ratelimit-remaining": 9, 213 | } 214 | ); 215 | 216 | aftership.last_checkpoint 217 | .getLastCheckpoint(param) 218 | .then((result) => { 219 | if ( 220 | result && 221 | result.checkpoint && 222 | result.checkpoint.slug === "fedex" 223 | ) { 224 | done(); 225 | } else { 226 | done("not found last checkpoint"); 227 | } 228 | }) 229 | .catch((e) => done(e.message)); 230 | }); 231 | 232 | it("should catch exception when error", function (done) { 233 | const param = { 234 | id: "5b74f4958776db0e00b6f5ed", 235 | }; 236 | 237 | // This sets the mock adapter on the default instance 238 | var mock = new MockAdapter(axios); 239 | mock.onGet(`/last_checkpoint/${param.id}`).reply( 240 | 404, 241 | { 242 | meta: { 243 | code: 4004, 244 | type: "BadRequest", 245 | message: "Tracking does not exist.", 246 | }, 247 | data: {}, 248 | }, 249 | { 250 | "x-ratelimit-reset": 1406096275, 251 | "x-ratelimit-limit": 10, 252 | "x-ratelimit-remaining": 9, 253 | } 254 | ); 255 | 256 | aftership.last_checkpoint 257 | .getLastCheckpoint(param) 258 | .then(_ => { 259 | done("not catch the exception"); 260 | }) 261 | .catch(e => { 262 | if (e.type === "BadRequest") { 263 | done(); 264 | } else { 265 | done("not parse the error type correctly"); 266 | } 267 | }); 268 | }); 269 | }); 270 | }); 271 | -------------------------------------------------------------------------------- /__tests__/test_tracking.js: -------------------------------------------------------------------------------- 1 | var Aftership = require("../dist/index.js").AfterShip; 2 | var TrackingCreateParams = require("../dist/model/tracking/tracking_create_params").TrackingCreateParams 3 | var axios = require("axios"); 4 | var MockAdapter = require("axios-mock-adapter"); 5 | 6 | var aftership = new Aftership("SOME_API_KEY"); 7 | 8 | describe("Tracking", function () { 9 | describe("#createTracking(), validate post body", function () { 10 | it("should throw exception when not specify tracking number", function () { 11 | let expected_error = 12 | "ConstructorError: tracking_number"; 13 | 14 | try { 15 | const param = new TrackingCreateParams({ 16 | slug: "ups", 17 | }); 18 | 19 | } catch (e) { 20 | expect(e.message).toEqual(expected_error); 21 | } 22 | }); 23 | }); 24 | 25 | describe("#deleteTracking(), validate url params", function () { 26 | it("should throw exception when both specify id and tracking number", async function () { 27 | let expected_error = 28 | "HandlerError: Cannot specify id and tracking number at the same time"; 29 | const param = { 30 | id: "5b74f4958776db0e00b6f5ed", 31 | slug: "ups", 32 | tracking_number: "1234567890", 33 | }; 34 | try { 35 | await aftership.tracking.deleteTracking(param); 36 | } catch (e) { 37 | expect(e.message).toEqual(expected_error); 38 | } 39 | }); 40 | 41 | it("should throw exception when only specify slug", async function () { 42 | let expected_error = 43 | "HandlerError: You must specify both slug and tracking number"; 44 | const param = { 45 | slug: "ups", 46 | }; 47 | try { 48 | await aftership.tracking.deleteTracking(param); 49 | } catch (e) { 50 | expect(e.message).toEqual(expected_error); 51 | } 52 | }); 53 | 54 | it("should throw exception when only specify tracking number", async function () { 55 | let expected_error = 56 | "HandlerError: You must specify both slug and tracking number"; 57 | const param = { 58 | tracking_number: "1234567890", 59 | }; 60 | try { 61 | await aftership.tracking.deleteTracking(param); 62 | } catch (e) { 63 | expect(e.message).toEqual(expected_error); 64 | } 65 | }); 66 | }); 67 | 68 | describe("#listTrackings()", function () { 69 | it("should get trackings when success", function (done) { 70 | 71 | // This sets the mock adapter on the default instance 72 | var mock = new MockAdapter(axios); 73 | mock.onGet(`/trackings`).reply( 74 | 200, 75 | { 76 | "meta": { 77 | "code": 200 78 | }, 79 | "data": { 80 | "page": 1, 81 | "limit": 100, 82 | "count": 4, 83 | "keyword": "", 84 | "slug": "", 85 | "origin": [], 86 | "destination": [], 87 | "transit_time": [], 88 | "tag": "", 89 | "fields": "", 90 | "created_at_min": "2023-08-22T11:12:42+00:00", 91 | "created_at_max": "2023-12-20T11:12:42+00:00", 92 | "last_updated_at": null, 93 | "return_to_sender": [], 94 | "courier_destination_country_iso3": [], 95 | "trackings": [ 96 | { 97 | "id": "q82bqhzdrcxbjlqdnzng5024", 98 | "created_at": "2023-12-20T11:02:21+00:00", 99 | "updated_at": "2023-12-20T11:02:22+00:00", 100 | "last_updated_at": "2023-12-20T11:02:22+00:00", 101 | "tracking_number": "9434936105536968782556", 102 | "slug": "usps", 103 | "active": true, 104 | "android": [], 105 | "custom_fields": null, 106 | "customer_name": "3412341234", 107 | "destination_country_iso3": "AND", 108 | "courier_destination_country_iso3": "USA", 109 | "emails": [ 110 | "2342343@gmail.com" 111 | ], 112 | "expected_delivery": null, 113 | "ios": [], 114 | "note": "asdfasdfasdfa", 115 | "order_id": null, 116 | "order_id_path": null, 117 | "order_date": null, 118 | "origin_country_iso3": "AFG", 119 | "shipment_package_count": null, 120 | "shipment_pickup_date": null, 121 | "shipment_delivery_date": null, 122 | "shipment_type": "USPS Ground Advantage", 123 | "shipment_weight": null, 124 | "shipment_weight_unit": null, 125 | "signed_by": null, 126 | "smses": [ 127 | "+8613450378329" 128 | ], 129 | "source": "web", 130 | "tag": "InfoReceived", 131 | "subtag": "InfoReceived_001", 132 | "subtag_message": "Info Received", 133 | "title": "13r412341", 134 | "tracked_count": 1, 135 | "last_mile_tracking_supported": true, 136 | "language": "ab", 137 | "unique_token": "deprecated", 138 | "checkpoints": [ 139 | { 140 | "slug": "usps", 141 | "city": "FAYETTEVILLE", 142 | "created_at": "2023-12-20T11:02:22+00:00", 143 | "location": "FAYETTEVILLE, TX, 78940, USA, United States", 144 | "country_name": "USA", 145 | "message": "Shipping Label Created, USPS Awaiting Item", 146 | "country_iso3": "USA", 147 | "tag": "InfoReceived", 148 | "subtag": "InfoReceived_001", 149 | "subtag_message": "Info Received", 150 | "checkpoint_time": "2023-12-20T02:12:00-06:00", 151 | "coordinates": [], 152 | "state": "TX", 153 | "zip": "78940", 154 | "raw_tag": "GX" 155 | } 156 | ], 157 | "subscribed_smses": [], 158 | "subscribed_emails": [], 159 | "return_to_sender": false, 160 | "order_promised_delivery_date": null, 161 | "delivery_type": null, 162 | "pickup_location": null, 163 | "pickup_note": null, 164 | "courier_tracking_link": "https://tools.usps.com/go/TrackConfirmAction?tLabels=9434936105536968782556", 165 | "first_attempted_at": null, 166 | "courier_redirect_link": "https://tools.usps.com/go/TrackConfirmAction?tRef=fullpage&tLc=2&text28777=&tLabels=9434936105536968782556%2C", 167 | "order_tags": null, 168 | "order_number": "23423142", 169 | "aftership_estimated_delivery_date": null, 170 | "destination_raw_location": "VINELAND, NJ, 08360, USA, United States", 171 | "latest_estimated_delivery": null, 172 | "courier_connection_id": null, 173 | "first_estimated_delivery": null, 174 | "custom_estimated_delivery_date": null, 175 | "origin_state": null, 176 | "origin_city": null, 177 | "origin_postal_code": null, 178 | "origin_raw_location": "United States", 179 | "destination_state": "NJ", 180 | "destination_city": "VINELAND", 181 | "destination_postal_code": "08360", 182 | "shipment_tags": [], 183 | "next_couriers": [], 184 | "transit_time": null, 185 | "on_time_status": null, 186 | "on_time_difference": null, 187 | "tracking_account_number": null, 188 | "tracking_origin_country": "AFG", 189 | "tracking_destination_country": "AND", 190 | "tracking_key": null, 191 | "tracking_postal_code": null, 192 | "tracking_ship_date": null, 193 | "tracking_state": null 194 | } 195 | ] 196 | } 197 | }, 198 | { 199 | "x-ratelimit-reset": 1406096275, 200 | "x-ratelimit-limit": 10, 201 | "x-ratelimit-remaining": 9, 202 | } 203 | ); 204 | 205 | aftership 206 | .tracking.listTrackings() 207 | .then(x => { 208 | const trackings = x.trackings 209 | if (trackings.length === 1) { 210 | done() 211 | } else { 212 | done('not found tracking list') 213 | } 214 | }) 215 | .catch(e => done(e)) 216 | }); 217 | }); 218 | 219 | describe("#getTracking({slug, tracking_number})", function () { 220 | it("should get trackings when success", function (done) { 221 | const param = { 222 | slug: "ups", 223 | tracking_number: "1234567890", 224 | }; 225 | 226 | // This sets the mock adapter on the default instance 227 | var mock = new MockAdapter(axios); 228 | mock.onGet(`/trackings/${param.slug}/${param.tracking_number}`).reply( 229 | 200, 230 | { 231 | "meta": { 232 | "code": 200 233 | }, 234 | "data": { 235 | "tracking": 236 | { 237 | "id": "5b74f4958776db0e00b6f5ed", 238 | "created_at": "2018-08-16T03:50:45+00:00", 239 | "updated_at": "2018-08-16T03:50:54+00:00", 240 | "last_updated_at": "2018-08-16T03:50:53+00:00", 241 | "tracking_number": "1234567890", 242 | "slug": "ups", 243 | "active": false, 244 | "android": [], 245 | "custom_fields": null, 246 | "customer_name": null, 247 | "delivery_time": 2, 248 | "destination_country_iso3": null, 249 | "courier_destination_country_iso3": null, 250 | "emails": [], 251 | "expected_delivery": null, 252 | "ios": [], 253 | "note": null, 254 | "order_id": null, 255 | "order_id_path": null, 256 | "order_date": null, 257 | "order_number": null, 258 | "origin_country_iso3": "USA", 259 | "shipment_package_count": 1, 260 | "shipment_pickup_date": "2018-07-31T06:00:00", 261 | "shipment_delivery_date": "2018-08-01T17:19:47", 262 | "shipment_type": "FedEx Home Delivery", 263 | "shipment_weight": null, 264 | "shipment_weight_unit": "kg", 265 | "signed_by": "Signature not required", 266 | "smses": [], 267 | "source": "web", 268 | "tag": "Delivered", 269 | "subtag": "Delivered_001", 270 | "subtag_message": "Delivered", 271 | "title": "1111111111111", 272 | "tracked_count": 1, 273 | "last_mile_tracking_supported": null, 274 | "language": null, 275 | "unique_token": "deprecated", 276 | "checkpoints": [ 277 | { 278 | "slug": "fedex", 279 | "city": null, 280 | "created_at": "2018-08-16T03:50:47+00:00", 281 | "location": null, 282 | "country_name": null, 283 | "message": "Shipment information sent to FedEx", 284 | "country_iso3": null, 285 | "tag": "InfoReceived", 286 | "subtag": "InfoReceived_001", 287 | "subtag_message": "Info Received", 288 | "checkpoint_time": "2018-07-31T10:33:00-04:00", 289 | "coordinates": [], 290 | "state": null, 291 | "zip": null, 292 | "raw_tag": "FPX_L_RPIF" 293 | } 294 | ], 295 | "subscribed_smses": [], 296 | "subscribed_emails": [], 297 | "return_to_sender": false, 298 | "tracking_account_number": null, 299 | "tracking_origin_country": null, 300 | "tracking_destination_country": null, 301 | "tracking_key": null, 302 | "tracking_postal_code": null, 303 | "tracking_ship_date": null, 304 | "tracking_state": null, 305 | "order_promised_delivery_date": null, 306 | "delivery_type": null, 307 | "pickup_location": null, 308 | "pickup_note": null, 309 | "courier_tracking_link": "https://www.fedex.com/fedextrack/?tracknumbers=111111111111&cntry_code=us", 310 | "first_attempted_at": "2018-08-01T13:19:47-04:00", 311 | "aftership_estimated_delivery_date": null, 312 | "latest_estimated_delivery": { 313 | "type": "specific", 314 | "source": "Custom EDD", 315 | "datetime": "2022-07-06", 316 | "datetime_min": null, 317 | "datetime_max": null 318 | } 319 | } 320 | } 321 | }, 322 | { 323 | "x-ratelimit-reset": 1406096275, 324 | "x-ratelimit-limit": 10, 325 | "x-ratelimit-remaining": 9, 326 | } 327 | ); 328 | 329 | aftership 330 | .tracking.getTracking(param) 331 | .then(x => { 332 | 333 | const tracking = x.tracking 334 | if (tracking.slug === 'ups') { 335 | done() 336 | } else { 337 | done('not found tracking') 338 | } 339 | }) 340 | .catch(e => done(e)) 341 | }); 342 | }); 343 | 344 | describe("#updateTracking(), validate url params", function () { 345 | it("should throw exception when both specify id and tracking number", async function () { 346 | let expected_error = 347 | "HandlerError: Cannot specify id and tracking number at the same time"; 348 | const param = { 349 | id: "5b74f4958776db0e00b6f5ed", 350 | slug: "ups", 351 | tracking_number: "1234567890", 352 | }; 353 | try { 354 | await aftership.tracking.updateTracking(param); 355 | } catch (e) { 356 | expect(e.message).toEqual(expected_error); 357 | } 358 | }); 359 | 360 | it("should throw exception when only specify slug", async function () { 361 | let expected_error = 362 | "HandlerError: You must specify both slug and tracking number"; 363 | const param = { 364 | slug: "ups", 365 | }; 366 | try { 367 | await aftership.tracking.updateTracking(param); 368 | } catch (e) { 369 | expect(e.message).toEqual(expected_error); 370 | } 371 | }); 372 | 373 | it("should throw exception when only specify tracking number", async function () { 374 | let expected_error = 375 | "HandlerError: You must specify both slug and tracking number"; 376 | const param = { 377 | tracking_number: "1234567890", 378 | }; 379 | try { 380 | await aftership.tracking.updateTracking(param); 381 | } catch (e) { 382 | expect(e.message).toEqual(expected_error); 383 | } 384 | }); 385 | }); 386 | 387 | describe("#retrack(), validate url params", function () { 388 | it("should throw exception when both specify id and tracking number", async function () { 389 | let expected_error = 390 | "HandlerError: Cannot specify id and tracking number at the same time"; 391 | const param = { 392 | id: "5b74f4958776db0e00b6f5ed", 393 | slug: "ups", 394 | tracking_number: "1234567890", 395 | }; 396 | try { 397 | await aftership.tracking.retrack(param); 398 | } catch (e) { 399 | expect(e.message).toEqual(expected_error); 400 | } 401 | }); 402 | 403 | it("should throw exception when only specify slug", async function () { 404 | let expected_error = 405 | "HandlerError: You must specify both slug and tracking number"; 406 | const param = { 407 | slug: "ups", 408 | }; 409 | try { 410 | await aftership.tracking.retrack(param); 411 | } catch (e) { 412 | expect(e.message).toEqual(expected_error); 413 | } 414 | }); 415 | 416 | it("should throw exception when only specify tracking number", async function () { 417 | let expected_error = 418 | "HandlerError: You must specify both slug and tracking number"; 419 | const param = { 420 | tracking_number: "1234567890", 421 | }; 422 | try { 423 | await aftership.tracking.retrack(param); 424 | } catch (e) { 425 | expect(e.message).toEqual(expected_error); 426 | } 427 | }); 428 | }); 429 | 430 | describe("#markAsCompleted({slug, tracking_number}, { reason })", function () { 431 | it("should get tracking when success", function (done) { 432 | const param = { 433 | slug: "ups", 434 | tracking_number: "1234567890", 435 | }; 436 | 437 | const reason = { 438 | reason: "DELIVERED", 439 | } 440 | 441 | // This sets the mock adapter on the default instance 442 | var mock = new MockAdapter(axios); 443 | mock.onPost(`/trackings/${param.slug}/${param.tracking_number}/mark-as-completed`).reply( 444 | 200, 445 | { 446 | "meta": { 447 | "code": 200 448 | }, 449 | "data": { 450 | "tracking": 451 | { 452 | "id": "5b74f4958776db0e00b6f5ed", 453 | "created_at": "2018-08-16T03:50:45+00:00", 454 | "updated_at": "2018-08-16T03:50:54+00:00", 455 | "last_updated_at": "2018-08-16T03:50:53+00:00", 456 | "tracking_number": "1234567890", 457 | "slug": "ups", 458 | "active": false, 459 | "android": [], 460 | "custom_fields": null, 461 | "customer_name": null, 462 | "delivery_time": 2, 463 | "destination_country_iso3": null, 464 | "courier_destination_country_iso3": null, 465 | } 466 | } 467 | }, 468 | { 469 | "x-ratelimit-reset": 1406096275, 470 | "x-ratelimit-limit": 10, 471 | "x-ratelimit-remaining": 9, 472 | } 473 | ); 474 | 475 | aftership 476 | .tracking.markAsCompleted(param, reason) 477 | .then(x => { 478 | const tracking = x.tracking 479 | if (tracking) { 480 | done() 481 | } else { 482 | done('not return tracking after markAsCompleted') 483 | } 484 | }) 485 | .catch(e => done(e)) 486 | }); 487 | }); 488 | 489 | describe("#markAsCompleted(), validate params", function () { 490 | it("should throw exception when both specify id and tracking number", async function () { 491 | let expected_error = 492 | "HandlerError: Cannot specify id and tracking number at the same time"; 493 | const param = { 494 | id: "5b74f4958776db0e00b6f5ed", 495 | slug: "ups", 496 | tracking_number: "1234567890", 497 | }; 498 | 499 | const reason = { 500 | reason: "DELIVERED", 501 | } 502 | 503 | try { 504 | await aftership.tracking.markAsCompleted(param, reason); 505 | } catch (e) { 506 | expect(e.message).toEqual(expected_error); 507 | } 508 | }); 509 | 510 | it("should throw exception when only specify slug", async function () { 511 | let expected_error = 512 | "HandlerError: You must specify both slug and tracking number"; 513 | const param = { 514 | slug: "ups", 515 | }; 516 | 517 | const reason = { 518 | reason: "DELIVERED", 519 | } 520 | 521 | try { 522 | await aftership.tracking.markAsCompleted(param, reason); 523 | } catch (e) { 524 | expect(e.message).toEqual(expected_error); 525 | } 526 | }); 527 | 528 | it("should throw exception when only specify tracking number", async function () { 529 | let expected_error = 530 | "HandlerError: You must specify both slug and tracking number"; 531 | const param = { 532 | tracking_number: "1234567890", 533 | }; 534 | 535 | const reason = { 536 | reason: "DELIVERED", 537 | } 538 | 539 | try { 540 | await aftership.tracking.markAsCompleted(param, reason); 541 | } catch (e) { 542 | expect(e.message).toEqual(expected_error); 543 | } 544 | }); 545 | 546 | it("should throw exception when reason is incorrect", async function () { 547 | let expected_error = 548 | `HandlerError: Reason must be one of "DELIVERED", "LOST" or "RETURNED_TO_SENDER"`; 549 | const param = { 550 | slug: "ups", 551 | tracking_number: "1234567890", 552 | }; 553 | 554 | const reason = { 555 | reason: "INVALID REASON", 556 | } 557 | 558 | try { 559 | await aftership.tracking.markAsCompleted(param, reason); 560 | } catch (e) { 561 | expect(e.message).toEqual(expected_error); 562 | } 563 | }); 564 | }); 565 | }); 566 | -------------------------------------------------------------------------------- /__tests__/test_util.js: -------------------------------------------------------------------------------- 1 | var util = require("../dist/lib/util.js"); 2 | 3 | describe("Test util", function () { 4 | describe("Test correct cases in buildTrackingUrl()", function () { 5 | it("should build url with slug and tracking_number correctly", function () { 6 | const slug = "ups"; 7 | const tracking_number = "1234567890"; 8 | const url = util.buildTrackingUrl({ 9 | slug, 10 | tracking_number, 11 | }); 12 | 13 | expect(url).toEqual(`${slug}/${tracking_number}`); 14 | }); 15 | 16 | it("should build url with slug, tracking_number and one optional parameter correctly", function () { 17 | const slug = "ups"; 18 | const tracking_number = "1234567890"; 19 | const optional_parameters = { 20 | tracking_postal_code: "1234", 21 | }; 22 | const url = util.buildTrackingUrl({ 23 | slug, 24 | tracking_number, 25 | optional_parameters, 26 | }); 27 | 28 | expect(url).toEqual( 29 | `${slug}/${tracking_number}?tracking_postal_code=${optional_parameters.tracking_postal_code}` 30 | ); 31 | }); 32 | 33 | it("should build url with slug, tracking_number and multi optional parameters correctly", function () { 34 | const slug = "ups"; 35 | const tracking_number = "1234567890"; 36 | const optional_parameters = { 37 | tracking_postal_code: "1234", 38 | tracking_ship_date: "20200423", 39 | }; 40 | const url = util.buildTrackingUrl({ 41 | slug, 42 | tracking_number, 43 | optional_parameters, 44 | }); 45 | 46 | expect(url).toEqual( 47 | `${slug}/${tracking_number}?tracking_postal_code=${optional_parameters.tracking_postal_code}&tracking_ship_date=${optional_parameters.tracking_ship_date}` 48 | ); 49 | }); 50 | 51 | it("should build url with slug, tracking_number and sub path correctly", function () { 52 | const slug = "ups"; 53 | const tracking_number = "1234567890"; 54 | const sub_path = "retrack"; 55 | const url = util.buildTrackingUrl({ 56 | slug, 57 | tracking_number, 58 | }, sub_path); 59 | 60 | expect(url).toEqual( 61 | `${slug}/${tracking_number}/${sub_path}` 62 | ); 63 | }); 64 | 65 | it("should build url with slug, tracking_number, sub path and optional parameters correctly", function () { 66 | const slug = "ups"; 67 | const tracking_number = "1234567890"; 68 | const optional_parameters = { 69 | tracking_postal_code: "1234", 70 | }; 71 | const sub_path = "retrack"; 72 | const url = util.buildTrackingUrl({ 73 | slug, 74 | tracking_number, 75 | optional_parameters, 76 | }, sub_path); 77 | 78 | expect(url).toEqual( 79 | `${slug}/${tracking_number}/${sub_path}?tracking_postal_code=${optional_parameters.tracking_postal_code}` 80 | ); 81 | }); 82 | 83 | it("should build url with id correctly", function () { 84 | const id = "1234567890"; 85 | const url = util.buildTrackingUrl({ 86 | id, 87 | }); 88 | 89 | expect(url).toEqual(id); 90 | }); 91 | 92 | it("should build url with id and one optional parameter correctly", function () { 93 | const id = "1234567890"; 94 | const optional_parameters = { 95 | tracking_postal_code: "1234", 96 | }; 97 | const url = util.buildTrackingUrl({ 98 | id, 99 | optional_parameters, 100 | }); 101 | 102 | expect(url).toEqual( 103 | `${id}?tracking_postal_code=${optional_parameters.tracking_postal_code}` 104 | ); 105 | }); 106 | 107 | it("should build url with id and multi optional parameters correctly", function () { 108 | const id = "1234567890"; 109 | const optional_parameters = { 110 | tracking_postal_code: "1234", 111 | tracking_ship_date: "20200423", 112 | }; 113 | const url = util.buildTrackingUrl({ 114 | id, 115 | optional_parameters, 116 | }); 117 | 118 | expect(url).toEqual( 119 | `${id}?tracking_postal_code=${optional_parameters.tracking_postal_code}&tracking_ship_date=${optional_parameters.tracking_ship_date}` 120 | ); 121 | }); 122 | 123 | it("should build url with id and sub path correctly", function () { 124 | const id = "1234567890"; 125 | const sub_path = "retrack"; 126 | const url = util.buildTrackingUrl({ 127 | id, 128 | }, sub_path); 129 | 130 | expect(url).toEqual( 131 | `${id}/${sub_path}` 132 | ); 133 | }); 134 | 135 | it("should build url with id, sub path and optional parameters correctly", function () { 136 | const id = "1234567890"; 137 | const optional_parameters = { 138 | tracking_postal_code: "1234", 139 | }; 140 | const sub_path = "retrack"; 141 | const url = util.buildTrackingUrl({ 142 | id, 143 | optional_parameters, 144 | }, sub_path); 145 | 146 | expect(url).toEqual( 147 | `${id}/${sub_path}?tracking_postal_code=${optional_parameters.tracking_postal_code}` 148 | ); 149 | }); 150 | }); 151 | 152 | describe("Test error in buildTrackingUrl()", function () { 153 | it("should return error, if id, slug and trackibng_number are empty", function () { 154 | let expected_error = 155 | "HandlerError: You must specify the id or slug and tracking number"; 156 | try { 157 | util.buildTrackingUrl(); 158 | } catch (e) { 159 | expect(e.message).toEqual(expected_error); 160 | } 161 | }); 162 | 163 | it("should return error, if both id and trackibng_number are specificed", function () { 164 | let expected_error = 165 | 'HandlerError: Cannot specify id and tracking number at the same time'; 166 | try { 167 | util.buildTrackingUrl({ 168 | id: "5b74f4958776db0e00b6f5ed", 169 | tracking_number: "1234567890", 170 | }); 171 | } catch (e) { 172 | expect(e.message).toEqual(expected_error); 173 | } 174 | }); 175 | 176 | it("should return error, if all id. slug and trackibng_number are specificed", function () { 177 | let expected_error = 178 | 'HandlerError: Cannot specify id and tracking number at the same time'; 179 | try { 180 | util.buildTrackingUrl({ 181 | id: "5b74f4958776db0e00b6f5ed", 182 | slug: "ups", 183 | tracking_number: "1234567890", 184 | }); 185 | } catch (e) { 186 | expect(e.message).toEqual(expected_error); 187 | } 188 | }); 189 | 190 | it("should return error, if only trackibng_number is specificed", function () { 191 | let expected_error = 192 | 'HandlerError: You must specify both slug and tracking number'; 193 | try { 194 | util.buildTrackingUrl({ 195 | tracking_number: "1234567890", 196 | }); 197 | } catch (e) { 198 | expect(e.message).toEqual(expected_error); 199 | } 200 | }); 201 | 202 | it("should return error, if only slug is specificed", function () { 203 | let expected_error = 204 | 'HandlerError: You must specify both slug and tracking number'; 205 | try { 206 | util.buildTrackingUrl({ 207 | slug: "ups", 208 | }); 209 | } catch (e) { 210 | expect(e.message).toEqual(expected_error); 211 | } 212 | }); 213 | }); 214 | }); 215 | -------------------------------------------------------------------------------- /example/courier.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const API_KEY = 'SOME_API_KEY'; 4 | const { AfterShip } = require('../dist/index.js'); 5 | 6 | const aftership = new AfterShip(API_KEY); 7 | 8 | // GET /couriers 9 | aftership.courier.listCouriers() 10 | .then(result => console.log(result)) 11 | .catch(e => console.log(e)); 12 | 13 | 14 | // GET /couriers/all 15 | aftership.courier.listAllCouriers() 16 | .then(result => console.log(result)) 17 | .catch(e => console.log(e)); 18 | 19 | // POST /couriers/detect 20 | const payload = { 21 | 'tracking': { 22 | 'tracking_number': '906587618687', 23 | 'tracking_postal_code': 'DA15BU', 24 | 'tracking_ship_date': '20131231', 25 | 'tracking_account_number': '1234567890', 26 | 'slug': ['dhl', 'ups', 'fedex'] 27 | } 28 | } 29 | aftership.courier.detectCouriers(payload) 30 | .then(result => console.log(result)) 31 | .catch(e => console.log(e)); 32 | -------------------------------------------------------------------------------- /example/last_checkpoint.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const API_KEY = 'SOME_API_KEY'; 4 | const { AfterShip } = require('../dist/index.js'); 5 | 6 | const aftership = new AfterShip(API_KEY); 7 | 8 | // GET /last_checkpoint/:tracking_id 9 | aftership.last_checkpoint.getLastCheckpoint({ 10 | id:'oox5j5339istjlpwfoky' 11 | }) 12 | .then(result => console.log(result)) 13 | .catch(e => console.log(e)); 14 | 15 | // GET /last_checkpoint/:tracking_id 16 | aftership.last_checkpoint.getLastCheckpoint({ 17 | id: '5b74f4958776db0e00b6f5ed', 18 | }) 19 | .then(result => console.log(result)) 20 | .catch(e => console.log(e)); 21 | -------------------------------------------------------------------------------- /example/notification.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const API_KEY = 'SOME_API_KEY'; 4 | const { AfterShip } = require('../dist/index.js'); 5 | 6 | const aftership = new AfterShip(API_KEY); 7 | 8 | let param = { 9 | slug: "ups", 10 | tracking_number: "1234567890", 11 | }; 12 | 13 | // GET /notifications/:slug/:tracking_number 14 | aftership.notification.getNotification(param) 15 | .then(result => console.log(result)) 16 | .catch(e => console.log(e)); 17 | 18 | 19 | // POST /notifications/:slug/:tracking_number/add 20 | let notification = { 21 | 'notification': { 22 | 'emails': ['user1@gmail.com','user2@gmail.com','invalid EMail @ Gmail. com'], 23 | 'smses': ['+85291239123', '+85261236123', 'Invalid Mobile Phone Number'] 24 | } 25 | }; 26 | aftership.notification.addNotification(param, notification) 27 | .then(result => console.log(result)) 28 | .catch(e => console.log(e)); 29 | 30 | 31 | // POST /notifications/:slug/:tracking_number/remove 32 | notification = { 33 | 'notification': { 34 | 'emails': ['user1@gmail.com','user2@gmail.com','invalid EMail @ Gmail. com'], 35 | 'smses': ['+85291239123', '+85261236123', 'Invalid Mobile Phone Number'] 36 | } 37 | }; 38 | aftership.notification.removeNotification(param, notification) 39 | .then(result => console.log(result)) 40 | .catch(e => console.log(e)); 41 | 42 | -------------------------------------------------------------------------------- /example/tracking.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const API_KEY = "SOME_API_KEY"; 4 | const { AfterShip } = require('../dist/index.js'); 5 | 6 | const aftership = new AfterShip(API_KEY); 7 | 8 | // POST /trackings 9 | let payload = { 10 | tracking: { 11 | slug: "dhl", 12 | tracking_number: "123456789", 13 | title: "Title Name", 14 | smses: ["+18555072509", "+18555072501"], 15 | emails: ["email@yourdomain.com", "another_email@yourdomain.com"], 16 | order_id: "ID 1234", 17 | order_id_path: "http://www.aftership.com/order_id=1234", 18 | custom_fields: { 19 | product_name: "iPhone Case", 20 | product_price: "USD19.99", 21 | }, 22 | language: "en", 23 | order_promised_delivery_date: "2019-05-20", 24 | delivery_type: "pickup_at_store", 25 | pickup_location: "Flagship Store", 26 | pickup_note: 27 | "Reach out to our staffs when you arrive our stores for shipment pickup", 28 | }, 29 | }; 30 | aftership.tracking 31 | .createTracking(payload) 32 | .then((result) => console.log(result)) 33 | .catch((e) => console.log(e)); 34 | 35 | 36 | // DELETE /trackings/:tracking_id 37 | aftership.tracking 38 | .deleteTracking({ 39 | id: 'id1234567890' 40 | }) 41 | .then((result) => console.log(result)) 42 | .catch((e) => console.log(e)); 43 | 44 | 45 | // GET /trackings 46 | const query = { 47 | slug: 'dhl,ups,usps' 48 | }; 49 | aftership.tracking 50 | .listTrackings(query) 51 | .then((result) => console.log(result)) 52 | .catch((e) => console.log(e)); 53 | 54 | 55 | // GET /trackings/:tracking_id 56 | aftership.tracking 57 | .getTracking({ 58 | id: "id1234567890" 59 | }) 60 | .then((result) => console.log(result)) 61 | .catch((e) => console.log(e)); 62 | 63 | // PUT /trackings/:tracking_id 64 | payload = { 65 | tracking: { 66 | title: "New Title", 67 | }, 68 | }; 69 | aftership.tracking 70 | .updateTracking({ 71 | id: "id1234567890" 72 | }, payload) 73 | .then((result) => console.log(result)) 74 | .catch((e) => console.log(e)); 75 | 76 | 77 | // POST /trackings/:tracking_id 78 | aftership.tracking 79 | .retrack({ 80 | id: "id1234567890" 81 | }) 82 | .then((result) => console.log(result)) 83 | .catch((e) => console.log(e)); 84 | 85 | 86 | // POST /trackings/:tracking_id/mark-as-completed 87 | aftership.tracking 88 | .markAsCompleted({ 89 | id: "id1234567890" 90 | }, { 91 | reason: "DELIVERED" 92 | }) 93 | .then((result) => console.log(result)) 94 | .catch((e) => console.log(e)); 95 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | coverageDirectory: "./coverage/", 4 | collectCoverage: true, 5 | }; 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aftership", 3 | "description": "node.js SDK for AfterShip API", 4 | "version": "8.1.0", 5 | "homepage": "https://github.com/aftership/aftership-sdk-nodejs", 6 | "author": { 7 | "name": "AfterShip", 8 | "email": "support@aftership.com" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/aftership/aftership-sdk-nodejs.git" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/aftership/aftership-sdk-nodejs/issues" 16 | }, 17 | "main": "dist/index.js", 18 | "types": "dist/index.d.ts", 19 | "engines": { 20 | "node": ">=10.0" 21 | }, 22 | "scripts": { 23 | "prepare": "npm run build", 24 | "build": "npm run build:clean && tsc", 25 | "build:clean": "shx rm -rf ./dist ./coverage", 26 | "lint": "tslint --project .", 27 | "test": "jest" 28 | }, 29 | "dependencies": { 30 | "axios": "^1.6.2", 31 | "crypto-js": "^4.2.0", 32 | "debug": "^4.3.4", 33 | "uuid": "^8.3.2" 34 | }, 35 | "devDependencies": { 36 | "@types/crypto-js": "^4.2.1", 37 | "@types/debug": "^4.1.12", 38 | "@types/node": "^14.18.63", 39 | "@types/uuid": "^8.3.4", 40 | "axios-mock-adapter": "^1.22.0", 41 | "jest": "^29.7.0", 42 | "pre-commit": "^1.2.2", 43 | "shx": "^0.3.4", 44 | "ts-node": "^10.9.2", 45 | "tslint": "^6.1.3", 46 | "tslint-config-airbnb": "^5.11.2", 47 | "typescript": "^4.9.5" 48 | }, 49 | "keywords": [ 50 | "aftership" 51 | ], 52 | "license": "MIT", 53 | "pre-commit": [ 54 | "lint" 55 | ], 56 | "private": false 57 | } 58 | -------------------------------------------------------------------------------- /scripts/ci/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | npm test -------------------------------------------------------------------------------- /scripts/ci/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | npm install > /dev/null -------------------------------------------------------------------------------- /src/endpoint/courier_endpoint.ts: -------------------------------------------------------------------------------- 1 | import { CourierList } from '../model/courier/courier_list'; 2 | import { CourierDetectRequest } from '../model/courier/courier_detect_request'; 3 | import { CourierDetectList } from '../model/courier/courier_detect_list'; 4 | 5 | /** 6 | * Get a list of AfterShip supported couriers. 7 | */ 8 | export interface CourierEndpoint { 9 | /** 10 | * Return a list of couriers activated at your AfterShip account 11 | */ 12 | listCouriers(): Promise; 13 | 14 | /** 15 | * Return a list of all couriers 16 | */ 17 | listAllCouriers(): Promise; 18 | 19 | /** 20 | * Return a list of matched couriers based on tracking number format and selected couriers or a list of couriers 21 | * @param data data 22 | */ 23 | detectCouriers( 24 | data: CourierDetectRequest, 25 | ): Promise; 26 | } 27 | -------------------------------------------------------------------------------- /src/endpoint/estimated_delivery_date_endpoint.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EstimatedDeliveryDate, 3 | EstimatedDeliveryDateBatchPredictParams, 4 | } from '../model/estimated_delivery_date/estimated_delivery_date_batch_predict_params'; 5 | 6 | export interface EstimatedDeliveryDateEndpoint { 7 | /** 8 | * Batch predict the estimated delivery dates 9 | */ 10 | batchPredict( 11 | data: EstimatedDeliveryDateBatchPredictParams, 12 | ): Promise; 13 | } 14 | -------------------------------------------------------------------------------- /src/endpoint/last_checkpoint_endpoint.ts: -------------------------------------------------------------------------------- 1 | import { LastCheckpoint } from '../model/last_checkpoint/last_checkpoint'; 2 | import { SingleTrackingParam } from '../model/tracking/single_tracking_param'; 3 | 4 | /** 5 | * Get tracking information of the last checkpoint of a tracking. 6 | */ 7 | export interface LastCheckpointEndpoint { 8 | /** 9 | * Return the tracking information of the last checkpoint of a single tracking. 10 | * @param tracking_param The param to identify the single tracking. 11 | * Either id or (slug + tracking_number) should be specified. 12 | * @param fields Optional, List of fields to include in the response. Use comma for multiple values. 13 | * Fields to include: 14 | * slug, created_at, checkpoint_time, city, coordinates, country_iso3, country_name, message, state, tag, zip 15 | * Default: none, Example: city,tag 16 | * @param lang Optional, Support Chinese to English translation for china-ems and china-post only. (Example: en) 17 | */ 18 | getLastCheckpoint( 19 | tracking_param: SingleTrackingParam, 20 | fields?: string, 21 | lang?: string, 22 | ): Promise; 23 | } 24 | -------------------------------------------------------------------------------- /src/endpoint/notification_endpoint.ts: -------------------------------------------------------------------------------- 1 | import { Notification } from '../model/notification/notification'; 2 | import { NotificationRequest } from '../model/notification/notification_request'; 3 | import { SingleTrackingParam } from '../model/tracking/single_tracking_param'; 4 | 5 | /** 6 | * Get, add or remove contacts (sms or email) to be notified when the status of a tracking has changed. 7 | */ 8 | export interface NotificationEndpoint { 9 | /** 10 | * Get contact information for the users to notify when the tracking changes. 11 | * Please note that only customer receivers will be returned. 12 | * Any email, sms or webhook that belongs to the Store will not be returned. 13 | * @param tracking_param The param to identify the single tracking. 14 | * Either id or (slug + tracking_number) should be specified. 15 | */ 16 | getNotification( 17 | tracking_param: SingleTrackingParam, 18 | ): Promise; 19 | 20 | /** 21 | * Add notification receivers to a id (number). 22 | * @param tracking_param The param to identify the single tracking. 23 | * Either id or (slug + tracking_number) should be specified. 24 | * @param notification Notification Request Object 25 | */ 26 | addNotification( 27 | tracking_param: SingleTrackingParam, 28 | notification: NotificationRequest, 29 | ): Promise; 30 | 31 | /** 32 | * Remove notification receivers from a id (number). 33 | * @param tracking_param The param to identify the single tracking. 34 | * Either id or (slug + tracking_number) should be specified. 35 | * @param notification Notification Request Object 36 | */ 37 | removeNotification( 38 | tracking_param: SingleTrackingParam, 39 | notification: NotificationRequest, 40 | ): Promise; 41 | } 42 | -------------------------------------------------------------------------------- /src/endpoint/tracking_endpoint.ts: -------------------------------------------------------------------------------- 1 | import { Tracking } from '../model/tracking/tracking'; 2 | import { TrackingList } from '../model/tracking/tracking_list'; 3 | import { TrackingCreateParams } from '../model/tracking/tracking_create_params'; 4 | import { MultiTrackingsQueryParams } from '../model/tracking/multi_trackings_query_params'; 5 | import { TrackingQueryParams } from '../model/tracking/tracking_query_params'; 6 | import { TrackingUpdateParams } from '../model/tracking/tracking_update_params'; 7 | import { SingleTrackingParam } from '../model/tracking/single_tracking_param'; 8 | import { MarkAsCompletedParam } from '../model/tracking/mark_as_complated_param'; 9 | 10 | /** 11 | * Create trackings, update trackings, and get tracking results. 12 | */ 13 | export interface TrackingEndpoint { 14 | /** 15 | * Create a tracking. 16 | * @param tracking_post_params Tracking post Request Object 17 | */ 18 | createTracking( 19 | tracking_post_params: TrackingCreateParams, 20 | ): Promise; 21 | 22 | /** 23 | * Delete a tracking. 24 | * @param single_tracking_param The param to identify the single tracking. 25 | */ 26 | deleteTracking( 27 | single_tracking_param: SingleTrackingParam, 28 | ): Promise; 29 | 30 | /** 31 | * Get tracking results of multiple trackings. 32 | * trackings_query_params to include: page,limit,keyword,slug,delivery_time,origin 33 | * ,destination,tag,created_at_min,created_at_max,fields,lang 34 | * @param trackings_query_params Tracking list query params object 35 | */ 36 | listTrackings( 37 | trackings_query_params?: MultiTrackingsQueryParams, 38 | ): Promise; 39 | 40 | /** 41 | * Get tracking results of a single tracking. 42 | * tracking_query_params to include: fields, lang 43 | * @param single_tracking_param The param to identify the single tracking. 44 | * @param tracking_query_params Tracking query params object 45 | */ 46 | getTracking( 47 | single_tracking_param: SingleTrackingParam, 48 | tracking_query_params?: TrackingQueryParams, 49 | ): Promise; 50 | 51 | /** 52 | * Update a tracking. 53 | * tracking_update_params to include: smses, emails,title,customer_name,order_id, 54 | * order_id_path,order_number,order_date,custom_fields,note,language, 55 | * order_promised_delivery_date,delivery_type,pickup_location,pickup_note 56 | * @param single_tracking_param The param to identify the single tracking. 57 | * @param tracking_update_params Tracking update params object 58 | */ 59 | updateTracking( 60 | single_tracking_param: SingleTrackingParam, 61 | tracking_update_params?: TrackingUpdateParams, 62 | ): Promise; 63 | 64 | /** 65 | * Retrack an expired tracking. Max 3 times per tracking. 66 | * @param single_tracking_param The param to identify the single tracking. 67 | */ 68 | retrack( 69 | single_tracking_param: SingleTrackingParam, 70 | ): Promise; 71 | 72 | /** 73 | * Mark a tracking as completed. The tracking won't auto update until retrack it. 74 | * @param single_tracking_param The param to identify the single tracking. 75 | * @param reason_param The param to mark tracking as complete. 76 | */ 77 | markAsCompleted( 78 | single_tracking_param: SingleTrackingParam, 79 | reason_param: MarkAsCompletedParam, 80 | ): Promise; 81 | } 82 | -------------------------------------------------------------------------------- /src/error/error.ts: -------------------------------------------------------------------------------- 1 | export class AftershipError extends Error { 2 | public type: string | undefined; 3 | public code: number | null; 4 | public data: any; 5 | public responseBody: string; 6 | 7 | constructor(type?: string, message?: string) { 8 | super(message); 9 | this.type = type; 10 | this.code = null; 11 | this.data = {}; 12 | this.responseBody = ''; 13 | } 14 | 15 | /** 16 | * Static Method for getting SDK error 17 | * @param error error 18 | * @param errorData The object trigger the error 19 | */ 20 | public static getSdkError( 21 | error: AftershipError, 22 | errorData: any, 23 | ): AftershipError { 24 | error.data = errorData; 25 | Error.captureStackTrace(error); 26 | 27 | return error; 28 | } 29 | 30 | /** 31 | * Static Method for getting REQUEST error 32 | * @param error error 33 | * @param error_data The object trigger the error 34 | */ 35 | public static getRequestError(request_error: any, error_data: any): AftershipError { 36 | const error = new AftershipError( 37 | request_error.errno, 38 | request_error.message, 39 | ); 40 | error.data = error_data; 41 | error.code = request_error.code; 42 | 43 | return error; 44 | } 45 | 46 | /** 47 | * Static Method for getting API error 48 | * @param responseBody response-body 49 | */ 50 | public static getApiError(responseBody: any): AftershipError { 51 | const error = new AftershipError(); 52 | if (responseBody === null || responseBody === undefined) { 53 | // Can't get the response body, set 500 error by default 54 | error.type = 'InternalError'; 55 | error.code = 500; 56 | return error; 57 | } 58 | 59 | if (responseBody.meta !== null && responseBody.meta !== undefined) { 60 | error.type = responseBody.meta.type; 61 | error.message = responseBody.meta.message; 62 | error.code = responseBody.meta.code; 63 | } 64 | 65 | error.data = responseBody.data; 66 | error.responseBody = JSON.stringify(responseBody); 67 | 68 | return error; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/error/error_enum.ts: -------------------------------------------------------------------------------- 1 | import { AftershipError } from './error'; 2 | 3 | export enum ErrorType { 4 | ConstructorError = 'ConstructorError', 5 | HandlerError = 'HandlerError', 6 | InternalError = 'InternalError', 7 | } 8 | 9 | export class ErrorEnum { 10 | public static constructorInvalidApiKey = new AftershipError( 11 | ErrorType.ConstructorError, 12 | 'ConstructorError: Invalid API key', 13 | ); 14 | 15 | public static constructorInvalidOptions = new AftershipError( 16 | ErrorType.ConstructorError, 17 | 'ConstructorError: Invalid Options value', 18 | ); 19 | 20 | public static constructorInvalidEndpoint = new AftershipError( 21 | ErrorType.ConstructorError, 22 | 'ConstructorError: Invalid Endpoint value', 23 | ); 24 | 25 | public static constructorInvalidProxy = new AftershipError( 26 | ErrorType.ConstructorError, 27 | 'ConstructorError: Invalid Proxy value', 28 | ); 29 | 30 | public static constructorInvalidTrackingNumber = new AftershipError( 31 | ErrorType.ConstructorError, 32 | 'ConstructorError: tracking_number', 33 | ); 34 | 35 | public static handlerInvalidBody = new AftershipError( 36 | ErrorType.HandlerError, 37 | 'HandlerError: Invalid Body value', 38 | ); 39 | 40 | public static handlerInvalidQuery = new AftershipError( 41 | ErrorType.HandlerError, 42 | 'HandlerError: Invalid Query value', 43 | ); 44 | 45 | public static handlerInvalidRaw = new AftershipError( 46 | ErrorType.HandlerError, 47 | 'HandlerError: Invalid Raw value', 48 | ); 49 | 50 | public static handlerInvalidApiKey = new AftershipError( 51 | ErrorType.HandlerError, 52 | 'HandlerError: Invalid API key', 53 | ); 54 | 55 | public static handlerInvalidTimeout = new AftershipError( 56 | ErrorType.HandlerError, 57 | 'HandlerError: Invalid Timeout', 58 | ); 59 | 60 | public static handlerInvalidBothTrackingIdAndNumber = new AftershipError( 61 | ErrorType.HandlerError, 62 | 'HandlerError: Cannot specify id and tracking number at the same time', 63 | ); 64 | 65 | public static handlerInvalidEmptyTrackingId = new AftershipError( 66 | ErrorType.HandlerError, 67 | 'HandlerError: You must specify the id ', 68 | ); 69 | 70 | public static handlerInvalidEmptyTrackingIdAndNumber = new AftershipError( 71 | ErrorType.HandlerError, 72 | 'HandlerError: You must specify the id or slug and tracking number', 73 | ); 74 | 75 | public static handlerInvalidEmptySlugOrTrackNumber = new AftershipError( 76 | ErrorType.HandlerError, 77 | 'HandlerError: You must specify both slug and tracking number', 78 | ); 79 | 80 | public static handlerInvalidMarkAsCompletedReason = new AftershipError( 81 | ErrorType.HandlerError, 82 | 'HandlerError: Reason must be one of "DELIVERED", "LOST" or "RETURNED_TO_SENDER"', 83 | ); 84 | 85 | // API InternalError 86 | public static internalError = new AftershipError( 87 | ErrorType.InternalError, 88 | "Something went wrong on AfterShip's end.", 89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /src/implementation/courier.ts: -------------------------------------------------------------------------------- 1 | import { ApiRequest } from '../lib/api_request'; 2 | import { CourierEndpoint } from '../endpoint/courier_endpoint'; 3 | import { CourierList } from '../model/courier/courier_list'; 4 | import { CourierDetectRequest } from '../model/courier/courier_detect_request'; 5 | import { CourierDetectList } from '../model/courier/courier_detect_list'; 6 | 7 | /** 8 | * The implementation of the courier endpoint request 9 | */ 10 | export class CourierImplementation implements CourierEndpoint { 11 | private readonly request: ApiRequest; 12 | 13 | constructor(request: ApiRequest) { 14 | this.request = request; 15 | } 16 | 17 | /** 18 | * Return a list of couriers activated at your AfterShip account 19 | */ 20 | public listCouriers(): Promise { 21 | return this.request.makeRequest( 22 | { method: 'GET', url: '/couriers' }, 23 | ); 24 | } 25 | 26 | /** 27 | * Return a list of all couriers 28 | */ 29 | public listAllCouriers(): Promise { 30 | return this.request.makeRequest( 31 | { method: 'GET', url: '/couriers/all' }, 32 | ); 33 | } 34 | 35 | /** 36 | * Return a list of matched couriers based on tracking number format and selected couriers or a list of couriers 37 | * @param data data 38 | */ 39 | public detectCouriers(data: CourierDetectRequest): Promise { 40 | return this.request.makeRequest( 41 | { method: 'POST', url: '/couriers/detect' }, 42 | data, 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/implementation/estimated_delivery_date.ts: -------------------------------------------------------------------------------- 1 | import { ApiRequest } from '../lib/api_request'; 2 | import { EstimatedDeliveryDateEndpoint } from '../endpoint/estimated_delivery_date_endpoint'; 3 | import { 4 | EstimatedDeliveryDate, 5 | EstimatedDeliveryDateBatchPredictParams, 6 | } from '../model/estimated_delivery_date/estimated_delivery_date_batch_predict_params'; 7 | 8 | export class EstimatedDeliveryDateImplementation implements EstimatedDeliveryDateEndpoint { 9 | private readonly request: ApiRequest; 10 | 11 | constructor(request: ApiRequest) { 12 | this.request = request; 13 | } 14 | 15 | /** 16 | * Batch predict the estimated delivery dates 17 | */ 18 | public batchPredict( 19 | data: EstimatedDeliveryDateBatchPredictParams, 20 | ): Promise { 21 | 22 | // make request 23 | return this.request.makeRequest( 24 | { 25 | method: 'POST', 26 | url: '/estimated-delivery-date/predict-batch', 27 | }, 28 | data, 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/implementation/last_checkpoint.ts: -------------------------------------------------------------------------------- 1 | import { ApiRequest } from '../lib/api_request'; 2 | import { LastCheckpointEndpoint } from '../endpoint/last_checkpoint_endpoint'; 3 | import { SingleTrackingParam } from '../model/tracking/single_tracking_param'; 4 | import { LastCheckpoint } from '../model/last_checkpoint/last_checkpoint'; 5 | import { buildTrackingUrl, isStringValid, combineUrlQuery } from '../lib/util'; 6 | 7 | export class LastCheckpointImplementation implements LastCheckpointEndpoint { 8 | private readonly request: ApiRequest; 9 | 10 | constructor(request: ApiRequest) { 11 | this.request = request; 12 | } 13 | 14 | /** 15 | * Return the tracking information of the last checkpoint of a single tracking. 16 | * @param tracking_param The param to identify the single tracking. 17 | * Either id or (slug + tracking_number) should be specified. 18 | * @param fields Optional, List of fields to include in the response. Use comma for multiple values. 19 | * Fields to include: 20 | * slug, created_at, checkpoint_time, city, coordinates, country_iso3, country_name, message, state, tag, zip 21 | * Default: none, Example: city,tag 22 | * @param lang Optional, Support Chinese to English translation for china-ems and china-post only. (Example: en) 23 | */ 24 | public getLastCheckpoint( 25 | tracking_param: SingleTrackingParam, 26 | fields?: string, 27 | lang?: string, 28 | ): Promise { 29 | try { 30 | let trackingUrl = buildTrackingUrl(tracking_param); 31 | // Add optional params to tracking url 32 | const optionalParams = []; 33 | if (isStringValid(fields)) { 34 | optionalParams.push(`fields=${fields}`); 35 | } 36 | 37 | if (isStringValid(lang)) { 38 | optionalParams.push(`lang=${lang}`); 39 | } 40 | 41 | if (optionalParams.length > 0) { 42 | trackingUrl = combineUrlQuery(trackingUrl, optionalParams.join('&')); 43 | } 44 | 45 | // make request 46 | return this.request.makeRequest({ 47 | method: 'GET', 48 | url: `/last_checkpoint/${trackingUrl}`, 49 | }); 50 | } catch (e) { 51 | return Promise.reject(e); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/implementation/notification.ts: -------------------------------------------------------------------------------- 1 | import { ApiRequest } from '../lib/api_request'; 2 | import { NotificationEndpoint } from '../endpoint/notification_endpoint'; 3 | import { Notification } from '../model/notification/notification'; 4 | import { NotificationRequest } from '../model/notification/notification_request'; 5 | import { buildTrackingUrl } from '../lib/util'; 6 | import { SingleTrackingParam } from '../model/tracking/single_tracking_param'; 7 | 8 | export class NotificationImplementation implements NotificationEndpoint { 9 | private readonly request: ApiRequest; 10 | 11 | constructor(request: ApiRequest) { 12 | this.request = request; 13 | } 14 | 15 | /** 16 | * Get contact information for the users to notify when the tracking changes. 17 | * Please note that only customer receivers will be returned. 18 | * Any email, sms or webhook that belongs to the Store will not be returned. 19 | * @param tracking_param The param to identify the single tracking. 20 | * Either id or (slug + tracking_number) should be specified. 21 | */ 22 | public getNotification( 23 | tracking_param: SingleTrackingParam, 24 | ): Promise { 25 | try { 26 | const trackingUrl = buildTrackingUrl(tracking_param); 27 | return this.request.makeRequest({ 28 | method: 'GET', 29 | url: `/notifications/${trackingUrl}`, 30 | }); 31 | } catch (e) { 32 | return Promise.reject(e); 33 | } 34 | } 35 | 36 | /** 37 | * Add notification receivers to a tracking id (number). 38 | * @param tracking_param The param to identify the single tracking. 39 | * Either id or (slug + tracking_number) should be specified. 40 | * @param notification Notification Request Object 41 | */ 42 | public addNotification( 43 | tracking_param: SingleTrackingParam, 44 | notification: NotificationRequest, 45 | ): Promise { 46 | try { 47 | const trackingUrl = buildTrackingUrl(tracking_param, 'add'); 48 | return this.request.makeRequest( 49 | { method: 'POST', url: `/notifications/${trackingUrl}` }, 50 | notification, 51 | ); 52 | } catch (e) { 53 | return Promise.reject(e); 54 | } 55 | } 56 | 57 | /** 58 | * Remove notification receivers from a tracking id (number). 59 | * @param tracking_param The param to identify the single tracking. 60 | * Either id or (slug + tracking_number) should be specified. 61 | * @param notification Notification Request Object 62 | */ 63 | public removeNotification( 64 | tracking_param: SingleTrackingParam, 65 | notification: NotificationRequest, 66 | ): Promise { 67 | try { 68 | const trackingUrl = buildTrackingUrl(tracking_param, 'remove'); 69 | return this.request.makeRequest( 70 | { 71 | method: 'POST', 72 | url: `/notifications/${trackingUrl}`, 73 | }, 74 | notification, 75 | ); 76 | } catch (e) { 77 | return Promise.reject(e); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/implementation/tracking.ts: -------------------------------------------------------------------------------- 1 | import { ApiRequest } from '../lib/api_request'; 2 | import { TrackingEndpoint } from '../endpoint/tracking_endpoint'; 3 | import { SingleTrackingParam } from '../model/tracking/single_tracking_param'; 4 | import { Tracking } from '../model/tracking/tracking'; 5 | import { TrackingList } from '../model/tracking/tracking_list'; 6 | import { TrackingCreateParams } from '../model/tracking/tracking_create_params'; 7 | import { MultiTrackingsQueryParams } from '../model/tracking/multi_trackings_query_params'; 8 | import { TrackingQueryParams } from '../model/tracking/tracking_query_params'; 9 | import { TrackingUpdateParams } from '../model/tracking/tracking_update_params'; 10 | import { MarkAsCompletedParam } from '../model/tracking/mark_as_complated_param'; 11 | import { buildTrackingUrl, getQueryString, combineUrlQuery } from '../lib/util'; 12 | import { AftershipError } from '../error/error'; 13 | import { ErrorEnum } from '../error/error_enum'; 14 | 15 | export class TrackingImplementation implements TrackingEndpoint { 16 | private readonly request: ApiRequest; 17 | 18 | constructor(request: ApiRequest) { 19 | this.request = request; 20 | } 21 | 22 | /** 23 | * Create a tracking. 24 | * @param data Tracking post Request Object 25 | */ 26 | public createTracking( 27 | data: TrackingCreateParams, 28 | ): Promise { 29 | 30 | // make request 31 | return this.request.makeRequest( 32 | { method: 'POST', url: '/trackings' }, 33 | data, 34 | ); 35 | } 36 | 37 | /** 38 | * Delete a tracking. 39 | * @param single_tracking_param The param to identify the single tracking. 40 | */ 41 | public deleteTracking( 42 | single_tracking_param: SingleTrackingParam, 43 | ): Promise { 44 | const trackingUrl = buildTrackingUrl(single_tracking_param); 45 | // make request 46 | return this.request.makeRequest( 47 | { method: 'DELETE', url: `/trackings/${trackingUrl}` }, 48 | ); 49 | } 50 | 51 | /** 52 | * Get tracking results of multiple trackings. 53 | * trackings_query_params to include: page,limit,keyword,slug,delivery_time,origin 54 | * ,destination,tag,created_at_min,created_at_max,fields,lang 55 | * @param trackings_query_params Tracking list query params object 56 | */ 57 | public listTrackings( 58 | trackings_query_params?: MultiTrackingsQueryParams, 59 | ): Promise { 60 | 61 | const queryString = getQueryString(trackings_query_params); 62 | const trackingUrl = queryString === '' ? '/trackings' : `/trackings?${queryString}`; 63 | 64 | // make request 65 | return this.request.makeRequest( 66 | { method: 'GET', url: trackingUrl }, 67 | ); 68 | } 69 | 70 | /** 71 | * Get tracking results of a single tracking. 72 | * tracking_query_params to include: fields, lang 73 | * @param single_tracking_param The param to identify the single tracking. 74 | * @param tracking_query_params Tracking query params object 75 | */ 76 | public getTracking( 77 | single_tracking_param: SingleTrackingParam, 78 | tracking_query_params?: TrackingQueryParams, 79 | ): Promise { 80 | 81 | let trackingUrl = `/trackings/${buildTrackingUrl(single_tracking_param)}`; 82 | 83 | const queryString = getQueryString(tracking_query_params); 84 | trackingUrl = combineUrlQuery(trackingUrl, queryString); 85 | 86 | // make request 87 | return this.request.makeRequest( 88 | { method: 'GET', url: trackingUrl }, 89 | ); 90 | } 91 | 92 | /** 93 | * Update a tracking. 94 | * tracking_update_params to include: smses, emails,title,customer_name,order_id, 95 | * order_id_path,order_number,order_date,custom_fields,note,language, 96 | * order_promised_delivery_date,delivery_type,pickup_location,pickup_note 97 | * @param single_tracking_param The param to identify the single tracking. 98 | * @param tracking_update_params Tracking update params object 99 | */ 100 | public updateTracking( 101 | single_tracking_param: SingleTrackingParam, 102 | data?: TrackingUpdateParams, 103 | ): Promise { 104 | const trackingUrl = `/trackings/${buildTrackingUrl(single_tracking_param)}`; 105 | 106 | // make request 107 | return this.request.makeRequest( 108 | { method: 'PUT', url: trackingUrl }, 109 | data, 110 | ); 111 | } 112 | 113 | /** 114 | * Retrack an expired tracking. Max 3 times per tracking. 115 | * @param single_tracking_param The param to identify the single tracking. 116 | */ 117 | public retrack( 118 | single_tracking_param: SingleTrackingParam, 119 | ): Promise { 120 | const trackingUrl = `/trackings/${buildTrackingUrl(single_tracking_param, 'retrack')}`; 121 | 122 | // make request 123 | return this.request.makeRequest( 124 | { method: 'POST', url: trackingUrl }, 125 | ); 126 | } 127 | 128 | /** 129 | * Mark a tracking as completed. The tracking won't auto update until retrack it. 130 | * @param single_tracking_param The param to identify the single tracking. 131 | * @param reason_param The param to mark tracking as complete. 132 | */ 133 | public markAsCompleted( 134 | single_tracking_param: SingleTrackingParam, 135 | reason_param: MarkAsCompletedParam, 136 | ): Promise { 137 | const trackingUrl = `/trackings/${buildTrackingUrl(single_tracking_param, 'mark-as-completed')}`; 138 | 139 | if (reason_param === undefined || (reason_param.reason !== 'DELIVERED' 140 | && reason_param.reason !== 'LOST' && reason_param.reason !== 'RETURNED_TO_SENDER')) { 141 | throw AftershipError.getSdkError( 142 | ErrorEnum.handlerInvalidMarkAsCompletedReason, 143 | reason_param, 144 | ); 145 | } 146 | 147 | // make request 148 | return this.request.makeRequest( 149 | { method: 'POST', url: trackingUrl }, 150 | reason_param, 151 | ); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { ApiRequestImplementation } from './lib/api_request'; 2 | import { AftershipError } from './error/error'; 3 | import { ErrorEnum } from './error/error_enum'; 4 | import { AftershipOption } from './model/aftership_option'; 5 | import { RateLimit } from './model/rate_limit'; 6 | import { isStringValid } from './lib/util'; 7 | import { CourierEndpoint } from './endpoint/courier_endpoint'; 8 | import { LastCheckpointEndpoint } from './endpoint/last_checkpoint_endpoint'; 9 | import { NotificationEndpoint } from './endpoint/notification_endpoint'; 10 | import { TrackingEndpoint } from './endpoint/tracking_endpoint'; 11 | import { EstimatedDeliveryDateEndpoint } from './endpoint/estimated_delivery_date_endpoint'; 12 | import { CourierImplementation } from './implementation/courier'; 13 | import { LastCheckpointImplementation } from './implementation/last_checkpoint'; 14 | import { NotificationImplementation } from './implementation/notification'; 15 | import { TrackingImplementation } from './implementation/tracking'; 16 | import { EstimatedDeliveryDateImplementation } from './implementation/estimated_delivery_date'; 17 | import { AuthType } from './lib/auth_enum'; 18 | 19 | const DEFAULT_ENDPOINT = 'https://api.aftership.com/tracking/2023-10'; 20 | const DEFAULT_USER_AGENT = 'aftership-sdk-nodejs'; 21 | 22 | export class AfterShip { 23 | public readonly authType: number; 24 | public readonly apiKey: string; 25 | public readonly apiSecret: string; 26 | public readonly endpoint: string; 27 | public readonly user_agent_prefix: string; 28 | 29 | /** 30 | * The recent rate limit after making an API call 31 | */ 32 | public rate_limit: RateLimit; 33 | 34 | /** 35 | * Courier endpoint 36 | */ 37 | public readonly courier: CourierEndpoint; 38 | 39 | /** 40 | * Last Checkpoint endpoint 41 | */ 42 | public readonly last_checkpoint: LastCheckpointEndpoint; 43 | 44 | /** 45 | * Notification endpoint 46 | */ 47 | public readonly notification: NotificationEndpoint; 48 | 49 | /** 50 | * Tracking endpoint 51 | */ 52 | public readonly tracking: TrackingEndpoint; 53 | 54 | /** 55 | * EstimatedDeliveryDate endpoint 56 | */ 57 | public readonly estimated_delivery_date: EstimatedDeliveryDateEndpoint; 58 | 59 | constructor(apiKey: string, options?: AftershipOption) { 60 | this.errorHandling(apiKey, options); 61 | this.apiKey = apiKey; 62 | 63 | // Setup 64 | if (options !== null && options !== undefined) { 65 | this.endpoint = isStringValid(options.endpoint) 66 | ? options.endpoint 67 | : DEFAULT_ENDPOINT; 68 | this.user_agent_prefix = isStringValid(options.user_agent_prefix) 69 | ? options.user_agent_prefix 70 | : DEFAULT_USER_AGENT; 71 | this.authType = options.auth_type !== undefined 72 | ? options.auth_type 73 | : 0; 74 | this.apiSecret = isStringValid(options.api_secret) 75 | ? options.api_secret 76 | : ''; 77 | } else { 78 | this.authType = AuthType.ApiKey; 79 | this.apiSecret = ''; 80 | this.endpoint = DEFAULT_ENDPOINT; 81 | this.user_agent_prefix = DEFAULT_USER_AGENT; 82 | } 83 | 84 | this.rate_limit = { 85 | reset: null, 86 | limit: null, 87 | remaining: null, 88 | }; 89 | 90 | const request = new ApiRequestImplementation(this); 91 | 92 | // Endpoints 93 | this.courier = new CourierImplementation(request); 94 | this.last_checkpoint = new LastCheckpointImplementation(request); 95 | this.notification = new NotificationImplementation(request); 96 | this.tracking = new TrackingImplementation(request); 97 | this.estimated_delivery_date = new EstimatedDeliveryDateImplementation(request); 98 | } 99 | 100 | /** 101 | * Error Handling function 102 | * Throw error if the input param contain incorrect type 103 | * @param apiKey api key 104 | */ 105 | private errorHandling(apiKey: string, options?: AftershipOption): void { 106 | if (!isStringValid(apiKey)) { 107 | // Verify api_key 108 | throw AftershipError.getSdkError( 109 | ErrorEnum.constructorInvalidApiKey, 110 | apiKey, 111 | ); 112 | } 113 | 114 | if (options !== null && options !== undefined) { 115 | // Verify options 116 | if (typeof options !== 'object') { 117 | throw AftershipError.getSdkError( 118 | ErrorEnum.constructorInvalidOptions, 119 | options, 120 | ); 121 | } 122 | 123 | // Verify options value 124 | if ( 125 | options.endpoint !== null && 126 | options.endpoint !== undefined && 127 | typeof options.endpoint !== 'string' 128 | ) { 129 | // Verify endpoint 130 | throw AftershipError.getSdkError( 131 | ErrorEnum.constructorInvalidEndpoint, 132 | options.endpoint, 133 | ); 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/lib/api_request.ts: -------------------------------------------------------------------------------- 1 | import axios, { Method } from 'axios'; 2 | import debug from 'debug'; 3 | import { v4 as uuidv4 } from 'uuid'; 4 | import { AfterShip } from '../index'; 5 | import { VERSION } from './version'; 6 | import { AftershipError } from '../error/error'; 7 | import { ApiSignature, ApiSignatureImplement } from './signature'; 8 | import { AuthType } from './auth_enum'; 9 | 10 | const debugMakeRequest = debug('aftership:makeRequest'); 11 | const debugProcessResponse = debug('aftership:processResponse'); 12 | const debugProcessException = debug('aftership:processException'); 13 | const debugRateLimiting = debug('aftership:setRateLimiting'); 14 | 15 | const TIMEOUT = 50000; 16 | 17 | interface RequestConfig { 18 | method: Method; 19 | url: string; 20 | } 21 | 22 | /** 23 | * API request interface 24 | */ 25 | export interface ApiRequest { 26 | /** 27 | * Make the request to AfterShip API 28 | * @param config the config of request (f.e. url, method) 29 | * @param data data 30 | */ 31 | makeRequest( 32 | { url, method }: RequestConfig, 33 | data?: T, 34 | ): Promise; 35 | } 36 | 37 | /** 38 | * The implementation of API request 39 | */ 40 | export class ApiRequestImplementation implements ApiRequest { 41 | private readonly app: AfterShip; 42 | private readonly signer: ApiSignature; 43 | 44 | constructor(app: AfterShip) { 45 | this.app = app; 46 | this.signer = new ApiSignatureImplement(app.apiSecret); 47 | } 48 | 49 | /** 50 | * Make a request call to AfterShip API 51 | * @param config the config of request (f.e. url, method) 52 | * @param data data 53 | */ 54 | public makeRequest( 55 | { url, method }: RequestConfig, 56 | data?: T, 57 | ): Promise { 58 | debugMakeRequest('config %o', { 59 | url, 60 | method, 61 | apiKey: this.app.apiKey, 62 | }); 63 | 64 | const request_id = uuidv4(); 65 | const headers: any = { 66 | 'as-api-key': this.app.apiKey, 67 | 'Content-Type': 'application/json', 68 | 'request-id': request_id, 69 | 'aftership-agent': `nodejs-sdk-${VERSION}`, 70 | }; 71 | 72 | // Only set User-Agent header in Node 73 | if (typeof window === 'undefined') { 74 | headers['User-Agent'] = `${this.app.user_agent_prefix}/${VERSION}`; 75 | } 76 | 77 | const date = new Date().toUTCString(); 78 | 79 | if (this.app.authType === AuthType.Aes) { 80 | headers['as-signature-hmac-sha256'] = this.signer.sign( 81 | this.app.endpoint + url, date, method, JSON.stringify(data), 82 | 'application/json', headers); 83 | } 84 | 85 | headers['date'] = date; 86 | 87 | const request = axios.request({ 88 | url, 89 | method, 90 | headers, 91 | baseURL: this.app.endpoint, 92 | data: data !== undefined ? { ...data } : undefined, 93 | timeout: TIMEOUT, 94 | }); 95 | 96 | // return Promise 97 | return new Promise((resolve, reject) => { 98 | request 99 | .then(({ headers, data }) => { 100 | this.setRateLimiting(this.app, headers); 101 | resolve(this.processResponse(data)); 102 | }) 103 | .catch(e => reject(this.processException(e))); 104 | }); 105 | } 106 | 107 | private processResponse(data: any): R { 108 | debugProcessResponse('body %o', data); 109 | 110 | // Return data in response 111 | return data['data']; 112 | } 113 | 114 | private processException(error: any): AftershipError { 115 | debugProcessException('UnexpectedError %s', error.message); 116 | if (error.response) { 117 | // The request was made and the server responded with a status code 118 | // that falls out of the range of 2xx 119 | if (error.response.status !== 401) { 120 | // Not UnauthorizedError 121 | // Set rate_limit 122 | this.setRateLimiting(this.app, error.response.headers); 123 | } 124 | return AftershipError.getApiError(error.response.data); 125 | } 126 | 127 | if (error.request) { 128 | // The request was made but no response was received 129 | // `error.request` is an instance of XMLHttpRequest in the browser and an instance of 130 | // http.ClientRequest in node.js 131 | return AftershipError.getRequestError(error, error.config); 132 | } 133 | 134 | // Something happened in setting up the request that triggered an Error 135 | return new AftershipError('Setup Request Error', error.message); 136 | } 137 | 138 | private setRateLimiting(app: AfterShip, data: any): void { 139 | if (!data) { 140 | return; 141 | } 142 | 143 | const rateLimiting = { 144 | reset: data['x-ratelimit-reset'], 145 | limit: data['x-ratelimit-limit'], 146 | remaining: data['x-ratelimit-remaining'], 147 | }; 148 | 149 | debugRateLimiting('rateLimiting %o', rateLimiting); 150 | app.rate_limit = rateLimiting; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/lib/auth_enum.ts: -------------------------------------------------------------------------------- 1 | export enum AuthType { 2 | ApiKey, 3 | Aes, 4 | Rsa, 5 | } 6 | -------------------------------------------------------------------------------- /src/lib/signature.ts: -------------------------------------------------------------------------------- 1 | 2 | import cryptoJs from 'crypto-js'; 3 | 4 | const SEPERATOR = '\n'; 5 | const AFTERSHIP_HEADER_PREFIX = 'as-'; 6 | 7 | export interface ApiSignature { 8 | sign( 9 | url: string, 10 | date: string, 11 | method: string, 12 | body: string, 13 | contentType: string, 14 | headers: any, 15 | ): string; 16 | } 17 | 18 | export class ApiSignatureImplement implements ApiSignature { 19 | private readonly apiSecret: string; 20 | 21 | constructor(apiSecret: string) { 22 | this.apiSecret = apiSecret; 23 | } 24 | 25 | public sign( 26 | url: string, 27 | date: string, 28 | method: string, 29 | body: string, 30 | contentType: string, 31 | headers: any, 32 | ): string { 33 | const canonicalizedHeader = this.canonicalizedHeaders(headers); 34 | const resource = this.canonicalizedResource(url); 35 | const s = this.signString( 36 | method, 37 | body, 38 | contentType, 39 | date, canonicalizedHeader, resource); 40 | return cryptoJs.enc.Base64.stringify(cryptoJs.HmacSHA256(s, this.apiSecret)); 41 | } 42 | 43 | private canonicalizedHeaders(headers: any): string { 44 | if (headers.length === 0) { 45 | return ''; 46 | } 47 | 48 | const keys: string[] = []; 49 | const newHeaders: any = {}; 50 | Object.keys(headers).forEach((key: string) => { 51 | const newKey = key.toLowerCase(); 52 | if (newKey.indexOf(AFTERSHIP_HEADER_PREFIX) === -1) { 53 | return; 54 | } 55 | keys.push(newKey); 56 | newHeaders[newKey] = headers[key].trim(); 57 | }); 58 | keys.sort(); 59 | 60 | const result: string[] = []; 61 | for (let i = 0; i < keys.length; i += 1) { 62 | result.push(`${keys[i]}:${newHeaders[keys[i]]}`); 63 | } 64 | return result.join(SEPERATOR); 65 | } 66 | 67 | private canonicalizedResource(url: string): string { 68 | const u = new URL(url); 69 | const params = u.search.split('?'); 70 | if (params.length < 2) { 71 | return u.pathname; 72 | } 73 | const sortedParams = new URLSearchParams(params[1]); 74 | sortedParams.sort(); 75 | return `${u.pathname}?${sortedParams.toString()}`; 76 | } 77 | 78 | private signString( 79 | method: string, 80 | body: string, 81 | contentType: string, 82 | date: string, 83 | canonicalizedHeader: string, 84 | resource: string, 85 | ): string { 86 | let result: string = method + SEPERATOR; 87 | let newBody = ''; 88 | let newContentType = contentType; 89 | if (body === undefined || body.length === 0) { 90 | newContentType = ''; 91 | } else { 92 | newBody = cryptoJs.MD5(body).toString().toUpperCase(); 93 | } 94 | result += newBody + SEPERATOR; 95 | result += newContentType + SEPERATOR; 96 | result += date + SEPERATOR; 97 | result += canonicalizedHeader + SEPERATOR; 98 | result += resource; 99 | return result; 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/lib/util.ts: -------------------------------------------------------------------------------- 1 | import { AftershipError } from '../error/error'; 2 | import { ErrorEnum } from '../error/error_enum'; 3 | import { SingleTrackingParam } from '../model/tracking/single_tracking_param'; 4 | 5 | /** 6 | * Build tracking url by param 7 | * @param param tracking param 8 | * @param sub_path the sub path behind /:id, f.e. /:id/retrack 9 | */ 10 | export function buildTrackingUrl(param: SingleTrackingParam, sub_path?: string): string { 11 | if (param === undefined) { 12 | throw AftershipError.getSdkError( 13 | ErrorEnum.handlerInvalidEmptyTrackingIdAndNumber, 14 | param, 15 | ); 16 | } 17 | 18 | if ( 19 | isStringValid(param.id) && 20 | (isStringValid(param.slug) || isStringValid(param.tracking_number)) 21 | ) { 22 | throw AftershipError.getSdkError( 23 | ErrorEnum.handlerInvalidBothTrackingIdAndNumber, 24 | param.id, 25 | ); 26 | } else if ( 27 | !isStringValid(param.id) && 28 | !isStringValid(param.slug) && 29 | !isStringValid(param.tracking_number) 30 | ) { 31 | throw AftershipError.getSdkError( 32 | ErrorEnum.handlerInvalidEmptyTrackingIdAndNumber, 33 | param.tracking_number, 34 | ); 35 | } else if ( 36 | !isStringValid(param.id) && 37 | (!isStringValid(param.slug) || !isStringValid(param.tracking_number)) 38 | ) { 39 | throw AftershipError.getSdkError( 40 | ErrorEnum.handlerInvalidEmptySlugOrTrackNumber, 41 | param.tracking_number, 42 | ); 43 | } 44 | 45 | // Build url 46 | let url = ''; 47 | 48 | // id 49 | if (isStringValid(param.id)) { 50 | url = `${param.id}`; 51 | } else { 52 | // slug && tracking_number 53 | 54 | url = `${param.slug}/${param.tracking_number}`; 55 | } 56 | 57 | // Add sub path 58 | if (isStringValid(sub_path)) { 59 | url += `/${sub_path}`; 60 | } 61 | 62 | // Add the additional parameters to query string 63 | if (param.optional_parameters !== undefined) { 64 | const query_string = getQueryString(param.optional_parameters); 65 | if (isStringValid(query_string)) { 66 | url = `${url}?${query_string}`; 67 | } 68 | } 69 | 70 | return url; 71 | } 72 | 73 | /** 74 | * Check if the string value is valid 75 | * @param val string value 76 | */ 77 | export function isStringValid(val: string | undefined): boolean { 78 | return ( 79 | val !== undefined && val !== null && typeof val === 'string' && val !== '' 80 | ); 81 | } 82 | 83 | /** 84 | * Object to query string 85 | * @param data Object 86 | */ 87 | export function getQueryString(data: any | undefined): string { 88 | if (data === undefined) return ''; 89 | 90 | return Object.keys(data) 91 | .map((key) => { 92 | const val = encodeURIComponent(data[key]); 93 | return `${key}=${val}`; 94 | }) 95 | .join('&'); 96 | } 97 | 98 | /** 99 | * Combine the url and query string 100 | * @param url url 101 | * @param query query string 102 | */ 103 | export function combineUrlQuery(url: string, query: string): string { 104 | // When url or query is invalid, don't need to combine the query string 105 | if (!isStringValid(url) || !isStringValid(query)) { 106 | return url; 107 | } 108 | 109 | return `${url}${url.indexOf('?') === 0 ? '?' : '&'}${query}`; 110 | } 111 | -------------------------------------------------------------------------------- /src/lib/version.ts: -------------------------------------------------------------------------------- 1 | export const VERSION = '6.0.0'; 2 | -------------------------------------------------------------------------------- /src/model/aftership_option.ts: -------------------------------------------------------------------------------- 1 | export interface AftershipOption { 2 | /** 3 | * AfterShip endpoint, default 'https://api.aftership.com/v4' 4 | */ 5 | endpoint: string; 6 | 7 | /** 8 | * Prefix of User-Agent in headers, default 'aftership-sdk-nodejs' 9 | */ 10 | user_agent_prefix: string; 11 | 12 | /** 13 | * Authentication type, API key and AES is current implemented. 14 | */ 15 | auth_type: number; 16 | 17 | /** 18 | * AES encrypt secret 19 | */ 20 | api_secret: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/model/aftership_response.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The response of AfterShip API calls 3 | */ 4 | export interface AftershipResponse { 5 | /** 6 | * The meta key is used to communicate extra information about the response to the developer. 7 | */ 8 | meta: Meta; 9 | 10 | /** 11 | * The data key is the meat of the response. It may be a list of results, 12 | * but either way this is where you'll find the data you requested. 13 | */ 14 | data?: T; 15 | } 16 | 17 | export interface Meta { 18 | code: number; 19 | message?: string; 20 | type?: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/model/checkpoint/checkpoint.ts: -------------------------------------------------------------------------------- 1 | import { DeliveryStatus } from '../tracking/delivery_status'; 2 | 3 | /** 4 | * checkpoint information. 5 | */ 6 | export interface Checkpoint { 7 | 8 | /** 9 | * Date and time of the tracking created. 10 | */ 11 | created_at?: string; 12 | 13 | /** 14 | * The unique code of courier for this checkpoint message. 15 | */ 16 | slug?: string; 17 | 18 | /** 19 | * Date and time of the checkpoint, provided by courier. 20 | * Value may be:YYYY-MM-DD, YYYY-MM-DDTHH:MM:SS, or YYYY-MM-DDTHH:MM:SS+TIMEZONE 21 | */ 22 | checkpoint_time?: string; 23 | 24 | /** 25 | * Location info provided by carrier (if any) 26 | */ 27 | location?: string; 28 | 29 | /** 30 | * City info provided by carrier (if any) 31 | */ 32 | city?: string; 33 | 34 | /** 35 | * State info provided by carrier (if any) 36 | */ 37 | state?: string; 38 | 39 | /** 40 | * Deprecated as of March 2013 41 | */ 42 | coordinates?: [string]; 43 | 44 | /** 45 | * Country ISO Alpha-3 (three letters) of the checkpoint 46 | */ 47 | country_iso3?: string; 48 | 49 | /** 50 | * Country name of the checkpoint, may also contain other location info. 51 | */ 52 | country_name?: string; 53 | 54 | /** 55 | * Checkpoint message 56 | */ 57 | message?: string; 58 | 59 | /** 60 | * Current status of checkpoint. 61 | */ 62 | tag?: DeliveryStatus; 63 | 64 | /** 65 | * Current subtag of checkpoint. 66 | */ 67 | subtag?: string; 68 | 69 | /** 70 | * Normalized checkpoint message. 71 | */ 72 | subtag_message?: string; 73 | 74 | /** 75 | * ocation info (if any) 76 | */ 77 | zip?: string; 78 | 79 | /** 80 | * Checkpoint status provided by courier (if any) 81 | */ 82 | raw_tag?: string; 83 | } 84 | -------------------------------------------------------------------------------- /src/model/courier/courier.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Courier Object 3 | */ 4 | export interface Courier { 5 | /** 6 | * Unique code of courier 7 | */ 8 | slug: string; 9 | 10 | /** 11 | * Name of courier 12 | */ 13 | name: string; 14 | 15 | /** 16 | * Contact phone number of courier 17 | */ 18 | phone: string; 19 | 20 | /** 21 | * Other name of courier 22 | */ 23 | other_name: string; 24 | 25 | /** 26 | * Website link of courier 27 | */ 28 | web_url: string; 29 | 30 | /** 31 | * The extra fields need for tracking, such as `tracking_account_number`, `tracking_postal_code`, 32 | * `tracking_ship_date`, `tracking_key`, `tracking_destination_country` 33 | */ 34 | required_fields: string[]; 35 | 36 | /** 37 | * the extra fields which are optional for tracking. Basically it's the same as required_fields, 38 | * but the difference is that only some of the tracking numbers require these fields. 39 | */ 40 | optional_fields: string[]; 41 | 42 | /** 43 | * Default language of tracking results 44 | */ 45 | default_language: string; 46 | 47 | /** 48 | * Other supported languages 49 | */ 50 | support_languages: string[]; 51 | 52 | /** 53 | * Country code (ISO Alpha-3) where the courier provides service 54 | */ 55 | service_from_country_iso3: string[]; 56 | } 57 | -------------------------------------------------------------------------------- /src/model/courier/courier_detect_list.ts: -------------------------------------------------------------------------------- 1 | import { Courier } from './courier'; 2 | 3 | /** 4 | * The response of couriers detect request 5 | */ 6 | export interface CourierDetectList { 7 | /** 8 | * Total number of matched couriers 9 | */ 10 | total: number; 11 | 12 | /** 13 | * A list of matched couriers based on tracking number format. 14 | */ 15 | couriers: Courier[]; 16 | } 17 | -------------------------------------------------------------------------------- /src/model/courier/courier_detect_request.ts: -------------------------------------------------------------------------------- 1 | import { AftershipError } from '../../error/error'; 2 | import { ErrorEnum } from '../../error/error_enum'; 3 | 4 | /** 5 | * The request object of couriers detect 6 | */ 7 | export class CourierDetectRequest { 8 | /** 9 | * Tracking Object. 10 | */ 11 | public tracking: CourierDetectTracking; 12 | 13 | /** 14 | * CourierDetectRequest constructor 15 | * @param tracking tracking object, the tracking_number field is required. 16 | */ 17 | constructor(tracking: CourierDetectTracking) { 18 | if (tracking === undefined || tracking.tracking_number === undefined 19 | || tracking.tracking_number === '') { 20 | // Verify tracking_number 21 | throw AftershipError.getSdkError( 22 | ErrorEnum.constructorInvalidTrackingNumber, 23 | tracking, 24 | ); 25 | } 26 | 27 | this.tracking = tracking; 28 | } 29 | } 30 | 31 | /** 32 | * The tracking object in couriers detect request 33 | */ 34 | export interface CourierDetectTracking { 35 | /** 36 | * Tracking number. (Required) 37 | */ 38 | tracking_number: string; 39 | 40 | /** 41 | * The postal code of receiver's address. Required by some couriers, such asdeutsch-post 42 | */ 43 | tracking_postal_code?: string; 44 | 45 | /** 46 | * Shipping date in YYYYMMDD format. Required by some couriers, such asdeutsch-post 47 | */ 48 | tracking_ship_date?: string; 49 | 50 | /** 51 | * Key of the shipment for a specific courier. Required by some couriers, such assic-teliway 52 | */ 53 | tracking_key?: string; 54 | 55 | /** 56 | * Destination Country of the shipment for a specific courier. Required by some couriers, such aspostnl-3s 57 | */ 58 | tracking_destination_country?: string; 59 | 60 | /** 61 | * Account number of the shipper for a specific courier. Required by some couriers. 62 | * Refer to this page for more details 63 | */ 64 | tracking_account_number?: string; 65 | 66 | /** 67 | * Origin Country/Region of the shipment for a specific courier. Required by some couriers 68 | */ 69 | tracking_origin_country?: string; 70 | 71 | /** 72 | * State of the destination shipping address of the shipment. Required by some couriers 73 | */ 74 | tracking_state?: string; 75 | 76 | /** 77 | * Slug group is a group of slugs which belong to same courier. 78 | * For example, when you inpit "fedex-group" as slug_group, 79 | * AfterShip will detect the tracking with "fedex-uk", "fedex-fims", 80 | * and other slugs which belong to "fedex". 81 | * It cannot be used with slug at the same time 82 | */ 83 | slug_group?: string; 84 | 85 | /** 86 | * Enter ISO Alpha-3 (three letters) to specify the origin of the shipment (e.g. USA for United States) 87 | */ 88 | origin_country_iso3?: string; 89 | 90 | /** 91 | * Enter ISO Alpha-3 (three letters) to specify the destination of the shipment (e.g. USA for United States) 92 | */ 93 | destination_country_iso3?: string; 94 | 95 | /** 96 | * If not specified, Aftership will automatically detect the courier based on the tracking number format 97 | * and your selected couriers. Use array or comma separated to input a list of couriers for auto detect. 98 | */ 99 | slug?: string | string[]; 100 | } 101 | -------------------------------------------------------------------------------- /src/model/courier/courier_list.ts: -------------------------------------------------------------------------------- 1 | import { Courier } from './courier'; 2 | 3 | /** 4 | * The response of courier list 5 | */ 6 | export interface CourierList { 7 | /** 8 | * Total number of couriers supported by AfterShip. 9 | */ 10 | total: number; 11 | 12 | /** 13 | * Array of Hash describes the couriers information. 14 | */ 15 | couriers: Courier[]; 16 | } 17 | -------------------------------------------------------------------------------- /src/model/estimated_delivery_date/address.ts: -------------------------------------------------------------------------------- 1 | export interface Address { 2 | /** 3 | * The country/region of the origin location from where the package is 4 | * picked up by the carrier to be delivered to the final destination. 5 | * Use 3 letters of ISO 3166-1 country/region code. 6 | */ 7 | country?: string; 8 | 9 | /** 10 | * State, province, or the equivalent location of the origin address. 11 | * Either `origin_address.state` or `origin_address.postal_code` is required. 12 | */ 13 | state?: string; 14 | 15 | /** 16 | * City of the origin address. 17 | */ 18 | city?: string; 19 | 20 | /** 21 | * Postal code of the origin address. 22 | * Either `origin_address.state` or `origin_address.postal_code` is required. 23 | */ 24 | postal_code?: string; 25 | 26 | /** 27 | * Raw location of the origin address. 28 | * A raw address will help AI to identify the accurate location of the origin address. 29 | */ 30 | raw_location?: string; 31 | } 32 | -------------------------------------------------------------------------------- /src/model/estimated_delivery_date/estimated_delivery_date.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Estimated Delivery Date information. 3 | */ 4 | export interface EstimatedDeliveryDate { 5 | 6 | /** 7 | * The estimated arrival date of the shipment. 8 | */ 9 | estimated_delivery_date?: string; 10 | 11 | /** 12 | * The reliability of the estimated delivery date based on the trend of the transit time 13 | * for the similar delivery route and the carrier's delivery performance 14 | * range from 0.0 to 1.0 (Beta feature). 15 | */ 16 | confidence_score?: number; 17 | 18 | /** 19 | * Earliest estimated delivery date of the shipment. 20 | */ 21 | estimated_delivery_date_min?: string; 22 | 23 | /** 24 | * Latest estimated delivery date of the shipment. 25 | */ 26 | estimated_delivery_date_max?: string; 27 | } 28 | -------------------------------------------------------------------------------- /src/model/estimated_delivery_date/estimated_delivery_date_batch_predict_params.ts: -------------------------------------------------------------------------------- 1 | import { Address } from './address'; 2 | import { Weight } from './weight'; 3 | import { EstimatedPickup } from './estimated_pickup'; 4 | 5 | /** 6 | * The request object of batch predict 7 | */ 8 | export class EstimatedDeliveryDateBatchPredictParams { 9 | /** 10 | * EstimatedDeliveryDate Array Object. 11 | */ 12 | public estimated_delivery_dates: EstimatedDeliveryDate[]; 13 | 14 | /** 15 | * EstimatedDeliveryDateBatchPredictParams constructor 16 | * @param estimatedDeliveryDates EstimatedDeliveryDate array object 17 | */ 18 | constructor(estimatedDeliveryDates: EstimatedDeliveryDate[]) { 19 | this.estimated_delivery_dates = estimatedDeliveryDates; 20 | } 21 | } 22 | 23 | /** 24 | * The estimatedDeliveryDate object for batch predict request 25 | */ 26 | export interface EstimatedDeliveryDate { 27 | /** 28 | * AfterShip's unique code of courier. 29 | * Please refer to https://track.aftership.com/couriers/download. 30 | */ 31 | slug?: string; 32 | 33 | /** 34 | * Shipping and delivery options provided by the carrier. 35 | */ 36 | service_type_name?: string; 37 | 38 | /** 39 | * The location from where the package is picked up by the carrier to be delivered to the final destination. 40 | */ 41 | origin_address?: Address; 42 | 43 | /** 44 | * The final destination of the customer where the delivery will be made. 45 | */ 46 | destination_address?: Address; 47 | 48 | /** 49 | * AfterShip uses this object to calculate the total weight of the order. 50 | */ 51 | weight?: Weight; 52 | 53 | /** 54 | * The number of packages. 55 | */ 56 | package_count?: number; 57 | 58 | /** 59 | * The local pickup time of the package. 60 | * Either `pickup_time` or `estimated_pickup` is required. 61 | */ 62 | pickup_time?: string; 63 | 64 | /** 65 | * The local pickup time of the package. 66 | * Either `pickup_time` or `estimated_pickup` is required. 67 | */ 68 | estimated_pickup?: EstimatedPickup; 69 | 70 | /** 71 | * The estimated arrival date of the shipment, provided by AfterShip. 72 | */ 73 | estimated_delivery_date?: string; 74 | 75 | /** 76 | * The earliest estimated delivery date of the shipment, provided by AfterShip. 77 | */ 78 | estimated_delivery_date_min?: string; 79 | 80 | /** 81 | * The latest estimated delivery date of the shipment, provided by AfterShip. 82 | */ 83 | estimated_delivery_date_max?: string; 84 | } 85 | -------------------------------------------------------------------------------- /src/model/estimated_delivery_date/estimated_pickup.ts: -------------------------------------------------------------------------------- 1 | export interface EstimatedPickup { 2 | /** 3 | * The local order time of the package. 4 | */ 5 | order_time?: string; 6 | 7 | /** 8 | * Order cut off time. AfterShip will set 18:00:00 as the default value. 9 | */ 10 | order_cutoff_time?: string; 11 | 12 | /** 13 | * Operating days in a week. Number refers to the weekday. 14 | * E.g., [1,2,3,4,5] means operating days are from Monday to Friday. 15 | * AfterShip will set [1,2,3,4,5] as the default value. 16 | */ 17 | business_days?: number[]; 18 | 19 | order_processing_time?: OrderProcessingTime; 20 | 21 | /** 22 | * The local pickup time of the package. 23 | */ 24 | pickup_time?: string; 25 | } 26 | 27 | export interface OrderProcessingTime { 28 | /** 29 | * Processing time of an order, from being placed to being picked up. 30 | * Only support day as value now. AfterShip will set day as the default value. 31 | */ 32 | unit?: string; 33 | 34 | /** 35 | * Processing time of an order, from being placed to being picked up. 36 | * AfterShip will set 0 as the default value. 37 | */ 38 | value?: number; 39 | } 40 | -------------------------------------------------------------------------------- /src/model/estimated_delivery_date/weight.ts: -------------------------------------------------------------------------------- 1 | export interface Weight { 2 | /** 3 | * The weight unit of the package. 4 | */ 5 | unit?: string; 6 | 7 | /** 8 | * The weight of the shipment. 9 | */ 10 | value?: number; 11 | } 12 | -------------------------------------------------------------------------------- /src/model/last_checkpoint/last_checkpoint.ts: -------------------------------------------------------------------------------- 1 | import { Checkpoint } from '../checkpoint/checkpoint'; 2 | import { DeliveryStatus } from '../tracking/delivery_status'; 3 | 4 | /** 5 | * Last Checkpoint Object 6 | */ 7 | export interface LastCheckpoint { 8 | /** 9 | * Tracking number. 10 | */ 11 | tracking_number: string; 12 | 13 | /** 14 | * Unique code of courier. 15 | */ 16 | slug: string; 17 | /** 18 | * Current status of tracking. Values include: 19 | * Pending, InfoReceived, InTransit, OutForDelivery, AttemptFail, Delivered, AvailableForPickup, Exception, Expired 20 | * See tag definition: https://docs.aftership.com/api/4/delivery-status 21 | */ 22 | tag: DeliveryStatus; 23 | 24 | /** 25 | * Current subtag of tracking. 26 | * See subtag definition: https://help.aftership.com/hc/en-us/articles/360007823253 27 | */ 28 | subtag: string; 29 | 30 | /** 31 | * Normalized tracking message. 32 | * See subtag message definition: https://help.aftership.com/hc/en-us/articles/360007823253 33 | */ 34 | subtag_message: string; 35 | 36 | /** 37 | * Hash describes the checkpoint information. 38 | */ 39 | checkpoint: Checkpoint; 40 | } 41 | -------------------------------------------------------------------------------- /src/model/notification/notification.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Notification Object 3 | */ 4 | export interface Notification { 5 | /** 6 | * Hash describes the notification information. 7 | */ 8 | notification: { 9 | /** 10 | * Email address(es) to receive email notifications. 11 | */ 12 | emails: string[]; 13 | 14 | /** 15 | * Phone number(s) to receive sms notifications. 16 | */ 17 | smses: string[]; 18 | }; 19 | } 20 | -------------------------------------------------------------------------------- /src/model/notification/notification_request.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Notification Request Object 3 | */ 4 | export interface NotificationRequest { 5 | /** 6 | * Hash describes the notification information. 7 | */ 8 | notification: { 9 | /** 10 | * Email address(es) to receive email notifications. 11 | * Accept either array or comma separated as input. 12 | */ 13 | emails: string[] | string; 14 | 15 | /** 16 | * Phone number(s) to receive sms notifications. Enter+andarea codebefore phone number. 17 | * Accept either array or comma separated as input. 18 | */ 19 | smses: string[] | string; 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /src/model/rate_limit.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * RateLimit Object 3 | */ 4 | export interface RateLimit { 5 | /** 6 | * The unix timestamp when the rate limit will be reset. 7 | */ 8 | reset: number | null; 9 | 10 | /** 11 | * The rate limit ceiling for your account per sec. 12 | */ 13 | limit: number | null; 14 | 15 | /** 16 | * The number of requests left for the 1 second window. 17 | */ 18 | remaining: number | null; 19 | } 20 | -------------------------------------------------------------------------------- /src/model/tracking/custom_estimated_delivery_date.ts: -------------------------------------------------------------------------------- 1 | export interface CustomEstimatedDeliveryDate { 2 | /** 3 | * The format of the EDD. Either a single date or a date range. 4 | */ 5 | type?: string; 6 | 7 | /** 8 | * The specific EDD date. 9 | */ 10 | datetime?: string; 11 | 12 | /** 13 | * For a date range EDD format, the date and time for the lower end of the range. 14 | */ 15 | datetime_min?: string; 16 | 17 | /** 18 | * For a date range EDD format, the date and time for the upper end of the range. 19 | */ 20 | datetime_max?: string; 21 | } 22 | -------------------------------------------------------------------------------- /src/model/tracking/delivery_status.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Delivery Status 3 | * AfterShip automatically tag a status to each checkpoint when getting tracking information from carrier. 4 | * https://docs.aftership.com/api/4/delivery-status 5 | */ 6 | export enum DeliveryStatus { 7 | Pending = 'Pending', 8 | InfoReceived = 'InfoReceived', 9 | InTransit = 'InTransit', 10 | OutForDelivery = 'OutForDelivery', 11 | AttemptFail = 'AttemptFail', 12 | Delivered = 'Delivered', 13 | AvailableForPickup = 'AvailableForPickup', 14 | Exception = 'Exception', 15 | Expired = 'Expired', 16 | } 17 | -------------------------------------------------------------------------------- /src/model/tracking/delivery_type.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Shipment delivery type 3 | */ 4 | export enum DeliveryType { 5 | PickupAtStore = 'pickup_at_store', 6 | PickupAtCourier = 'pickup_at_courier', 7 | DoorToDoor = 'door_to_door', 8 | } 9 | -------------------------------------------------------------------------------- /src/model/tracking/first_estimated_delivery.ts: -------------------------------------------------------------------------------- 1 | export interface FirstEstimatedDelivery { 2 | /** 3 | * The format of the EDD. Either a single date or a date range. 4 | */ 5 | type?: string; 6 | 7 | /** 8 | * The source of the EDD. Either the carrier, AfterShip AI, or based on your custom EDD settings. 9 | */ 10 | source?: string; 11 | 12 | /** 13 | * The latest EDD time. 14 | */ 15 | datetime?: string; 16 | 17 | /** 18 | * For a date range EDD format, the date and time for the lower end of the range. 19 | */ 20 | datetime_min?: string; 21 | 22 | /** 23 | * For a date range EDD format, the date and time for the upper end of the range. 24 | */ 25 | datetime_max?: string; 26 | } 27 | -------------------------------------------------------------------------------- /src/model/tracking/latest_estimated_delivery.ts: -------------------------------------------------------------------------------- 1 | export interface LatestEstimatedDelivery { 2 | /** 3 | * The format of the EDD. Either a single date or a date range. 4 | */ 5 | type?: string; 6 | 7 | /** 8 | * The source of the EDD. Either the carrier, AfterShip AI, or based on your custom EDD settings. 9 | */ 10 | source?: string; 11 | 12 | /** 13 | * The latest EDD time. 14 | */ 15 | datetime?: string; 16 | 17 | /** 18 | * For a date range EDD format, the date and time for the lower end of the range. 19 | */ 20 | datetime_min?: string; 21 | 22 | /** 23 | * For a date range EDD format, the date and time for the upper end of the range. 24 | */ 25 | datetime_max?: string; 26 | } 27 | -------------------------------------------------------------------------------- /src/model/tracking/mark_as_complated_param.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The param to mark tracking as complete 3 | */ 4 | export interface MarkAsCompletedParam { 5 | /** 6 | * One of "DELIVERED", "LOST" or "RETURNED_TO_SENDER". 7 | */ 8 | reason: MarkAsCompletedReason; 9 | } 10 | 11 | /** 12 | * One of "DELIVERED", "LOST" or "RETURNED_TO_SENDER". 13 | */ 14 | export enum MarkAsCompletedReason { 15 | /** 16 | * Mark the tracking as completed with "DELIVERED". The tag of the tracking will be updated to Delivered 17 | * and the subtag will be updated to Delivered_001. 18 | */ 19 | Delivered = 'DELIVERED', 20 | 21 | /** 22 | * Mark the tracking as completed with "LOST". The tag of the tracking will be updated to Exception 23 | * and the subtag will be updated to Exception_013. 24 | */ 25 | Lost = 'LOST', 26 | 27 | /** 28 | * Mark the tracking as completed with "RETURNED_TO_SENDER". The tag of the tracking will be updated to Exception 29 | * and the subtag will be updated to Exception_011. 30 | */ 31 | ReturnToSender = 'RETURNED_TO_SENDER', 32 | } 33 | -------------------------------------------------------------------------------- /src/model/tracking/multi_trackings_query_params.ts: -------------------------------------------------------------------------------- 1 | import { DeliveryStatus } from './delivery_status'; 2 | 3 | /** 4 | * Tracking list query params object 5 | */ 6 | export interface MultiTrackingsQueryParams { 7 | 8 | /** 9 | * Page to show. (Default: 1) 10 | */ 11 | page?: number; 12 | 13 | /** 14 | * Number of trackings each page contain. (Default: 100, Max: 200) 15 | */ 16 | limit?: number; 17 | 18 | /** 19 | * Search the content of the tracking record fields: 20 | * tracking_number, title, order_id, customer_name, custom_fields, order_id, emails, smses 21 | */ 22 | keyword?: string; 23 | 24 | /** 25 | * Tracking number of shipments. Use comma to separate multiple values 26 | * (Example: RA123456789US,LE123456789US) 27 | */ 28 | tracking_numbers?: string; 29 | 30 | /** 31 | * Unique courier code Use comma for multiple values. (Example: dhl,ups,usps) 32 | */ 33 | slug?: string; 34 | 35 | /** 36 | * Total delivery time in days. 37 | * - Difference of 1st checkpoint time and delivered time for delivered shipments 38 | * - Difference of 1st checkpoint time and current time for non-delivered shipments 39 | * Value as 0 for pending shipments or delivered shipment with only one checkpoint. 40 | */ 41 | delivery_time?: number; 42 | 43 | /** 44 | * Origin country of trackings. Use ISO Alpha-3 (three letters). Use comma for multiple values. (Example: USA,HKG) 45 | */ 46 | origin?: string; 47 | 48 | /** 49 | * Destination country of trackings. Use ISO Alpha-3 (three letters). 50 | * Use comma for multiple values. (Example: USA,HKG) 51 | */ 52 | destination?: string; 53 | 54 | /** 55 | * Current status of tracking. 56 | */ 57 | tag?: DeliveryStatus; 58 | 59 | /** 60 | * Start date and time of trackings created. AfterShip only stores data of 90 days. 61 | * (Defaults: 30 days ago, Example: 2013-03-15T16:41:56+08:00) 62 | */ 63 | created_at_min?: string; 64 | 65 | /** 66 | * End date and time of trackings created. 67 | * (Defaults: now, Example: 2013-04-15T16:41:56+08:00) 68 | */ 69 | created_at_max?: string; 70 | 71 | /** 72 | * Start date and time of trackings updated. 73 | * (Example: 2013-04-15T16:41:56+08:00) 74 | */ 75 | updated_at_min?: string; 76 | 77 | /** 78 | * End date and time of trackings updated. (Example: 2013-04-15T16:41:56+08:00) 79 | */ 80 | updated_at_max?: string; 81 | 82 | /** 83 | * List of fields to include in the response. 84 | * Use comma for multiple values. Fields to include: title, order_id, tag, 85 | * checkpoints, checkpoint_time, message, country_name 86 | * Defaults: none, Example: title,order_id 87 | */ 88 | fields?: string; 89 | 90 | /** 91 | * Default: '' / Example: 'en' 92 | * Support Chinese to English translation for china-ems and china-post only 93 | */ 94 | lang?: string; 95 | 96 | /** 97 | * Tracking last updated at 98 | * (Example: 2013-03-15T16:41:56+08:00) 99 | */ 100 | last_updated_at?: string; 101 | 102 | /** 103 | * Select return to sender, the value should be true or false, 104 | * with optional comma separated. 105 | */ 106 | return_to_sender?: string; 107 | 108 | /** 109 | * Destination country of trackings returned by courier. 110 | * Use ISO Alpha-3 (three letters). 111 | * Use comma for multiple values. (Example: USA,HKG) 112 | */ 113 | courier_destination_country_iso3?: string; 114 | 115 | /** 116 | * Tags you added to your shipments to help categorize and filter them easily. 117 | * Use a comma to separate multiple values (Example: a,b) 118 | */ 119 | shipment_tags?: string; 120 | 121 | /** 122 | * Total delivery time in days. 123 | */ 124 | transit_time?: string; 125 | 126 | } 127 | -------------------------------------------------------------------------------- /src/model/tracking/next_courier.ts: -------------------------------------------------------------------------------- 1 | export interface NextCourier { 2 | /** 3 | * Unique code of courier 4 | */ 5 | slug?: string; 6 | 7 | /** 8 | * Tracking number 9 | */ 10 | tracking_number?: string; 11 | 12 | /** 13 | * Source of next couriers 14 | */ 15 | source?: string; 16 | } 17 | -------------------------------------------------------------------------------- /src/model/tracking/single_tracking_param.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The param to identify the single tracking. 3 | * Either id or (slug + tracking_number) should be specified. 4 | */ 5 | export interface SingleTrackingParam { 6 | /** 7 | * A unique identifier generated by AfterShip for the tracking. 8 | */ 9 | id?: string; 10 | 11 | /** 12 | * Unique code of courier 13 | */ 14 | slug?: string; 15 | 16 | /** 17 | * Tracking number of a shipment. 18 | */ 19 | tracking_number?: string; 20 | 21 | /** 22 | * Optional parameters 23 | */ 24 | optional_parameters?: { 25 | /** 26 | * The postal code of receiver's address. Required by some couriers, such asdeutsch-post 27 | */ 28 | tracking_postal_code?: string; 29 | 30 | /** 31 | * Shipping date in YYYYMMDD format. Required by some couriers, such asdeutsch-post 32 | */ 33 | tracking_ship_date?: string; 34 | 35 | /** 36 | * Destination Country of the shipment for a specific courier. Required by some couriers, such aspostnl-3s 37 | */ 38 | tracking_destination_country?: string; 39 | 40 | /** 41 | * Account number of the shipper for a specific courier. Required by some couriers, such asdynamic-logistics 42 | */ 43 | tracking_account_number?: string; 44 | 45 | /** 46 | * Key of the shipment for a specific courier. Required by some couriers, such assic-teliway 47 | */ 48 | tracking_key?: string; 49 | 50 | /** 51 | * Origin Country of the shipment for a specific courier. Required by some couriers, such asdhl 52 | */ 53 | tracking_origin_country?: string; 54 | 55 | /** 56 | * Located state of the shipment for a specific courier. Required by some couriers, such asstar-track-courier 57 | */ 58 | tracking_state?: string; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /src/model/tracking/tracking.ts: -------------------------------------------------------------------------------- 1 | import { Checkpoint } from '../checkpoint/checkpoint'; 2 | import { EstimatedDeliveryDate } from '../estimated_delivery_date/estimated_delivery_date'; 3 | import { DeliveryStatus } from './delivery_status'; 4 | import { DeliveryType } from './delivery_type'; 5 | import { LatestEstimatedDelivery } from './latest_estimated_delivery'; 6 | import { CustomEstimatedDeliveryDate } from './custom_estimated_delivery_date'; 7 | import { FirstEstimatedDelivery } from './first_estimated_delivery'; 8 | import { NextCourier } from './next_courier'; 9 | 10 | /** 11 | * Tracking Object 12 | */ 13 | export interface Tracking { 14 | /** 15 | * Hash describes the tracking information. 16 | */ 17 | tracking?: { 18 | /** 19 | * A unique identifier generated by AfterShip for the tracking. 20 | */ 21 | id?: string; 22 | 23 | /** 24 | * Date and time of the tracking created. 25 | */ 26 | created_at?: string; 27 | 28 | /** 29 | * Date and time of the tracking last updated. 30 | */ 31 | updated_at?: string; 32 | 33 | /** 34 | * Date and time the tracking was last updated 35 | */ 36 | last_updated_at?: string; 37 | 38 | /** 39 | * Tracking number. 40 | */ 41 | tracking_number: string; 42 | 43 | /** 44 | * Unique code of courier. Get courier slug here 45 | */ 46 | slug?: string; 47 | 48 | /** 49 | * Whether or not AfterShip will continue tracking the shipments. 50 | * Value is false when tag (status) is Delivered, Expired, or further updates for 30 days since last update. 51 | */ 52 | active?: boolean; 53 | 54 | /** 55 | * Custom fields of the tracking. 56 | */ 57 | custom_fields?: object; 58 | 59 | /** 60 | * Customer name of the tracking. 61 | */ 62 | customer_name?: string; 63 | 64 | /** 65 | * Total transit time in days. 66 | */ 67 | transit_time?: number; 68 | 69 | /** 70 | * Total delivery time in days. 71 | * - Difference of 1st checkpoint time and delivered time for delivered shipments 72 | * - Difference of 1st checkpoint time and current time for non-delivered shipments 73 | * value as 0 for pending shipments or delivered shipment with only one checkpoint. 74 | */ 75 | delivery_time?: number; 76 | 77 | /** 78 | * Destination country of the tracking. ISO Alpha-3 (three letters). 79 | * If you use postal service to send international shipments, 80 | * AfterShip will automatically get tracking results from destination postal service based on destination country. 81 | */ 82 | destination_country_iso3?: string; 83 | 84 | /** 85 | * Shipping address that the shipment is shipping to. 86 | */ 87 | destination_raw_location?: string; 88 | 89 | /** 90 | * Destination country of the tracking detected from the courier. 91 | * ISO Alpha-3 (three letters). Value will be null if the courier doesn't provide the destination country. 92 | */ 93 | courier_destination_country_iso3?: string; 94 | 95 | /** 96 | * Email address(es) to receive email notifications. Comma separated for multiple values. 97 | */ 98 | emails?: [string]; 99 | 100 | /** 101 | * Expected delivery date (nullable). 102 | * Available format:YYYY-MM-DD,YYYY-MM-DDTHH:MM:SS, or YYYY-MM-DDTHH:MM:SS+TIMEZONE 103 | */ 104 | expected_delivery?: string; 105 | 106 | /** 107 | * Text field for the note. 108 | */ 109 | note?: string; 110 | 111 | /** 112 | * Text field for order ID 113 | */ 114 | order_id?: string; 115 | 116 | /** 117 | * Text field for order path 118 | */ 119 | order_id_path?: string; 120 | 121 | /** 122 | * Date and time of the order created 123 | */ 124 | order_date?: string; 125 | 126 | /** 127 | * Origin country of the tracking. ISO Alpha-3 (three letters). 128 | */ 129 | origin_country_iso3?: string; 130 | 131 | /** 132 | * Number of packages under the tracking (if any). 133 | */ 134 | shipment_package_count?: number; 135 | 136 | /** 137 | * Date and time the tracking was picked up 138 | */ 139 | shipment_pickup_date?: string; 140 | 141 | /** 142 | * shipment_delivery_date 143 | */ 144 | shipment_delivery_date?: string; 145 | 146 | /** 147 | * Shipment type provided by carrier (if any). 148 | */ 149 | shipment_type?: string; 150 | 151 | /** 152 | * Shipment weight provied by carrier (if any) 153 | */ 154 | shipment_weight?: number; 155 | 156 | /** 157 | * Weight unit provied by carrier, either in kg or lb (if any) 158 | */ 159 | shipment_weight_unit?: string; 160 | 161 | /** 162 | * Signed by information for delivered shipment (if any). 163 | */ 164 | signed_by?: string; 165 | 166 | /** 167 | * Phone number(s) to receive sms notifications. 168 | * The phone number(s) to receive sms notifications. 169 | * Phone number should begin with `+` and `Area Code` before phone number. 170 | * Comma separated for multiple values. 171 | */ 172 | smses?: [string]; 173 | 174 | /** 175 | * Source of how this tracking is added. 176 | */ 177 | source?: string; 178 | 179 | /** 180 | * Current status of tracking. 181 | * Values include: Pending,InfoReceived,InTransit,OutForDelivery,AttemptFail, 182 | * Delivered,AvailableForPickup,Exception,Expired 183 | */ 184 | tag?: DeliveryStatus; 185 | 186 | /** 187 | * Current subtag of tracking. 188 | */ 189 | subtag?: string; 190 | 191 | /** 192 | * Normalized tracking message. 193 | */ 194 | subtag_message?: string; 195 | 196 | /** 197 | * Title of the tracking. 198 | */ 199 | title?: string; 200 | 201 | /** 202 | * Number of attempts AfterShip tracks at courier's system. 203 | */ 204 | tracked_count?: number; 205 | 206 | /** 207 | * Indicates if the shipment is trackable till the final destination. 208 | * Three possible values: true,true,null. 209 | */ 210 | last_mile_tracking_supported?: boolean | null; 211 | 212 | /** 213 | * Store, customer, or order language of the tracking. 214 | */ 215 | language?: string | null; 216 | 217 | /** 218 | * Array of Hash describes the checkpoint information. 219 | */ 220 | checkpoints?: [Checkpoint]; 221 | 222 | /** 223 | * Phone number(s) subscribed to receive sms notifications. Comma separated for multiple values 224 | */ 225 | subscribed_smses?: [string]; 226 | 227 | /** 228 | * Email address(es) subscribed to receive email notifications. Comma separated for multiple values 229 | */ 230 | subscribed_emails?: [string]; 231 | 232 | /** 233 | * Whether or not the shipment is returned to sender. 234 | * Value istruewhen any of its checkpoints has subtagException_010(returning to sender) 235 | * orException_011(returned to sender). Otherwise value isfalse 236 | */ 237 | return_to_sender?: boolean; 238 | 239 | /** 240 | * Promised delivery date of an order inYYYY-MM-DDformat. 241 | */ 242 | order_promised_delivery_date?: string; 243 | 244 | /** 245 | * Shipment delivery type 246 | */ 247 | delivery_type?: DeliveryType; 248 | 249 | /** 250 | * Shipment pickup location for receiver 251 | */ 252 | pickup_location?: string; 253 | 254 | /** 255 | * Shipment pickup note for receiver 256 | */ 257 | pickup_note?: string; 258 | 259 | /** 260 | * Official tracking URL of the courier (if any) 261 | */ 262 | courier_tracking_link?: string; 263 | 264 | /** 265 | * date and time of the first attempt by the carrier to deliver the package to the addressee 266 | * Available format:YYYY-MM-DDTHH:MM:SS, or YYYY-MM-DDTHH:MM:SS+TIMEZONE 267 | */ 268 | first_attempted_at?: string; 269 | 270 | /** 271 | * Delivery instructions (delivery date or address) can be modified by visiting the link if supported by a carrier. 272 | */ 273 | courier_redirect_link?: string; 274 | 275 | /** 276 | * Account number of the shipper for a specific courier. Required by some couriers, such asdynamic-logistics 277 | */ 278 | tracking_account_number?: string; 279 | 280 | /** 281 | * Origin Country of the shipment for a specific courier. Required by some couriers, such asdhl 282 | */ 283 | tracking_origin_country?: string; 284 | 285 | /** 286 | * Destination Country of the shipment for a specific courier. Required by some couriers, such aspostnl-3s 287 | */ 288 | tracking_destination_country?: string; 289 | 290 | /** 291 | * Key of the shipment for a specific courier. Required by some couriers, such assic-teliway 292 | */ 293 | tracking_key?: string; 294 | 295 | /** 296 | * The postal code of receiver's address. Required by some couriers, such asdeutsch-post 297 | */ 298 | tracking_postal_code?: string; 299 | 300 | /** 301 | * Shipping date inYYYYMMDDformat. Required by some couriers, such asdeutsch-post 302 | */ 303 | tracking_ship_date?: string; 304 | 305 | /** 306 | * Located state of the shipment for a specific courier. Required by some couriers, such asstar-track-courier 307 | */ 308 | tracking_state?: string; 309 | 310 | /** 311 | * Whether the tracking is delivered on time or not. 312 | */ 313 | on_time_status?: string; 314 | 315 | /** 316 | * The difference days of the on time. 317 | */ 318 | on_time_difference?: number; 319 | 320 | /** 321 | * The tags of the order. 322 | */ 323 | order_tags?: [string]; 324 | 325 | /** 326 | * Estimated delivery time of the shipment provided by AfterShip, indicate when the shipment should arrive. 327 | */ 328 | aftership_estimated_delivery_date?: EstimatedDeliveryDate; 329 | 330 | /** 331 | * Text field for order number 332 | */ 333 | order_number?: string; 334 | 335 | /** 336 | * The latest estimated delivery date. 337 | * May come from the carrier, AfterShip AI, or based on your custom settings. 338 | * This can appear in 1 of 3 formats based on the data received. 339 | * 1. Date only: `YYYY-MM-DD` 340 | * 2. Date and time: `YYYY-MM-DDTHH:mm:ss` 341 | * 3. Date, time, and time zone: `YYYY-MM-DDTHH:mm:ssZ` 342 | */ 343 | latest_estimated_delivery?: LatestEstimatedDelivery; 344 | 345 | /** 346 | * The state of the sender’s address 347 | */ 348 | origin_state: string | null; 349 | 350 | /** 351 | * The city of the sender’s address 352 | */ 353 | origin_city: string | null; 354 | 355 | /** 356 | * The postal code of the sender’s address 357 | */ 358 | origin_postal_code: string | null; 359 | 360 | /** 361 | * The sender address that the shipment is shipping from 362 | */ 363 | origin_raw_location: string | null; 364 | 365 | /** 366 | * The state of the recipient’s address 367 | */ 368 | destination_state: string | null; 369 | 370 | /** 371 | * The city of the recipient’s address 372 | */ 373 | destination_city: string | null; 374 | 375 | /** 376 | * The postal code of the recipient’s address 377 | */ 378 | destination_postal_code: string | null; 379 | 380 | /** 381 | * Estimated delivery time of the shipment based on your 382 | * custom EDD settings(https://admin.aftership.com/settings/promised-delivery-date). 383 | * It uses the format YYYY-MM-DD based on the shipment recipient’s timezone. 384 | */ 385 | custom_estimated_delivery_date: CustomEstimatedDeliveryDate | null; 386 | 387 | /** 388 | * The shipment’s original estimated delivery date. It could be provided by the carrier, 389 | * AfterShip AI,or based on your custom settings 390 | */ 391 | first_estimated_delivery: FirstEstimatedDelivery | null; 392 | 393 | /** 394 | * Used to add tags to your shipments to help categorize and filter them easily 395 | */ 396 | shipment_tags: string[]; 397 | 398 | /** 399 | * The courier connection id tells which carrier account you’ve used to handle a shipment so we can track it 400 | */ 401 | courier_connection_id: string | null; 402 | 403 | /** 404 | * The next couriers get the second carrier information from user or AfterShip 405 | */ 406 | next_couriers: NextCourier[]; 407 | 408 | }; 409 | } 410 | -------------------------------------------------------------------------------- /src/model/tracking/tracking_create_params.ts: -------------------------------------------------------------------------------- 1 | import { AftershipError } from '../../error/error'; 2 | import { ErrorEnum } from '../../error/error_enum'; 3 | import { DeliveryType } from './delivery_type'; 4 | import { NextCourier } from './next_courier'; 5 | 6 | /** 7 | * The request object of tracking create 8 | */ 9 | 10 | export class TrackingCreateParams { 11 | /** 12 | * Tracking Object. 13 | */ 14 | public tracking: TrackingCreate; 15 | 16 | /** 17 | * TrackingCreateParams constructor 18 | * @param tracking tracking object, the tracking_number field is required. 19 | */ 20 | constructor(tracking: TrackingCreate) { 21 | if (tracking === undefined || tracking.tracking_number === undefined 22 | || tracking.tracking_number === '') { 23 | // Verify tracking_number 24 | throw AftershipError.getSdkError( 25 | ErrorEnum.constructorInvalidTrackingNumber, 26 | tracking, 27 | ); 28 | } 29 | 30 | this.tracking = tracking; 31 | } 32 | } 33 | 34 | /** 35 | * The tracking object in tracking create request 36 | */ 37 | export interface TrackingCreate { 38 | /** 39 | * Tracking number. (Required) 40 | */ 41 | tracking_number: string; 42 | 43 | /** 44 | * Unique code of each courier. 45 | * Provide a single courier or array for a list of couriers. 46 | * If you do not specify a slug, Aftership will automatically detect 47 | * the courier based on the tracking number format and your selected couriers. 48 | * Get a list of courier slug using GET /couriers 49 | */ 50 | slug?: string | [string]; 51 | 52 | /** 53 | * Title of the tracking. Default value astracking_number 54 | */ 55 | title?: string; 56 | 57 | /** 58 | * Text field for order ID 59 | */ 60 | order_id?: string; 61 | 62 | /** 63 | * Text field for order path 64 | */ 65 | order_id_path?: string; 66 | 67 | /** 68 | * Custom fields that accept a hash with string, boolean or number fields 69 | */ 70 | custom_fields?: Object; 71 | 72 | /** 73 | * Enter ISO 639-1 Language Code to specify the store, customer or order language. 74 | */ 75 | language?: string; 76 | 77 | /** 78 | * Promised delivery date of an order inYYYY-MM-DDformat. 79 | */ 80 | order_promised_delivery_date?: string; 81 | 82 | /** 83 | * Shipment delivery type 84 | */ 85 | delivery_type?: DeliveryType; 86 | 87 | /** 88 | * Shipment pickup location for receiver 89 | */ 90 | pickup_location?: string; 91 | 92 | /** 93 | * Shipment pickup note for receiver 94 | */ 95 | pickup_note?: string; 96 | 97 | /** 98 | * Account number of the shipper for a specific courier. Required by some couriers, such asdynamic-logistics 99 | */ 100 | tracking_account_number?: string; 101 | 102 | /** 103 | * Origin Country of the shipment for a specific courier. Required by some couriers, such asdhl 104 | */ 105 | tracking_origin_country?: string; 106 | /** 107 | * Destination Country of the shipment for a specific courier. Required by some couriers, such aspostnl-3s 108 | */ 109 | tracking_destination_country?: string; 110 | 111 | /** 112 | * Key of the shipment for a specific courier. Required by some couriers, such assic-teliway 113 | */ 114 | tracking_key?: string; 115 | 116 | /** 117 | * The postal code of receiver's address. Required by some couriers, such asdeutsch-post 118 | */ 119 | tracking_postal_code?: string; 120 | 121 | /** 122 | * Shipping date inYYYYMMDDformat. Required by some couriers, such asdeutsch-post 123 | */ 124 | tracking_ship_date?: string; 125 | 126 | /** 127 | * Located state of the shipment for a specific courier. Required by some couriers, such asstar-track-courier 128 | */ 129 | tracking_state?: string; 130 | 131 | /** 132 | * Apple iOS device IDs to receive the push notifications. 133 | * Accept either array or comma separated as input. 134 | */ 135 | ios?: string | [string]; 136 | 137 | /** 138 | * Google cloud message registration IDs to receive the push notifications. 139 | * Accept either array or comma separated as input. 140 | */ 141 | android?: string | [string]; 142 | 143 | /** 144 | * Email address(es) to receive email notifications. 145 | * Accept either array or comma separated as input. 146 | */ 147 | emails?: string | [string]; 148 | 149 | /** 150 | * Phone number(s) to receive sms notifications. 151 | * Enter+ andarea code before phone number. 152 | * Accept either array or comma separated as input. 153 | */ 154 | smses?: string | [string]; 155 | 156 | /** 157 | * Customer name of the tracking. 158 | */ 159 | customer_name?: string; 160 | 161 | /** 162 | * Enter ISO Alpha-3 (three letters) to specify the origin of the shipment (e.g. USA for United States). 163 | */ 164 | origin_country_iso3?: string; 165 | 166 | /** 167 | * Enter ISO Alpha-3 (three letters) to specify the destination of the shipment (e.g. USA for United States). 168 | * If you use postal service to send international shipments, AfterShip will automatically 169 | * get tracking results at destination courier as well. 170 | */ 171 | destination_country_iso3?: string; 172 | 173 | /** 174 | * Text field for the note 175 | */ 176 | note?: string; 177 | 178 | /** 179 | * Slug group is a group of slugs which belong to same courier. 180 | * For example, when you inpit "fedex-group" as slug_group, 181 | * AfterShip will detect the tracking with "fedex-uk", "fedex-fims", 182 | * and other slugs which belong to "fedex". 183 | * It cannot be used with slug at the same time. 184 | */ 185 | slug_group?: string; 186 | 187 | /** 188 | * Date and time of the order created 189 | */ 190 | order_date?: string; 191 | 192 | /** 193 | * Text field for order number 194 | */ 195 | order_number?: string; 196 | 197 | /** 198 | * The carrier’s shipment type. When you input this field, AfterShip will not get updates from the carrier. 199 | */ 200 | shipment_type?: string; 201 | 202 | /** 203 | * The state of the sender’s address. This can help AfterShip with various functions like tracking, 204 | * carrier auto-detection and auto-correction, calculating an EDD, etc. 205 | */ 206 | origin_state?: string; 207 | 208 | /** 209 | * The city of the sender’s address. This can help AfterShip with various functions like tracking, 210 | * carrier auto-detection and auto-correction, calculating an EDD, etc. 211 | */ 212 | origin_city?: string; 213 | 214 | /** 215 | * The postal of the sender’s address. This can help AfterShip with various functions like tracking, 216 | * carrier auto-detection and auto-correction, calculating an EDD, etc. 217 | */ 218 | origin_postal_code?: string; 219 | 220 | /** 221 | * The sender address that the shipment is shipping from. 222 | * This can help AfterShip with various functions like tracking, 223 | * carrier auto-detection and auto-correction, calculating an EDD, etc. 224 | */ 225 | origin_raw_location?: string; 226 | 227 | /** 228 | * The state of the recipient’s address. This can help AfterShip with various functions like tracking, 229 | * carrier auto-detection and auto-correction, calculating an EDD, etc. 230 | * Also the additional field required by some carriers to retrieve the tracking info. 231 | * The state/province of the recipient’s address. 232 | */ 233 | destination_state?: string; 234 | 235 | /** 236 | * The city of the recipient’s address. This can help AfterShip with various functions like tracking, 237 | * carrier auto-detection and auto-correction, calculating an EDD, etc. 238 | */ 239 | destination_city?: string; 240 | 241 | /** 242 | * The postal of the recipient’s address. This can help AfterShip with various functions like tracking, 243 | * carrier auto-detection and auto-correction, calculating an EDD, etc. 244 | * Also the additional field required by some carriers to retrieve the tracking info. 245 | * The postal code of the recipient’s address. 246 | */ 247 | destination_postal_code?: string; 248 | 249 | /** 250 | * The shipping address that the shipment is shipping to. 251 | * This can help AfterShip with various functions like tracking, 252 | * carrier auto-detection and auto-correction, calculating an EDD, etc. 253 | */ 254 | destination_raw_location?: string; 255 | 256 | /** 257 | * Used to add tags to your shipments to help categorize and filter them easily. 258 | */ 259 | shipment_tags?: string[]; 260 | 261 | /** 262 | * If you’ve connected multiple accounts for a single carrier on AfterShip, 263 | * you can now use the courier_connection_id field to tell AfterShip which 264 | * carrier account you’ve used to handle a shipment so we can track it. 265 | */ 266 | courier_connection_id?: string; 267 | 268 | /** 269 | * If a shipment has multiple carriers, you can use the next_couriers 270 | * field to tell AfterShip who the second carrier is. 271 | * This is useful if the first carrier does not send us this information. 272 | */ 273 | next_couriers?: NextCourier[]; 274 | } 275 | -------------------------------------------------------------------------------- /src/model/tracking/tracking_list.ts: -------------------------------------------------------------------------------- 1 | import { Tracking } from '../tracking/tracking'; 2 | /** 3 | * tracking results. 4 | */ 5 | export interface TrackingList { 6 | 7 | /** 8 | * Number of trackings each page contain. (Default: 100) 9 | */ 10 | limit?: number; 11 | 12 | /** 13 | * Total number of matched trackings, max. number is 10,000 14 | */ 15 | count?: number; 16 | 17 | /** 18 | * Page to show. (Default: 1) 19 | */ 20 | page?: number; 21 | 22 | /** 23 | * Searching keyword 24 | */ 25 | keyword?: string; 26 | 27 | /** 28 | * Unique courier code Use comma for multiple values. 29 | */ 30 | slug?: string; 31 | 32 | /** 33 | * Origin country/region of trackings. Use ISO Alpha-3 (three letters). 34 | */ 35 | origin?: string[]; 36 | 37 | /** 38 | * Destination country/region of trackings. Use ISO Alpha-3 (three letters). 39 | */ 40 | destination?: string[]; 41 | 42 | /** 43 | * Current status of tracking. 44 | */ 45 | tag?: string; 46 | 47 | /** 48 | * Start date and time of trackings created. AfterShip only stores data of 90 days. 49 | */ 50 | created_at_min?: string; 51 | 52 | /** 53 | * End date and time of trackings created. 54 | */ 55 | created_at_max?: string; 56 | 57 | /** 58 | * Date and time the tracking was last updated. 59 | */ 60 | last_updated_at?: string; 61 | 62 | /** 63 | * Whether or not the shipment is returned to sender. 64 | */ 65 | return_to_sender?: boolean[]; 66 | 67 | /** 68 | * Total delivery time in days. 69 | */ 70 | transit_time?: number; 71 | 72 | /** 73 | * Destination country/region of the tracking detected from the courier. 74 | */ 75 | courier_destination_country_iso3?: string[]; 76 | 77 | /** 78 | * Array of Hash describes the tracking information. 79 | */ 80 | trackings?: [Tracking['tracking']]; 81 | } 82 | -------------------------------------------------------------------------------- /src/model/tracking/tracking_query_params.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Tracking query params object 3 | */ 4 | export interface TrackingQueryParams { 5 | 6 | /** 7 | * List of fields to include in the response. 8 | * Use comma for multiple values. Fields to include: 9 | * tracking_postal_code,tracking_ship_date,tracking_account_number,tracking_key, 10 | * tracking_origin_country,tracking_destination_country,tracking_state,title,order_id, 11 | * tag,checkpoints,checkpoint_time, message, country_name 12 | * Defaults: none, Example: title,order_id 13 | */ 14 | fields?: string; 15 | 16 | /** 17 | * Support Chinese to English translation for china-ems and china-post only (Example: en) 18 | */ 19 | lang?: string; 20 | } 21 | -------------------------------------------------------------------------------- /src/model/tracking/tracking_update_params.ts: -------------------------------------------------------------------------------- 1 | import { DeliveryType } from './delivery_type'; 2 | /** 3 | * Tracking update params object 4 | */ 5 | export interface TrackingUpdateParams { 6 | /** 7 | * Tracking Update Object. 8 | */ 9 | tracking: TrackingUpdate; 10 | } 11 | 12 | interface TrackingUpdate { 13 | 14 | /** 15 | * Phone number(s) to receive sms notifications. 16 | */ 17 | smses?: [string]; 18 | 19 | /** 20 | * Email address(es) to receive email notifications. 21 | */ 22 | emails?: [string]; 23 | 24 | /** 25 | * Title of the tracking. 26 | */ 27 | title?: string; 28 | 29 | /** 30 | * Customer name of the tracking. 31 | */ 32 | customer_name?: string; 33 | 34 | /** 35 | * Text field for order ID 36 | */ 37 | order_id?: string; 38 | 39 | /** 40 | * Text field for order path 41 | */ 42 | order_id_path?: string; 43 | 44 | /** 45 | * Text field for order number 46 | */ 47 | order_number?: string; 48 | 49 | /** 50 | * Date and time of the order created 51 | */ 52 | order_date?: string; 53 | 54 | /** 55 | * Custom fields that accept a hash with string, boolean or number fields 56 | */ 57 | custom_fields?: object; 58 | 59 | /** 60 | * Text field for the note 61 | */ 62 | note?: string; 63 | 64 | /** 65 | * Enter ISO 639-1 Language Code to specify the store, customer or order language. 66 | */ 67 | language?: string; 68 | 69 | /** 70 | * Promised delivery date of an order inYYYY-MM-DDformat. 71 | */ 72 | order_promised_delivery_date?: string; 73 | 74 | /** 75 | * Shipment delivery type 76 | */ 77 | delivery_type?: DeliveryType; 78 | 79 | /** 80 | * Shipment pickup location for receiver 81 | */ 82 | pickup_location?: string; 83 | 84 | /** 85 | * Shipment pickup note for receiver 86 | */ 87 | pickup_note?: string; 88 | 89 | /** 90 | * The carrier’s shipment type. When you input this field, AfterShip will not get updates from the carrier. 91 | */ 92 | shipment_type?: string; 93 | 94 | /** 95 | * Unique code of each courier. Provide a single courier 96 | */ 97 | slug?: string; 98 | 99 | /** 100 | * Additional field required by some carriers to retrieve the tracking info. The shipper’s carrier account number. 101 | */ 102 | tracking_account_number?: string; 103 | 104 | /** 105 | * Additional field required by some carriers to retrieve the tracking info. 106 | * A type of tracking credential required by some carriers 107 | */ 108 | tracking_key?: string; 109 | 110 | /** 111 | * Additional field required by some carriers to retrieve the tracking info. 112 | * The date the shipment was sent, using the format YYYYMMDD 113 | */ 114 | tracking_ship_date?: string; 115 | 116 | /** 117 | * The ISO Alpha-3 code (3 letters) for the origin country/region. 118 | * E.g. USA for the United States. 119 | * This can help AfterShip with various functions like tracking, 120 | * carrier auto-detection and auto-correction, calculating an EDD, etc. 121 | * Also the additional field required by some carriers to retrieve 122 | * the tracking info. The origin country/region of the shipment 123 | */ 124 | origin_country_iso3?: string; 125 | 126 | /** 127 | * The state of the sender’s address. This can help AfterShip with 128 | * various functions like tracking, carrier auto-detection and auto-correction, 129 | * calculating an EDD, etc 130 | */ 131 | origin_state?: string; 132 | 133 | /** 134 | * The city of the sender’s address. This can help AfterShip with 135 | * various functions like tracking, carrier auto-detection and auto-correction, 136 | * calculating an EDD, etc 137 | */ 138 | origin_city?: string; 139 | 140 | /** 141 | * The postal of the sender’s address. This can help AfterShip with 142 | * various functions like tracking, carrier auto-detection and auto-correction, 143 | * calculating an EDD, etc. 144 | */ 145 | origin_postal_code?: string; 146 | 147 | /** 148 | * The sender address that the shipment is shipping from. 149 | * This can help AfterShip with various functions like tracking, 150 | * carrier auto-detection and auto-correction, calculating an EDD, etc 151 | */ 152 | origin_raw_location?: string; 153 | 154 | /** 155 | * The ISO Alpha-3 code (3 letters) for the destination country/region. 156 | * E.g. USA for the United States. This can help AfterShip with various functions like tracking, 157 | * carrier auto-detection and auto-correction, calculating an EDD, etc. 158 | * Also the additional field required by some carriers to retrieve the 159 | * tracking info. The destination country/region of the shipment. 160 | */ 161 | destination_country_iso3?: string; 162 | 163 | /** 164 | * The state of the recipient’s address. This can help AfterShip with various 165 | * functions like tracking, carrier auto-detection and auto-correction, 166 | * calculating an EDD, etc. Also the additional field required by some carriers 167 | * to retrieve the tracking info. The state/province of the recipient’s address. 168 | */ 169 | destination_state?: string; 170 | 171 | /** 172 | * The city of the recipient’s address. This can help AfterShip with various 173 | * functions like tracking, carrier auto-detection and auto-correction, 174 | * calculating an EDD, etc 175 | */ 176 | destination_city?: string; 177 | 178 | /** 179 | * The postal of the recipient’s address. This can help AfterShip with various functions 180 | * like tracking, carrier auto-detection and auto-correction, calculating an EDD, etc. 181 | * Also the additional field required by some carriers to retrieve the tracking info. 182 | * The postal code of the recipient’s address 183 | */ 184 | destination_postal_code?: string; 185 | 186 | /** 187 | * The shipping address that the shipment is shipping to. This can help AfterShip with 188 | * various functions like tracking, carrier auto-detection and auto-correction, 189 | * calculating an EDD, etc. 190 | */ 191 | destination_raw_location?: string; 192 | } 193 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "declarationMap": true, 7 | "sourceMap": true, 8 | "outDir": "dist", 9 | "strict": true, 10 | "noUnusedLocals": true, 11 | "noUnusedParameters": true, 12 | "noImplicitReturns": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "moduleResolution": "node", 15 | "baseUrl": ".", 16 | "paths": { 17 | "*": [ 18 | "./types/*" 19 | ] 20 | }, 21 | "esModuleInterop": true, 22 | }, 23 | "include": [ 24 | "src/**/*" 25 | ], 26 | "exclude": [ 27 | "src/**/*.spec.js", 28 | "src/**/*.js" 29 | ], 30 | "jsdoc": { 31 | "out": "support/jsdoc", 32 | "access": "public" 33 | } 34 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint-config-airbnb" 4 | ], 5 | "rules": { 6 | /* modifications to base config */ 7 | // adds statements, members, and elements to the base config 8 | "align": [ 9 | true, 10 | "parameters", 11 | "arguments", 12 | "statements", 13 | "members", 14 | "elements" 15 | ], 16 | // adds number of spaces so auto-fixing will work 17 | "indent": [ 18 | true, 19 | "spaces", 20 | 2 21 | ], 22 | // increase value from 100 in base config to 120 23 | "max-line-length": [ 24 | true, 25 | 120 26 | ], 27 | // adds avoid-escape and avoid-template 28 | "quotemark": [ 29 | true, 30 | "single", 31 | "avoid-escape", 32 | "avoid-template" 33 | ], 34 | // adds ban-keywords and allow-leading-underscores 35 | // once this gets implemented, we should incorporate it: https://github.com/palantir/tslint/issues/3442 36 | "variable-name": [ 37 | true, 38 | "ban-keywords", 39 | "check-format", 40 | "allow-leading-underscore", 41 | "allow-snake-case" 42 | ], 43 | // adds check-module, check-type, check-rest-spread, check-typecast, check-type-operator 44 | "whitespace": [ 45 | true, 46 | "check-branch", 47 | "check-decl", 48 | "check-operator", 49 | "check-preblock", 50 | "check-type", 51 | "check-module", 52 | "check-separator", 53 | "check-rest-spread", 54 | "check-typecast", 55 | "check-type-operator" 56 | ], 57 | /* not used in base config */ 58 | "await-promise": true, 59 | "ban-comma-operator": true, 60 | // Disabling the following rule because of https://github.com/palantir/tslint/issues/4493 61 | // "completed-docs": true, 62 | "interface-over-type-literal": true, 63 | "jsdoc-format": [ 64 | true, 65 | "check-multiline-start" 66 | ], 67 | "member-access": [ 68 | true, 69 | "check-accessor" 70 | ], 71 | "no-duplicate-imports": true, 72 | "no-duplicate-switch-case": true, 73 | "no-duplicate-variable": true, 74 | "no-dynamic-delete": true, 75 | "no-empty": true, 76 | "no-floating-promises": true, 77 | "no-for-in-array": true, 78 | "no-implicit-dependencies": [true, "dev"], 79 | "no-object-literal-type-assertion": true, 80 | "no-redundant-jsdoc": true, 81 | "no-require-imports": true, 82 | "no-return-await": true, 83 | "no-submodule-imports": true, 84 | "no-this-assignment": true, 85 | "no-unused-expression": true, 86 | "no-var-requires": true, 87 | "one-line": [ 88 | true, 89 | "check-else", 90 | "check-whitespace", 91 | "check-open-brace", 92 | "check-catch", 93 | "check-finally" 94 | ], 95 | "strict-boolean-expressions": [ 96 | true, 97 | "allow-boolean-or-undefined" 98 | ], 99 | "typedef": [ 100 | true, 101 | "call-signature" 102 | ], 103 | "typedef-whitespace": [ 104 | true, 105 | { 106 | "call-signature": "nospace", 107 | "index-signature": "nospace", 108 | "parameter": "nospace", 109 | "property-declaration": "nospace", 110 | "variable-declaration": "nospace" 111 | } 112 | ] 113 | // TODO: find a rule similar to https://palantir.github.io/tslint/rules/no-construct/, except it bans those types 114 | // from interfaces (e.g. a function that returns Boolean is an error, it should return boolean) 115 | }, 116 | "linterOptions": { 117 | "format": "verbose" 118 | } 119 | } --------------------------------------------------------------------------------