├── .gitignore ├── .gitattributes ├── .travis.yml ├── test.js ├── .editorconfig ├── index.js ├── package.json ├── license ├── readme.md └── cli.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '6' 4 | - '5' 5 | - '4' 6 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | import test from 'ava'; 2 | import fn from './'; 3 | 4 | test('postnord', async t => { 5 | t.is(typeof await fn('12324790034SE', {apikey: 'bb956a2d13a54a152dba849b76288cf4'}), 'object'); 6 | }); 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [{package.json,*.yml}] 11 | indent_style = space 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | const condenseKeys = require('condense-keys'); 3 | const got = require('got'); 4 | const queryString = require('query-string'); 5 | 6 | module.exports = (id, opts) => { 7 | opts = Object.assign({ 8 | apikey: 'bb956a2d13a54a152dba849b76288cf4', 9 | locale: 'en' 10 | }, condenseKeys(opts), {id}); 11 | 12 | if (!opts.apikey) { 13 | throw new Error('Expected an API key'); 14 | } 15 | 16 | if (typeof id === 'undefined') { 17 | throw new TypeError('Expected a shipment or item identifier'); 18 | } 19 | 20 | return got(`https://api2.postnord.com/rest/shipment/v1/trackandtrace/findByIdentifier.json?${queryString.stringify(opts)}`, {json: true}).then(res => res.body); 21 | }; 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postnord", 3 | "version": "0.2.2", 4 | "description": "Track PostNord letters, parcels and pallets by their item ID number", 5 | "license": "MIT", 6 | "repository": "gillstrom/postnord", 7 | "author": { 8 | "name": "Andreas Gillström", 9 | "email": "andreasgillstrom@gmail.com", 10 | "url": "github.com/gillstrom" 11 | }, 12 | "bin": "cli.js", 13 | "engines": { 14 | "node": ">=4" 15 | }, 16 | "scripts": { 17 | "test": "xo && ava" 18 | }, 19 | "files": [ 20 | "index.js", 21 | "cli.js" 22 | ], 23 | "keywords": [ 24 | "api", 25 | "app", 26 | "cli", 27 | "cli-app", 28 | "letter", 29 | "pallet", 30 | "parcel", 31 | "post", 32 | "postnord", 33 | "track" 34 | ], 35 | "dependencies": { 36 | "arrify": "^1.0.1", 37 | "capitalize": "^1.0.0", 38 | "chalk": "^1.1.3", 39 | "condense-keys": "^1.1.0", 40 | "easydate": "^2.1.0", 41 | "got": "^6.3.0", 42 | "meow": "^3.7.0", 43 | "ora": "^0.2.1", 44 | "query-string": "^4.1.0" 45 | }, 46 | "devDependencies": { 47 | "ava": "^0.14.0", 48 | "xo": "^0.14.0" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Andreas Gillström (github.com/gillstrom) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # postnord [![Build Status](https://travis-ci.org/gillstrom/postnord.svg?branch=master)](https://travis-ci.org/gillstrom/postnord) 2 | 3 | > Track PostNord letters, parcels and pallets by their item ID number 4 | 5 | 6 | 7 | 8 | 9 | ## CLI 10 | 11 | ``` 12 | $ npm install --global postnord 13 | ``` 14 | 15 | ``` 16 | $ postnord --help 17 | 18 | Usage 19 | $ postnord [ID] 20 | 21 | Options 22 | -a, --apikey PostNord API key 23 | -l, --locale Default is en. Allowed values are en, sv, no, da and fi 24 | 25 | Example 26 | $ postnord 12371563697SE 27 | ``` 28 | 29 | 30 | ## Install 31 | 32 | ``` 33 | $ npm install --save postnord 34 | ``` 35 | 36 | 37 | ## Usage 38 | 39 | ```js 40 | const postnord = require('postnord'); 41 | 42 | postnord('12371563697SE', {apikey: 'aa956a2d13a54a152dba849b76288cf4'}).then(result => { 43 | console.log(result); 44 | //=> {'TrackingInformationResponse': {'shipments': [ ...} 45 | }); 46 | ``` 47 | 48 | 49 | ## API 50 | 51 | ### postnord(ID, [options]) 52 | 53 | Returns a promise that resolves in to an object. 54 | 55 | #### ID 56 | 57 | *Required*
58 | Type: `string` 59 | 60 | The item ID to track. 61 | 62 | #### options 63 | 64 | ##### apikey 65 | 66 | Type: `string`
67 | Default: My own, [please use your own.](https://developer.postnord.com) 68 | 69 | The unique PostNord API key. 70 | 71 | ##### locale 72 | 73 | Type: `string`
74 | Default: `'en'` 75 | 76 | Returns the result in different languages. Allowed values are `'en'`, `'sv'`, `'no'`, `'da'` and `'fi'`. 77 | 78 | 79 | ## License 80 | 81 | MIT © [Andreas Gillström](http://github.com/gillstrom) 82 | -------------------------------------------------------------------------------- /cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | const arrify = require('arrify'); 4 | const capitalize = require('capitalize'); 5 | const chalk = require('chalk'); 6 | const easydate = require('easydate'); 7 | const meow = require('meow'); 8 | const ora = require('ora'); 9 | const fn = require('./'); 10 | 11 | const cli = meow([` 12 | Usage 13 | $ postnord [ID] 14 | 15 | Options 16 | -a, --apikey PostNord API key 17 | -l, --locale Default is en. Allowed values are en, sv, no, da and fi 18 | 19 | Examples 20 | $ postnord 12371563697SE 21 | `], { 22 | alias: { 23 | a: 'apikey', 24 | l: 'locale' 25 | }, 26 | string: ['_'] 27 | }); 28 | 29 | if (!cli.input[0]) { 30 | console.error(chalk.red('Expected a shipment or item identifier')); 31 | process.exit(1); 32 | } 33 | 34 | const spinner = ora('Loading shipment information'); 35 | spinner.start(); 36 | 37 | const log = arr => console.log(arrify(arr).map(x => `\t${x}`).join('\n')); 38 | 39 | const getEvents = arr => { 40 | if (!arr || arr.length === 0) { 41 | return; 42 | } 43 | 44 | arr.reverse(); 45 | arr.forEach(x => { 46 | log([ 47 | `${easydate('Y-M-d h:m', {setDate: `${x.eventTime}+0200`, timeZone: 'local'})} - ${x.location.displayName}`, 48 | `\u00A0\u00A0${chalk.dim(x.eventDescription)}`, 49 | '' 50 | ]); 51 | }); 52 | }; 53 | 54 | const getMeasurement = obj => { 55 | for (const x in obj) { 56 | if (x) { 57 | log(`${capitalize(x)}:\t\t${obj[x].value} ${obj[x].unit}`); 58 | } 59 | } 60 | }; 61 | 62 | const getItems = arr => arr.forEach(obj => { 63 | log(['', obj.statusText.body]); 64 | 65 | if (obj.acceptor) { 66 | log(chalk.dim(` Signed by: ${obj.acceptor.name}`)); 67 | } 68 | 69 | log(['', '']); 70 | getEvents(obj.events); 71 | getMeasurement(obj.statedMeasurement); 72 | }); 73 | 74 | const print = obj => { 75 | log(['', `${obj.shipmentId} - ${obj.status.replace('_', '')}`]); 76 | 77 | if (!obj.deliveryDate) { 78 | log(chalk.dim(` Estimated delivery: ${easydate('Y-M-d h:m', {setDate: `${obj.estimatedTimeOfArrival}+0200`, timeZone: 'local'})}`)); 79 | } 80 | 81 | getItems(obj.items); 82 | log(['', `Sender:\t\t${obj.consignor.name}`]); 83 | 84 | if (obj.consignor.address) { 85 | log([ 86 | `\t\t${obj.consignor.address.postCode || ''} ${obj.consignor.address.city || ''}`, 87 | `\t\t${obj.consignor.address.country || ''}` 88 | ]); 89 | } 90 | 91 | log([ 92 | '', 93 | `Recipient:\t${obj.consignee.address.postCode || ''} ${obj.consignee.address.city || ''}`, 94 | `\t\t${obj.consignee.address.country || ''}` 95 | ]); 96 | }; 97 | 98 | fn(cli.input[0], {apikey: cli.flags.apikey, locale: cli.flags.locale}).then(res => { 99 | spinner.stop(); 100 | 101 | if (res.TrackingInformationResponse.shipments.length === 0) { 102 | console.error(chalk.red('Couldn\'t find any shipments with this ID')); 103 | } 104 | 105 | res.TrackingInformationResponse.shipments.forEach(x => print(x)); 106 | }); 107 | --------------------------------------------------------------------------------