@things-factory/integration-lmd 8.0.0-beta.8 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -14
- package/server/controllers/citylink/apis/index.ts +1 -0
- package/server/controllers/citylink/apis/shipment-request/create-shipment-request.ts +12 -0
- package/server/controllers/citylink/apis/shipment-request/index.ts +1 -0
- package/server/controllers/citylink/citylink.ts +161 -0
- package/server/controllers/citylink/index.ts +8 -0
- package/server/controllers/citylink/platform-action.ts +39 -0
- package/server/controllers/etrax/apis/create-shipment-request.ts +82 -0
- package/server/controllers/etrax/apis/index.ts +1 -0
- package/server/controllers/etrax/etrax.ts +108 -0
- package/server/controllers/etrax/index.ts +7 -0
- package/server/controllers/etrax/platform-action.ts +32 -0
- package/server/controllers/index.ts +10 -0
- package/server/controllers/lmd-api/decorators.ts +47 -0
- package/server/controllers/lmd-api/index.ts +39 -0
- package/server/engine/connector/index.ts +1 -0
- package/server/engine/connector/lmd-connector.ts +33 -0
- package/server/engine/index.ts +2 -0
- package/server/engine/task/index.ts +1 -0
- package/server/engine/task/lmd-api.ts +64 -0
- package/server/entities/courier-service.ts +44 -0
- package/server/entities/index.ts +6 -0
- package/server/entities/last-mile-delivery.ts +84 -0
- package/server/graphql/index.ts +9 -0
- package/server/graphql/resolvers/courier-service/courier-service-query.ts +54 -0
- package/server/graphql/resolvers/courier-service/create-courier-service.ts +16 -0
- package/server/graphql/resolvers/courier-service/delete-courier-service.ts +13 -0
- package/server/graphql/resolvers/courier-service/delete-courier-services.ts +18 -0
- package/server/graphql/resolvers/courier-service/index.ts +19 -0
- package/server/graphql/resolvers/courier-service/update-courier-service.ts +20 -0
- package/server/graphql/resolvers/courier-service/update-multiple-courier-service.ts +46 -0
- package/server/graphql/resolvers/index.ts +2 -0
- package/server/graphql/resolvers/last-mile-delivery/create-last-mile-delivery.ts +14 -0
- package/server/graphql/resolvers/last-mile-delivery/delete-last-mile-deliveries.ts +26 -0
- package/server/graphql/resolvers/last-mile-delivery/delete-last-mile-delivery.ts +13 -0
- package/server/graphql/resolvers/last-mile-delivery/index.ts +19 -0
- package/server/graphql/resolvers/last-mile-delivery/last-mile-delivery-query.ts +52 -0
- package/server/graphql/resolvers/last-mile-delivery/update-last-mile-delivery.ts +18 -0
- package/server/graphql/resolvers/last-mile-delivery/update-multiple-last-mile-delivery.ts +45 -0
- package/server/graphql/types/courier-service/courier-service-list.ts +8 -0
- package/server/graphql/types/courier-service/courier-service-patch.ts +11 -0
- package/server/graphql/types/courier-service/courier-service.ts +15 -0
- package/server/graphql/types/courier-service/index.ts +21 -0
- package/server/graphql/types/courier-service/new-courier-service.ts +9 -0
- package/server/graphql/types/index.ts +2 -0
- package/server/graphql/types/last-mile-delivery/index.ts +21 -0
- package/server/graphql/types/last-mile-delivery/last-mile-delivery-list.ts +8 -0
- package/server/graphql/types/last-mile-delivery/last-mile-delivery-patch.ts +20 -0
- package/server/graphql/types/last-mile-delivery/last-mile-delivery.ts +25 -0
- package/server/graphql/types/last-mile-delivery/new-last-mile-delivery.ts +18 -0
- package/server/index.ts +9 -0
- package/server/middlewares/index.ts +3 -0
- package/server/migrations/index.ts +9 -0
- package/server/routes.ts +26 -0
- package/tsconfig.json +9 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/integration-lmd",
|
|
3
|
-
"version": "8.0.0
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"main": "dist-server/index.js",
|
|
5
5
|
"browser": "client/index.js",
|
|
6
6
|
"things-factory": true,
|
|
@@ -24,18 +24,18 @@
|
|
|
24
24
|
"migration:create": "node ../../node_modules/typeorm/cli.js migration:create ./server/migrations/migration"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@operato/data-grist": "^8.0.0
|
|
28
|
-
"@things-factory/apptool-ui": "^8.0.0
|
|
29
|
-
"@things-factory/auth-ui": "^8.0.0
|
|
30
|
-
"@things-factory/biz-base": "^8.0.0
|
|
31
|
-
"@things-factory/code-ui": "^8.0.0
|
|
32
|
-
"@things-factory/context-ui": "^8.0.0
|
|
33
|
-
"@things-factory/i18n-base": "^8.0.0
|
|
34
|
-
"@things-factory/integration-ui": "^8.0.0
|
|
35
|
-
"@things-factory/more-ui": "^8.0.0
|
|
36
|
-
"@things-factory/resource-ui": "^8.0.0
|
|
37
|
-
"@things-factory/setting-ui": "^8.0.0
|
|
38
|
-
"@things-factory/shell": "^8.0.0
|
|
27
|
+
"@operato/data-grist": "^8.0.0",
|
|
28
|
+
"@things-factory/apptool-ui": "^8.0.0",
|
|
29
|
+
"@things-factory/auth-ui": "^8.0.0",
|
|
30
|
+
"@things-factory/biz-base": "^8.0.0",
|
|
31
|
+
"@things-factory/code-ui": "^8.0.0",
|
|
32
|
+
"@things-factory/context-ui": "^8.0.0",
|
|
33
|
+
"@things-factory/i18n-base": "^8.0.0",
|
|
34
|
+
"@things-factory/integration-ui": "^8.0.0",
|
|
35
|
+
"@things-factory/more-ui": "^8.0.0",
|
|
36
|
+
"@things-factory/resource-ui": "^8.0.0",
|
|
37
|
+
"@things-factory/setting-ui": "^8.0.0",
|
|
38
|
+
"@things-factory/shell": "^8.0.0",
|
|
39
39
|
"debug": "^4.1.1",
|
|
40
40
|
"node-fetch": "^2.6.0"
|
|
41
41
|
},
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"nock": "^13.0.2",
|
|
50
50
|
"should": "^13.2.3"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "07ef27d272dd9a067a9648ac7013748510556a18"
|
|
53
53
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './shipment-request'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './create-shipment-request'
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import fetch from 'node-fetch'
|
|
2
|
+
|
|
3
|
+
const ENDPOINT = 'http://202.133.101.28:8889/CitylinkService.svc/rest'
|
|
4
|
+
const debug = require('debug')('things-factory:integration-lmd:citylink')
|
|
5
|
+
|
|
6
|
+
export type CitylinkConfig = {
|
|
7
|
+
apiKey: string
|
|
8
|
+
apiSecret: string
|
|
9
|
+
accessToken?: string
|
|
10
|
+
tenantId?: string
|
|
11
|
+
callback?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class Citylink {
|
|
15
|
+
private config: CitylinkConfig
|
|
16
|
+
|
|
17
|
+
constructor(config: CitylinkConfig) {
|
|
18
|
+
this.config = {
|
|
19
|
+
...config
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
buildAuthURL(nonce) {
|
|
24
|
+
const scopes = 'offline_access openid profile email accounting.transactions accounting.settings'
|
|
25
|
+
const { apiKey, callback: redirectUrl } = this.config
|
|
26
|
+
|
|
27
|
+
return `https://login.xero.com/identity/connect/authorize?response_type=code&client_id=${apiKey}&scope=${scopes}&redirect_uri=${redirectUrl}&state=${nonce}`
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async get(path: string, data: any) {
|
|
31
|
+
const { accessToken, tenantId } = this.config
|
|
32
|
+
|
|
33
|
+
const qs = Object.entries(data)
|
|
34
|
+
.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`)
|
|
35
|
+
.join('&')
|
|
36
|
+
|
|
37
|
+
const endpoint = `${ENDPOINT}${path}${qs ? '?' + qs : ''}`
|
|
38
|
+
debug('endpoint', endpoint)
|
|
39
|
+
|
|
40
|
+
const response = await fetch(endpoint, {
|
|
41
|
+
method: 'get',
|
|
42
|
+
headers: {
|
|
43
|
+
accept: 'application/json',
|
|
44
|
+
'Content-Type': 'application/json',
|
|
45
|
+
Authorization: `Bearer ${accessToken}`,
|
|
46
|
+
'xero-tenant-id': tenantId
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw response
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let result = await response.json()
|
|
55
|
+
debug('response result', result)
|
|
56
|
+
result = this.convertStatusCode(result)
|
|
57
|
+
|
|
58
|
+
return result
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async post(path: string, data: any = {}) {
|
|
62
|
+
const { accessToken, tenantId } = this.config
|
|
63
|
+
|
|
64
|
+
debug('data', data)
|
|
65
|
+
|
|
66
|
+
const jsondata = JSON.stringify(data)
|
|
67
|
+
|
|
68
|
+
const endpoint = `${ENDPOINT}${path}`
|
|
69
|
+
debug('endpoint', endpoint)
|
|
70
|
+
|
|
71
|
+
const response = await fetch(endpoint, {
|
|
72
|
+
method: 'post',
|
|
73
|
+
headers: {
|
|
74
|
+
accept: 'application/json',
|
|
75
|
+
'Content-Type': 'application/json',
|
|
76
|
+
Authorization: `Bearer ${accessToken}`,
|
|
77
|
+
'xero-tenant-id': tenantId
|
|
78
|
+
},
|
|
79
|
+
body: jsondata
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
if (!response.ok) {
|
|
83
|
+
throw response
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
let result = await response.json()
|
|
87
|
+
debug('response result', result)
|
|
88
|
+
result = this.convertStatusCode(result)
|
|
89
|
+
|
|
90
|
+
return result
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async put(path: string, data: any = {}) {
|
|
94
|
+
const { accessToken, tenantId } = this.config
|
|
95
|
+
|
|
96
|
+
debug('data', data)
|
|
97
|
+
|
|
98
|
+
const jsondata = JSON.stringify(data)
|
|
99
|
+
|
|
100
|
+
const endpoint = `${ENDPOINT}${path}`
|
|
101
|
+
debug('endpoint', endpoint)
|
|
102
|
+
|
|
103
|
+
const response = await fetch(endpoint, {
|
|
104
|
+
method: 'put',
|
|
105
|
+
headers: {
|
|
106
|
+
accept: 'application/json',
|
|
107
|
+
'Content-Type': 'application/json',
|
|
108
|
+
Authorization: `Bearer ${accessToken}`,
|
|
109
|
+
'xero-tenant-id': tenantId
|
|
110
|
+
},
|
|
111
|
+
body: jsondata
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
if (!response.ok) {
|
|
115
|
+
throw response
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let result = await response.json()
|
|
119
|
+
debug('response result', result)
|
|
120
|
+
result = this.convertStatusCode(result)
|
|
121
|
+
|
|
122
|
+
return result
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async delete(path: string, data: any = {}) {
|
|
126
|
+
const { accessToken, tenantId } = this.config
|
|
127
|
+
|
|
128
|
+
debug('data', data)
|
|
129
|
+
|
|
130
|
+
const jsondata = JSON.stringify(data)
|
|
131
|
+
|
|
132
|
+
const endpoint = `${ENDPOINT}${path}`
|
|
133
|
+
debug('endpoint', endpoint)
|
|
134
|
+
|
|
135
|
+
const response = await fetch(endpoint, {
|
|
136
|
+
method: 'delete',
|
|
137
|
+
headers: {
|
|
138
|
+
accept: 'application/json',
|
|
139
|
+
'Content-Type': 'application/json',
|
|
140
|
+
Authorization: `Bearer ${accessToken}`,
|
|
141
|
+
'xero-tenant-id': tenantId
|
|
142
|
+
},
|
|
143
|
+
body: jsondata
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
throw response
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let result = await response.json()
|
|
151
|
+
debug('response result', result)
|
|
152
|
+
result = this.convertStatusCode(result)
|
|
153
|
+
|
|
154
|
+
return result
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private convertStatusCode(result: Record<string, any>): Record<string, any> {
|
|
158
|
+
result.ok = result.Status.toLowerCase() === 'ok'
|
|
159
|
+
return result
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Citylink } from './citylink'
|
|
2
|
+
|
|
3
|
+
import { config } from '@things-factory/env'
|
|
4
|
+
const citylinkConfig = config.get('lmdIntegrationCitylink', {})
|
|
5
|
+
const { apiKey, apiSecret, callback } = citylinkConfig
|
|
6
|
+
|
|
7
|
+
function substitute(path, obj) {
|
|
8
|
+
var props = []
|
|
9
|
+
var re = /{([^}]+)}/g
|
|
10
|
+
var text
|
|
11
|
+
|
|
12
|
+
while ((text = re.exec(path))) {
|
|
13
|
+
props.push(text[1])
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
var result = path
|
|
17
|
+
props.forEach(prop => {
|
|
18
|
+
let value = obj[prop.trim()]
|
|
19
|
+
result = result.replace(`{${prop}}`, value === undefined ? '' : value)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
return result
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const action = async ({ lastMile, method = 'get', path, request }) => {
|
|
26
|
+
const client = new Citylink({
|
|
27
|
+
apiKey,
|
|
28
|
+
apiSecret,
|
|
29
|
+
accessToken: lastMile.accessToken,
|
|
30
|
+
tenantId: lastMile.accountId,
|
|
31
|
+
callback
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const { resource = {}, payload = {} } = request
|
|
35
|
+
|
|
36
|
+
path = substitute(path, resource)
|
|
37
|
+
|
|
38
|
+
return await client[method](path, payload)
|
|
39
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
export function createShipmentRequest() {
|
|
2
|
+
return {
|
|
3
|
+
method: 'post',
|
|
4
|
+
path: '/rest/delivery.php?api=create',
|
|
5
|
+
denormalize(req) {
|
|
6
|
+
const {
|
|
7
|
+
orderNo,
|
|
8
|
+
clientId,
|
|
9
|
+
clientType,
|
|
10
|
+
clientName,
|
|
11
|
+
transporterId,
|
|
12
|
+
transporterId2,
|
|
13
|
+
name,
|
|
14
|
+
address1,
|
|
15
|
+
address2,
|
|
16
|
+
postCode,
|
|
17
|
+
city,
|
|
18
|
+
state,
|
|
19
|
+
phone,
|
|
20
|
+
email,
|
|
21
|
+
attentionTo,
|
|
22
|
+
quantity,
|
|
23
|
+
pickupName,
|
|
24
|
+
pickupAddress1,
|
|
25
|
+
pickupAddress2,
|
|
26
|
+
pickupPostcode,
|
|
27
|
+
pickupCity,
|
|
28
|
+
pickupState,
|
|
29
|
+
pickupPhone,
|
|
30
|
+
pickupEmail
|
|
31
|
+
} = req
|
|
32
|
+
|
|
33
|
+
//Creta a parcel array, but only 1 needed
|
|
34
|
+
const parcels = []
|
|
35
|
+
parcels.push({
|
|
36
|
+
desc: '',
|
|
37
|
+
qty: quantity,
|
|
38
|
+
weight: 0,
|
|
39
|
+
width: 0,
|
|
40
|
+
depth: 0,
|
|
41
|
+
height: 0
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
OrderNo: orderNo,
|
|
46
|
+
ExtOrderNo: orderNo,
|
|
47
|
+
ClientId: clientId,
|
|
48
|
+
ClientType: clientType,
|
|
49
|
+
ClientName: clientName,
|
|
50
|
+
TransporterId: transporterId,
|
|
51
|
+
TransporterId2: transporterId2,
|
|
52
|
+
PickUpAttnTo: pickupName,
|
|
53
|
+
PickUpAddr1: pickupAddress1,
|
|
54
|
+
PickUpAddr2: pickupAddress2,
|
|
55
|
+
PickUpPostCode: pickupPostcode,
|
|
56
|
+
PickUpCity: pickupCity,
|
|
57
|
+
PickUpState: pickupState,
|
|
58
|
+
PickUpPhone: pickupPhone,
|
|
59
|
+
PickUpEmail: pickupEmail,
|
|
60
|
+
DeliverToAttnTo: attentionTo,
|
|
61
|
+
DeliverToAddr1: address1,
|
|
62
|
+
DeliverToAddr2: address2,
|
|
63
|
+
DeliverToPostCode: postCode,
|
|
64
|
+
DeliverToCity: city,
|
|
65
|
+
DeliverToState: state,
|
|
66
|
+
DeliverToPhone: phone,
|
|
67
|
+
DeliverToEmail: email,
|
|
68
|
+
TotalWeight: 0,
|
|
69
|
+
TotalVolume: 0,
|
|
70
|
+
CollectionType: 'Prepaid',
|
|
71
|
+
CollectionCurrency: 'MYR',
|
|
72
|
+
FreightType: 0,
|
|
73
|
+
GoodsType: 0,
|
|
74
|
+
DeliveryMethod: 0,
|
|
75
|
+
Parcels: parcels
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
normalize(res) {
|
|
79
|
+
return res
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './create-shipment-request'
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import crypto from 'crypto'
|
|
2
|
+
import fetch from 'node-fetch'
|
|
3
|
+
|
|
4
|
+
import { getRepository } from '@things-factory/shell'
|
|
5
|
+
|
|
6
|
+
import { LastMileDelivery } from '../../entities'
|
|
7
|
+
|
|
8
|
+
const ENDPOINT = 'http://ets.sntglobal.com'
|
|
9
|
+
//const ENDPOINT = 'https://localhost:44344'
|
|
10
|
+
const debug = require('debug')('things-factory:integration-lmd:etrax')
|
|
11
|
+
|
|
12
|
+
export type EtraxAuth = {
|
|
13
|
+
SourceID: string
|
|
14
|
+
TimeStamp: string
|
|
15
|
+
Token: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class Etrax {
|
|
19
|
+
private _auth: EtraxAuth
|
|
20
|
+
private _secretKey: string
|
|
21
|
+
|
|
22
|
+
constructor(customerCode: string) {
|
|
23
|
+
this._auth = {
|
|
24
|
+
SourceID: '',
|
|
25
|
+
TimeStamp: '',
|
|
26
|
+
Token: ''
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this._auth.SourceID = customerCode
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async post(path: string, data: any) {
|
|
33
|
+
const timestamp = this.getTimestamp()
|
|
34
|
+
let apiKey = 'CREATE_DELIVERY'
|
|
35
|
+
|
|
36
|
+
//Path to APIKEY
|
|
37
|
+
if (path.includes('create-delivery')) {
|
|
38
|
+
apiKey = 'CREATE_DELIVERY'
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
await this.generateToken(timestamp, apiKey)
|
|
42
|
+
|
|
43
|
+
let finalData = {
|
|
44
|
+
...data,
|
|
45
|
+
...this._auth
|
|
46
|
+
}
|
|
47
|
+
let jsonData = JSON.stringify(finalData)
|
|
48
|
+
|
|
49
|
+
debug(ENDPOINT + path)
|
|
50
|
+
debug(jsonData)
|
|
51
|
+
|
|
52
|
+
const response = await fetch(ENDPOINT + path, {
|
|
53
|
+
method: 'post',
|
|
54
|
+
headers: {
|
|
55
|
+
accept: 'application/json',
|
|
56
|
+
'Content-Type': 'application/json'
|
|
57
|
+
},
|
|
58
|
+
body: jsonData
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
throw response
|
|
63
|
+
}
|
|
64
|
+
let result = await response.json()
|
|
65
|
+
debug('response result', result)
|
|
66
|
+
//result = this.convertStatusCode(result)
|
|
67
|
+
|
|
68
|
+
return result
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async validateToken(receivedToken: string, apiKey: string, timestamp: string) {
|
|
72
|
+
if (receivedToken !== (await this.generateToken(timestamp, apiKey))) {
|
|
73
|
+
return false
|
|
74
|
+
} else {
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async generateToken(timestamp: string, apiKey: string) {
|
|
80
|
+
const lmdRepo = getRepository(LastMileDelivery)
|
|
81
|
+
const lmd = await lmdRepo.findOne({ where: { clientName: this._auth.SourceID } })
|
|
82
|
+
const secretKey = lmd.secretKey
|
|
83
|
+
|
|
84
|
+
const temp = apiKey + timestamp + secretKey
|
|
85
|
+
const md5 = this.generateMD5(temp)
|
|
86
|
+
this._auth.TimeStamp = timestamp
|
|
87
|
+
this._auth.Token = md5
|
|
88
|
+
|
|
89
|
+
return md5.toString().toUpperCase()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
getTimestamp() {
|
|
93
|
+
const pad = (n: number) => (n < 10 ? '0' + n : n)
|
|
94
|
+
const date = new Date()
|
|
95
|
+
const timestamp =
|
|
96
|
+
date.getFullYear().toString() +
|
|
97
|
+
pad(date.getMonth() + 1) +
|
|
98
|
+
pad(date.getDate()) +
|
|
99
|
+
pad(date.getHours()) +
|
|
100
|
+
pad(date.getMinutes()) +
|
|
101
|
+
pad(date.getSeconds())
|
|
102
|
+
return timestamp
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
generateMD5(input: string) {
|
|
106
|
+
return crypto.createHash('md5').update(input).digest('hex')
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Etrax } from './etrax'
|
|
2
|
+
|
|
3
|
+
function substitute(path, obj) {
|
|
4
|
+
var props = []
|
|
5
|
+
var re = /{([^}]+)}/g
|
|
6
|
+
var text
|
|
7
|
+
|
|
8
|
+
while ((text = re.exec(path))) {
|
|
9
|
+
props.push(text[1])
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
var result = path
|
|
13
|
+
props.forEach(prop => {
|
|
14
|
+
let value = obj[prop.trim()]
|
|
15
|
+
result = result.replace(`{${prop}}`, value === undefined ? '' : value)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
return result
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const action = async ({ lastMile, method = 'get', path, request }) => {
|
|
22
|
+
//LMD ClientName Store SourceID
|
|
23
|
+
const customerCode = lastMile.clientName
|
|
24
|
+
|
|
25
|
+
const client = new Etrax(customerCode)
|
|
26
|
+
|
|
27
|
+
//const { resource = {}, payload = {} } = request
|
|
28
|
+
|
|
29
|
+
//path = substitute(path, resource)
|
|
30
|
+
|
|
31
|
+
return await client[method](path, request)
|
|
32
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import Debug from 'debug'
|
|
2
|
+
const debug = Debug('things-factory:integration-lmd:lmd-api-decorator')
|
|
3
|
+
|
|
4
|
+
import { LastMileDelivery } from '../../entities'
|
|
5
|
+
|
|
6
|
+
const NOOP = v => v
|
|
7
|
+
|
|
8
|
+
export const api = (target: Object, property: string, descriptor: TypedPropertyDescriptor<any>): any => {
|
|
9
|
+
const method = descriptor.value
|
|
10
|
+
|
|
11
|
+
descriptor.value = async function (lastMile: LastMileDelivery, request) {
|
|
12
|
+
const LastMileAPI = this
|
|
13
|
+
|
|
14
|
+
var { platform } = lastMile
|
|
15
|
+
|
|
16
|
+
var { action: platformAction, apis } = LastMileAPI.getPlatform(platform)
|
|
17
|
+
|
|
18
|
+
var m = apis[method.name]
|
|
19
|
+
if (!m) {
|
|
20
|
+
throw Error(`Last Mile Delivery Platform '${platform}' doesn't have API ${method.name}`)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
var {
|
|
24
|
+
path,
|
|
25
|
+
method: httpMethod = 'post',
|
|
26
|
+
denormalize = NOOP,
|
|
27
|
+
normalize = NOOP,
|
|
28
|
+
action = platformAction
|
|
29
|
+
} = m.apply(this, [request])
|
|
30
|
+
|
|
31
|
+
var denormalized = await denormalize(request || {}, { lastMile })
|
|
32
|
+
debug('request', denormalized)
|
|
33
|
+
|
|
34
|
+
var response = await action.apply(this, [
|
|
35
|
+
{ lastMile, method: httpMethod, path, request: denormalized, platformAction }
|
|
36
|
+
])
|
|
37
|
+
|
|
38
|
+
debug('response', response)
|
|
39
|
+
/*if (!response.ok) {
|
|
40
|
+
throw response
|
|
41
|
+
}*/
|
|
42
|
+
|
|
43
|
+
return await normalize(response, { lastMile })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return descriptor
|
|
47
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { getRepository } from '@things-factory/shell'
|
|
2
|
+
|
|
3
|
+
import { LastMileDelivery } from '../../entities'
|
|
4
|
+
import { api } from './decorators'
|
|
5
|
+
|
|
6
|
+
export const LAST_MILE_STATUS = {
|
|
7
|
+
ACTIVE: 'active',
|
|
8
|
+
INACTIVE: 'inactive',
|
|
9
|
+
TERMINATED: 'terminated'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class LastMileAPI {
|
|
13
|
+
static platforms = {}
|
|
14
|
+
|
|
15
|
+
static registerPlatform(name, action, apis) {
|
|
16
|
+
LastMileAPI.platforms[name] = {
|
|
17
|
+
action,
|
|
18
|
+
apis
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static getPlatform(name) {
|
|
23
|
+
return LastMileAPI.platforms[name]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static async getAccounting(id) {
|
|
27
|
+
const repository = getRepository(LastMileDelivery)
|
|
28
|
+
return await repository.findOne({
|
|
29
|
+
where: { id },
|
|
30
|
+
relations: ['domain']
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@api
|
|
35
|
+
static createShipmentRequest(lastMile, req): any {}
|
|
36
|
+
|
|
37
|
+
@api
|
|
38
|
+
static getShipmentTracking(lastMile, req): any {}
|
|
39
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './lmd-connector'
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ConnectionManager, Connector } from '@things-factory/integration-base'
|
|
2
|
+
|
|
3
|
+
export class LastMileConnector implements Connector {
|
|
4
|
+
async ready(connectionConfigs) {
|
|
5
|
+
await Promise.all(connectionConfigs.map(this.connect))
|
|
6
|
+
|
|
7
|
+
ConnectionManager.logger.info('lmd-connector connections are ready')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async connect(connection) {
|
|
11
|
+
const { domain, name, endpoint } = connection
|
|
12
|
+
|
|
13
|
+
ConnectionManager.addConnectionInstance(connection, { ...connection })
|
|
14
|
+
|
|
15
|
+
ConnectionManager.logger.info(`lmd-connector connection(${name}:${connection.endpoint}) is connected`)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async disconnect(connection) {
|
|
19
|
+
ConnectionManager.removeConnectionInstance(connection)
|
|
20
|
+
|
|
21
|
+
ConnectionManager.logger.info(`lmd-connector connection(${connection.name}) is disconnected`)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
get parameterSpec() {
|
|
25
|
+
return []
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
get taskPrefixes() {
|
|
29
|
+
return ['lmd']
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
ConnectionManager.registerConnector('lmd-connector', new LastMileConnector())
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './lmd-api'
|