@things-factory/integration-sellercraft 8.0.0-beta.8 → 8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +14 -14
- package/server/constants/index.ts +2 -0
- package/server/constants/order-status-mapping.ts +28 -0
- package/server/constants/platform.ts +6 -0
- package/server/controllers/index.ts +5 -0
- package/server/controllers/sellercraft/apis/add-inbound-order.ts +50 -0
- package/server/controllers/sellercraft/apis/echo.ts +14 -0
- package/server/controllers/sellercraft/apis/fetch-order-document.ts +18 -0
- package/server/controllers/sellercraft/apis/index.ts +8 -0
- package/server/controllers/sellercraft/apis/initiate-order-document.ts +17 -0
- package/server/controllers/sellercraft/apis/initiate-order-shipment.ts +29 -0
- package/server/controllers/sellercraft/apis/pack-marketplace-order.ts +35 -0
- package/server/controllers/sellercraft/apis/update-marketplace-order.ts +14 -0
- package/server/controllers/sellercraft/apis/update-product.ts +21 -0
- package/server/controllers/sellercraft/index.ts +7 -0
- package/server/controllers/sellercraft/platform-action.ts +39 -0
- package/server/controllers/sellercraft/sellercraft.ts +109 -0
- package/server/controllers/sellercraft-api/decorators.ts +52 -0
- package/server/controllers/sellercraft-api/index.ts +51 -0
- package/server/controllers/sellercraft-api/types.ts +0 -0
- package/server/controllers/sellercraft-channel-integration/apis/echo.ts +14 -0
- package/server/controllers/sellercraft-channel-integration/apis/index.ts +6 -0
- package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-categories.ts +37 -0
- package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-category-attributes.ts +65 -0
- package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-order-package.ts +62 -0
- package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-order.ts +92 -0
- package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-product.ts +97 -0
- package/server/controllers/sellercraft-channel-integration/index.ts +7 -0
- package/server/controllers/sellercraft-channel-integration/platform-action.ts +39 -0
- package/server/controllers/sellercraft-channel-integration/sellercraft-channel-integration.ts +115 -0
- package/server/controllers/sellercraft-channel-integration-api/decorators.ts +45 -0
- package/server/controllers/sellercraft-channel-integration-api/index.ts +45 -0
- package/server/controllers/sellercraft-channel-integration-api/types.ts +0 -0
- package/server/index.ts +7 -0
- package/server/middlewares/index.ts +3 -0
- package/server/migrations/index.ts +9 -0
- package/server/routers/sellercraft-router.ts +326 -0
- package/server/routes.ts +32 -0
- package/server/service/index.ts +23 -0
- package/server/service/marketplace-channel/index.ts +6 -0
- package/server/service/marketplace-channel/marketplace-channel-order-mutation.ts +456 -0
- package/server/service/marketplace-channel/marketplace-channel-product-mutation.ts +282 -0
- package/server/service/marketplace-channel/marketplace-channel.ts +76 -0
- package/server/service/sellercraft/index.ts +6 -0
- package/server/service/sellercraft/sellercraft-mutation.ts +126 -0
- package/server/service/sellercraft/sellercraft-query.ts +43 -0
- package/server/service/sellercraft/sellercraft-type.ts +54 -0
- package/server/service/sellercraft/sellercraft.ts +96 -0
- package/server/utils/tokencraft-util.ts +60 -0
- package/tsconfig.json +9 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
/* https://docs.sellercraft.co/docs/api-integrations/b3A6MjQzODUxMTE-ingest-channel-category-attributes */
|
2
|
+
|
3
|
+
export function ingestChannelCategoryAttributes() {
|
4
|
+
return {
|
5
|
+
method: 'post',
|
6
|
+
path: '/channel/ingest/category/attribute',
|
7
|
+
denormalize(req) {
|
8
|
+
let { channelAttributeKeys, scAttributeKeys } = req
|
9
|
+
|
10
|
+
let channel_attribute_keys = channelAttributeKeys.map(e => {
|
11
|
+
const {
|
12
|
+
channelCode: channel_code,
|
13
|
+
channelCountry: channel_country,
|
14
|
+
nativeCategoryId: native_category_id,
|
15
|
+
attributeKey: attribute_key,
|
16
|
+
valueType: value_type,
|
17
|
+
valueOptions: value_options,
|
18
|
+
displayName: display_name,
|
19
|
+
isCore: is_core,
|
20
|
+
isSearchProp: is_search_prop,
|
21
|
+
isOptional: is_optional
|
22
|
+
} = e
|
23
|
+
|
24
|
+
return {
|
25
|
+
channel_code,
|
26
|
+
channel_country,
|
27
|
+
native_category_id,
|
28
|
+
attribute_key,
|
29
|
+
value_type,
|
30
|
+
value_options,
|
31
|
+
display_name,
|
32
|
+
is_core,
|
33
|
+
is_search_prop,
|
34
|
+
is_optional
|
35
|
+
}
|
36
|
+
})
|
37
|
+
|
38
|
+
let sc_attribute_keys = scAttributeKeys.map(e => {
|
39
|
+
const {
|
40
|
+
channelCode: channel_code,
|
41
|
+
channelCountry: channel_country,
|
42
|
+
attributeKey: attribute_key,
|
43
|
+
displayName: display_name
|
44
|
+
} = e
|
45
|
+
|
46
|
+
return {
|
47
|
+
channel_code,
|
48
|
+
channel_country,
|
49
|
+
attribute_key,
|
50
|
+
display_name
|
51
|
+
}
|
52
|
+
})
|
53
|
+
|
54
|
+
return {
|
55
|
+
payload: {
|
56
|
+
channel_attribute_keys,
|
57
|
+
sc_attribute_keys
|
58
|
+
}
|
59
|
+
}
|
60
|
+
},
|
61
|
+
normalize(res) {
|
62
|
+
return res
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-order-package.ts
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
/* https://docs.sellercraft.co/docs/api-integrations/b3A6MjQzODUxMTQ-ingest-channel-order-package */
|
2
|
+
|
3
|
+
export function ingestChannelOrderPackage() {
|
4
|
+
return {
|
5
|
+
method: 'post',
|
6
|
+
path: '/channel/ingest/order/package',
|
7
|
+
denormalize(req) {
|
8
|
+
let {
|
9
|
+
channelShopId: channel_shop_id,
|
10
|
+
nativeOrderId: native_order_id,
|
11
|
+
nativePackageId: native_package_id,
|
12
|
+
shippingTrackingCode: shipping_tracking_code,
|
13
|
+
shippingTypeValue: shipping_type_value,
|
14
|
+
warehouseCode: warehouse_code,
|
15
|
+
shipper,
|
16
|
+
documents,
|
17
|
+
shipperLastMile: shipper_last_mile,
|
18
|
+
orderItemIds: order_item_ids
|
19
|
+
} = req
|
20
|
+
|
21
|
+
documents = documents.map(e => {
|
22
|
+
const { fileTypeValue: file_type_value, mimeType: mime_type, file } = e
|
23
|
+
|
24
|
+
return {
|
25
|
+
file_type_value,
|
26
|
+
mime_type,
|
27
|
+
file
|
28
|
+
}
|
29
|
+
})
|
30
|
+
|
31
|
+
shipper = {
|
32
|
+
is_cod_supported: shipper.isCodSupported,
|
33
|
+
name: shipper.name
|
34
|
+
}
|
35
|
+
|
36
|
+
shipper_last_mile = {
|
37
|
+
is_cod_supported: shipper_last_mile.isCodSupported,
|
38
|
+
name: shipper_last_mile.name
|
39
|
+
}
|
40
|
+
|
41
|
+
let newOrderPackage: any = {
|
42
|
+
channel_shop_id,
|
43
|
+
native_order_id,
|
44
|
+
native_package_id: native_package_id.toString(),
|
45
|
+
shipping_tracking_code,
|
46
|
+
shipping_type_value,
|
47
|
+
warehouse_code,
|
48
|
+
shipper,
|
49
|
+
documents,
|
50
|
+
shipper_last_mile,
|
51
|
+
order_item_ids
|
52
|
+
}
|
53
|
+
|
54
|
+
return {
|
55
|
+
payload: { ...newOrderPackage }
|
56
|
+
}
|
57
|
+
},
|
58
|
+
normalize(res) {
|
59
|
+
return res
|
60
|
+
}
|
61
|
+
}
|
62
|
+
}
|
@@ -0,0 +1,92 @@
|
|
1
|
+
/* https://docs.sellercraft.co/docs/api-integrations/b3A6MjQzODQ4OTg-ingest-channel-order */
|
2
|
+
|
3
|
+
import { ORDER_STATUS } from '../../../constants'
|
4
|
+
|
5
|
+
export function ingestChannelOrder() {
|
6
|
+
return {
|
7
|
+
method: 'post',
|
8
|
+
path: '/channel/ingest/order',
|
9
|
+
denormalize(req) {
|
10
|
+
const { orders } = req
|
11
|
+
|
12
|
+
let newOrders = orders.map(order => {
|
13
|
+
return {
|
14
|
+
organisation_id: order.organisationId,
|
15
|
+
channel_shop_id: order.channelShopId,
|
16
|
+
native_order_id: order.id.toString(),
|
17
|
+
native_order_display_id: order?.sellercraftDisplayOrderNo,
|
18
|
+
ordered_at_date: new Date(order.createdAt).toISOString().slice(0, -5) + 'Z',
|
19
|
+
ordered_at_time: new Date(order.createdAt).toISOString().slice(0, -5) + 'Z',
|
20
|
+
updated_at_date: new Date(order.updatedAt).toISOString().slice(0, -5) + 'Z',
|
21
|
+
updated_at_time: new Date(order.updatedAt).toISOString().slice(0, -5) + 'Z',
|
22
|
+
customer_first_name: order.custFirstName,
|
23
|
+
customer_last_name: order.custLastName,
|
24
|
+
charges: order.charges.map(charge => {
|
25
|
+
return {
|
26
|
+
charge_type_value: charge.name,
|
27
|
+
amount_gross: parseFloat(charge.grossAmount) || 0,
|
28
|
+
amount_nett: parseFloat(charge.nettAmount) || 0
|
29
|
+
}
|
30
|
+
}),
|
31
|
+
address_shipping: order?.shipAddress1 ? {
|
32
|
+
first_name: order.shipFirstName,
|
33
|
+
last_name: order.shipLastName,
|
34
|
+
line_1: order.shipAddress1,
|
35
|
+
line_2: order.shipAddress2,
|
36
|
+
line_3: order.shipAddress3,
|
37
|
+
line_4: order.shipAddress4,
|
38
|
+
line_5: order.shipAddress5,
|
39
|
+
city: order.shipCity,
|
40
|
+
postal_code: order.shipPostalCode,
|
41
|
+
country: order.shipCountry,
|
42
|
+
phone_1: order.shipPhone1,
|
43
|
+
phone_2: order.shipPhone2
|
44
|
+
} : null,
|
45
|
+
address_billing: {
|
46
|
+
first_name: order.billFirstName,
|
47
|
+
last_name: order.billLastName,
|
48
|
+
line_1: order.billAddress1,
|
49
|
+
line_2: order.billAddress2,
|
50
|
+
line_3: order.billAddress3,
|
51
|
+
line_4: order.billAddress4,
|
52
|
+
line_5: order.billAddress5,
|
53
|
+
city: order.billCity,
|
54
|
+
postal_code: order.billPostalCode,
|
55
|
+
country: order.billCountry,
|
56
|
+
phone_1: order.billPhone1,
|
57
|
+
phone_2: order.billPhone2
|
58
|
+
},
|
59
|
+
order_items: order.mappedOrderItems.map(orderItem => {
|
60
|
+
return {
|
61
|
+
native_item_id: orderItem.id.toString(),
|
62
|
+
native_variant_id: orderItem.variationId.toString(),
|
63
|
+
currency_code: orderItem.currency,
|
64
|
+
ordered_at_date: new Date(order.createdAt).toISOString().slice(0, -5) + 'Z',
|
65
|
+
ordered_at_time: new Date(order.createdAt).toISOString().slice(0, -5) + 'Z',
|
66
|
+
updated_at_date: new Date(order.updatedAt).toISOString().slice(0, -5) + 'Z',
|
67
|
+
updated_at_time: new Date(order.updatedAt).toISOString().slice(0, -5) + 'Z',
|
68
|
+
sla_expires_at: orderItem?.slaExpiresAt ? new Date(orderItem.slaExpiresAt).toISOString() : null,
|
69
|
+
charges: orderItem.charges.map(charge => {
|
70
|
+
return {
|
71
|
+
charge_type_value: charge.name,
|
72
|
+
amount_gross: parseFloat(charge.grossAmount) || 0,
|
73
|
+
amount_nett: parseFloat(charge.nettAmount) || 0
|
74
|
+
}
|
75
|
+
}),
|
76
|
+
order_status_value: ORDER_STATUS[`${order.status}`]
|
77
|
+
}
|
78
|
+
}),
|
79
|
+
seller_logistics: order?.isSOF,
|
80
|
+
shipping_type: order?.shipAddress1 ? 'DROP_SHIPPING' : 'SELF_PICKUP'
|
81
|
+
}
|
82
|
+
})
|
83
|
+
|
84
|
+
return {
|
85
|
+
payload: [...newOrders]
|
86
|
+
}
|
87
|
+
},
|
88
|
+
normalize(res) {
|
89
|
+
return res
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
@@ -0,0 +1,97 @@
|
|
1
|
+
/* https://docs.sellercraft.co/docs/api-integrations/b3A6MTY4NjQxODU-initiate-order-shipment */
|
2
|
+
|
3
|
+
export function ingestChannelProduct() {
|
4
|
+
return {
|
5
|
+
method: 'post',
|
6
|
+
path: '/channel/ingest/product',
|
7
|
+
denormalize(req) {
|
8
|
+
const { products } = req
|
9
|
+
|
10
|
+
let newProducts = products.map(product => {
|
11
|
+
let productVariations: any[] = product.variations.map(variant => {
|
12
|
+
return {
|
13
|
+
seller_sku: variant.variationSku,
|
14
|
+
native_variant_id: variant.variationId.toString(),
|
15
|
+
label: variant.name,
|
16
|
+
is_enabled: variant.isEnabled || true,
|
17
|
+
is_sellable: variant.isSellable || true,
|
18
|
+
is_deleted: false, // default
|
19
|
+
variant_attributes: variant?.attributes
|
20
|
+
? variant?.attributes.map(attribute => {
|
21
|
+
return {
|
22
|
+
...attribute,
|
23
|
+
native_attribute_id: attribute?.native_attribute_id?.toString()
|
24
|
+
}
|
25
|
+
})
|
26
|
+
: [],
|
27
|
+
stock_locked: variant?.stockLocked ? variant.stockLocked : 0,
|
28
|
+
native_stock_reported: variant?.stockReported ? variant.stockReported : 0,
|
29
|
+
price_full: variant.fullPrice || 0,
|
30
|
+
price_discounted: variant.priceDiscounted || variant.fullPrice || 0,
|
31
|
+
inventory_products: variant?.inventoryProducts
|
32
|
+
? variant.inventoryProducts.map(inventoryProduct => {
|
33
|
+
return {
|
34
|
+
quantity: inventoryProduct.qty || 0,
|
35
|
+
name: inventoryProduct.name,
|
36
|
+
inventory_sku: inventoryProduct.sku,
|
37
|
+
product_versions: inventoryProduct?.productVersions
|
38
|
+
? inventoryProduct.productVersions.map(productVersion => {
|
39
|
+
return {
|
40
|
+
label: productVersion.label,
|
41
|
+
package_content: productVersion.packageContent,
|
42
|
+
package_length_mm: productVersion?.packageLengthMM ? productVersion.packageLengthMM : 0,
|
43
|
+
package_width_mm: productVersion?.packageWidthMM ? productVersion.packageWidthMM : 0,
|
44
|
+
package_height_mm: productVersion?.packageHeightMM ? productVersion.packageHeightMM : 0,
|
45
|
+
package_weight_gram: productVersion?.packageWeightGram
|
46
|
+
? productVersion.packageWeightGram
|
47
|
+
: 0,
|
48
|
+
stock_available: productVersion.qty || 0
|
49
|
+
}
|
50
|
+
})
|
51
|
+
: []
|
52
|
+
}
|
53
|
+
})
|
54
|
+
: [],
|
55
|
+
extra_metadata: variant?.extraMetadata
|
56
|
+
}
|
57
|
+
})
|
58
|
+
|
59
|
+
return {
|
60
|
+
organisation_id: product.organisationId,
|
61
|
+
channel_shop_id: product.channelShopId,
|
62
|
+
channel_code: product.channelCode,
|
63
|
+
channel_country: product.channelCountry,
|
64
|
+
native_category_id: product.categoryId.toString(),
|
65
|
+
native_product_id: product.productId.toString(),
|
66
|
+
label: product.name,
|
67
|
+
brand: product.brand || '',
|
68
|
+
is_verified: product.isVerified || true,
|
69
|
+
flexible_attributes:
|
70
|
+
product.channelCode == 'WCM' || product.channelCode == 'MGT' || product.channelCode == 'SPF' ? true : false, // add channels that do not support category_attributes ingestion
|
71
|
+
images:
|
72
|
+
product?.images?.map(image => {
|
73
|
+
return {
|
74
|
+
file_url: image.url
|
75
|
+
}
|
76
|
+
}) || [],
|
77
|
+
product_attributes:
|
78
|
+
product?.attributes?.map(attribute => {
|
79
|
+
return {
|
80
|
+
...attribute,
|
81
|
+
native_attribute_id: attribute.native_attribute_id.toString()
|
82
|
+
}
|
83
|
+
}) || [],
|
84
|
+
variants: productVariations,
|
85
|
+
has_all_variants: product.channelCode == 'MGT' ? false : true
|
86
|
+
}
|
87
|
+
})
|
88
|
+
|
89
|
+
return {
|
90
|
+
payload: [...newProducts]
|
91
|
+
}
|
92
|
+
},
|
93
|
+
normalize(res) {
|
94
|
+
return res
|
95
|
+
}
|
96
|
+
}
|
97
|
+
}
|
@@ -0,0 +1,7 @@
|
|
1
|
+
import { SellercraftChannelIntegrationAPI } from '../sellercraft-channel-integration-api'
|
2
|
+
import * as APIS from './apis'
|
3
|
+
import { action } from './platform-action'
|
4
|
+
|
5
|
+
export * from './sellercraft-channel-integration'
|
6
|
+
|
7
|
+
SellercraftChannelIntegrationAPI.registerPlatform('sellercraftChannelIntegration', action, APIS)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
import { config } from '@things-factory/env'
|
2
|
+
|
3
|
+
import { SellercraftChannelIntegration } from './sellercraft-channel-integration'
|
4
|
+
|
5
|
+
const sellercraftConfig = config.get('sellercraftChannelIntegrationConfig', {})
|
6
|
+
const { apiKey } = sellercraftConfig
|
7
|
+
|
8
|
+
function substitute(path, obj) {
|
9
|
+
var props = []
|
10
|
+
var re = /{([^}]+)}/g
|
11
|
+
var text
|
12
|
+
|
13
|
+
while ((text = re.exec(path))) {
|
14
|
+
props.push(text[1])
|
15
|
+
}
|
16
|
+
|
17
|
+
var result = path
|
18
|
+
props.forEach(prop => {
|
19
|
+
let value = obj[prop.trim()]
|
20
|
+
result = result.replace(`{${prop}}`, value === undefined ? '' : value)
|
21
|
+
})
|
22
|
+
|
23
|
+
return result
|
24
|
+
}
|
25
|
+
|
26
|
+
export const action = async ({ method = 'get', path, request }) => {
|
27
|
+
const client = new SellercraftChannelIntegration({ apiKey })
|
28
|
+
|
29
|
+
const { resource = {}, payload = {} } = request
|
30
|
+
|
31
|
+
path = substitute(path, resource)
|
32
|
+
|
33
|
+
var response = await client[method](path, payload)
|
34
|
+
if (response.errors) {
|
35
|
+
throw response
|
36
|
+
}
|
37
|
+
|
38
|
+
return response
|
39
|
+
}
|
@@ -0,0 +1,115 @@
|
|
1
|
+
import fetch from 'node-fetch'
|
2
|
+
import { v4 as uuidv4 } from 'uuid'
|
3
|
+
import { createPayloadLog } from '@things-factory/integration-base'
|
4
|
+
|
5
|
+
const debug = require('debug')('things-factory:integration-sellercraft:sellercraft')
|
6
|
+
|
7
|
+
export type SellercraftConfig = {
|
8
|
+
apiKey: string
|
9
|
+
}
|
10
|
+
|
11
|
+
export class SellercraftChannelIntegration {
|
12
|
+
private config: SellercraftConfig
|
13
|
+
|
14
|
+
constructor(config: SellercraftConfig) {
|
15
|
+
this.config = {
|
16
|
+
...config
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
20
|
+
getBaseUrl() {
|
21
|
+
return `https://open.sellercraft.co/v1`
|
22
|
+
}
|
23
|
+
|
24
|
+
generateRequestId() {
|
25
|
+
return uuidv4()
|
26
|
+
}
|
27
|
+
|
28
|
+
async post(path: string, data: any = {}) {
|
29
|
+
const { apiKey } = this.config
|
30
|
+
|
31
|
+
debug('data', data)
|
32
|
+
|
33
|
+
const jsondata = JSON.stringify(data)
|
34
|
+
const requestId: string = this.generateRequestId()
|
35
|
+
const fullPath: string = `${this.getBaseUrl()}${path}`
|
36
|
+
|
37
|
+
const response: any = await fetch(fullPath, {
|
38
|
+
method: 'post',
|
39
|
+
headers: {
|
40
|
+
'Content-Type': 'application/json',
|
41
|
+
request_id: requestId,
|
42
|
+
'X-Api-Key': apiKey
|
43
|
+
},
|
44
|
+
body: jsondata
|
45
|
+
})
|
46
|
+
|
47
|
+
const result = await response.json()
|
48
|
+
try {
|
49
|
+
createPayloadLog(JSON.parse(jsondata)[0]?.channel_shop_id || requestId, fullPath, jsondata, result, {
|
50
|
+
state: { domain: null }
|
51
|
+
})
|
52
|
+
} catch (e) {}
|
53
|
+
if (response.ok) {
|
54
|
+
return result
|
55
|
+
} else {
|
56
|
+
throw new Error(`(${response.status}) ${result.detail}`)
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
async put(path: string, data: any = {}) {
|
61
|
+
const { apiKey } = this.config
|
62
|
+
|
63
|
+
const jsondata = JSON.stringify(data)
|
64
|
+
const requestId: string = this.generateRequestId()
|
65
|
+
const fullPath: string = `${this.getBaseUrl()}${path}`
|
66
|
+
debug('data', data)
|
67
|
+
|
68
|
+
const response: any = await fetch(fullPath, {
|
69
|
+
method: 'put',
|
70
|
+
headers: {
|
71
|
+
'Content-Type': 'application/json',
|
72
|
+
request_id: requestId,
|
73
|
+
'x-api-key': apiKey
|
74
|
+
},
|
75
|
+
body: jsondata
|
76
|
+
})
|
77
|
+
|
78
|
+
if (response.ok) {
|
79
|
+
return await response.json()
|
80
|
+
} else {
|
81
|
+
const result = await response.json()
|
82
|
+
throw new Error(`(${response.status}) ${result.message}`)
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
async get(path: string, data: any = {}) {
|
87
|
+
const { apiKey } = this.config
|
88
|
+
|
89
|
+
const qs = Object.entries(data)
|
90
|
+
.map(([k, v]) => `${k}=${encodeURIComponent(String(v))}`)
|
91
|
+
.join('&')
|
92
|
+
|
93
|
+
const fullPath: string = `${this.getBaseUrl()}${path}`
|
94
|
+
const endpoint = `${fullPath}${qs ? '?' + qs : ''}`
|
95
|
+
debug('endpoint', endpoint)
|
96
|
+
|
97
|
+
const requestId: string = this.generateRequestId()
|
98
|
+
|
99
|
+
const response: any = await fetch(endpoint, {
|
100
|
+
method: 'get',
|
101
|
+
headers: {
|
102
|
+
'Content-Type': 'application/json',
|
103
|
+
request_id: requestId,
|
104
|
+
'x-api-key': apiKey
|
105
|
+
}
|
106
|
+
})
|
107
|
+
|
108
|
+
if (response.ok) {
|
109
|
+
return await response.json()
|
110
|
+
} else {
|
111
|
+
const result = await response.json()
|
112
|
+
throw new Error(`(${response.status}) ${result.message}`)
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import Debug from 'debug'
|
2
|
+
|
3
|
+
import { Sellercraft } from '../../service'
|
4
|
+
|
5
|
+
const debug = Debug('things-factory:integration-marketplace:store-api-decorator')
|
6
|
+
|
7
|
+
const NOOP = v => v
|
8
|
+
|
9
|
+
export const api = (target: Object, property: string, descriptor: TypedPropertyDescriptor<any>): any => {
|
10
|
+
const method = descriptor.value
|
11
|
+
|
12
|
+
descriptor.value = async function (store: Sellercraft, request) {
|
13
|
+
const SellercraftChannelIntegrationAPI = this
|
14
|
+
|
15
|
+
var { platform } = store
|
16
|
+
|
17
|
+
var { action: platformAction, apis } = SellercraftChannelIntegrationAPI.getPlatform(platform)
|
18
|
+
|
19
|
+
var m = apis[method.name]
|
20
|
+
if (!m) {
|
21
|
+
throw Error(`Sellercraft doesn't have API ${method.name}`)
|
22
|
+
}
|
23
|
+
|
24
|
+
var {
|
25
|
+
path,
|
26
|
+
method: httpMethod = 'post',
|
27
|
+
denormalize = NOOP,
|
28
|
+
normalize = NOOP,
|
29
|
+
action = platformAction
|
30
|
+
} = m.apply(this, [request])
|
31
|
+
|
32
|
+
var denormalized = await denormalize(request || {}, { store })
|
33
|
+
debug('request', denormalized)
|
34
|
+
|
35
|
+
var response = await action.apply(this, [
|
36
|
+
{ store, method: httpMethod, path, request: denormalized, platformAction }
|
37
|
+
])
|
38
|
+
|
39
|
+
debug('response', response)
|
40
|
+
|
41
|
+
return await normalize(response, { store })
|
42
|
+
}
|
43
|
+
|
44
|
+
return descriptor
|
45
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import { getRepository } from '@things-factory/shell'
|
2
|
+
|
3
|
+
import { Sellercraft } from '../../service'
|
4
|
+
import { api } from './decorators'
|
5
|
+
|
6
|
+
export class SellercraftChannelIntegrationAPI {
|
7
|
+
static platforms = {}
|
8
|
+
|
9
|
+
static registerPlatform(name, action, apis) {
|
10
|
+
SellercraftChannelIntegrationAPI.platforms[name] = {
|
11
|
+
action,
|
12
|
+
apis
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
static getPlatform(name) {
|
17
|
+
return SellercraftChannelIntegrationAPI.platforms[name]
|
18
|
+
}
|
19
|
+
|
20
|
+
static async getSellercraft(id) {
|
21
|
+
const repository = getRepository(Sellercraft)
|
22
|
+
return await repository.findOne({
|
23
|
+
where: { id },
|
24
|
+
relations: ['domain']
|
25
|
+
})
|
26
|
+
}
|
27
|
+
|
28
|
+
@api
|
29
|
+
static echo(sellercraft, req): any {}
|
30
|
+
|
31
|
+
@api
|
32
|
+
static ingestChannelCategories(sellercraft, req): any {}
|
33
|
+
|
34
|
+
@api
|
35
|
+
static ingestChannelCategoryAttributes(sellercraft, req): any {}
|
36
|
+
|
37
|
+
@api
|
38
|
+
static ingestChannelOrderPackage(sellercraft, req): any {}
|
39
|
+
|
40
|
+
@api
|
41
|
+
static ingestChannelOrder(sellercraft, req): any {}
|
42
|
+
|
43
|
+
@api
|
44
|
+
static ingestChannelProduct(sellercraft, req): any {}
|
45
|
+
}
|
File without changes
|
package/server/index.ts
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
const glob = require('glob')
|
2
|
+
const path = require('path')
|
3
|
+
|
4
|
+
export var migrations = []
|
5
|
+
|
6
|
+
glob.sync(path.resolve(__dirname, '.', '**', '*.js')).forEach(function(file) {
|
7
|
+
if (file.indexOf('index.js') !== -1) return
|
8
|
+
migrations = migrations.concat(Object.values(require(path.resolve(file))) || [])
|
9
|
+
})
|