Skip to content
This repository has been archived by the owner on Feb 6, 2024. It is now read-only.

Commit

Permalink
feat: parse, transform and validate response from sellers endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
gigobyte committed May 4, 2020
1 parent dd184cd commit 6b24657
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 58 deletions.
32 changes: 17 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@
"dependencies": {
"@scaleleap/amazon-marketplaces": "^4.0.2",
"@sinclair/typebox": "^0.9.13",
"ajv": "^6.12.2",
"axios": "^0.19.2",
"xml2js": "^0.4.23"
"fast-xml-parser": "^3.16.0"
},
"devDependencies": {
"@scaleleap/config": "1.6.1",
Expand Down
16 changes: 3 additions & 13 deletions src/http.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AmazonMarketplace } from '@scaleleap/amazon-marketplaces'
import axios from 'axios'
import parser from 'fast-xml-parser'
import { URLSearchParams } from 'url'
import { parseString } from 'xml2js'

import { sign } from './sign'

Expand Down Expand Up @@ -47,18 +47,8 @@ const canonicalizeParameters = (parameters: Parameters): string => {
return sp.toString().replace(/\+/g, '%20')
}

const defaultFetch = <T>({ url, method, headers, data }: Request) =>
axios({ method, url, headers, data }).then(
(response) =>
new Promise<T>((resolve, reject) =>
parseString(response.data, (error, result) => {
if (error) {
reject(error)
}
resolve(result)
}),
),
)
const defaultFetch = <T>({ url, method, headers, data }: Request): Promise<T> =>
axios({ method, url, headers, data }).then((response) => parser.parse(response.data))

export class HttpClient {
constructor(
Expand Down
14 changes: 14 additions & 0 deletions src/parsing.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/** A collection of parsing utils */

export const ensureArray = <T>(x: T | T[]): T[] => (Array.isArray(x) ? x : [x])

export const parseBoolean = (x: 'Yes' | 'No'): boolean => {
switch (x) {
case 'Yes':
return true
case 'No':
return false
default:
throw new Error('TODO, but should be library-specific error')
}
}
117 changes: 88 additions & 29 deletions src/sections/sellers.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,106 @@
import { Type } from '@sinclair/typebox'
import Ajv from 'ajv'

import { HttpClient, Resource } from '../http'
import { ensureArray, parseBoolean } from '../parsing'

interface MarketplaceParticipations {
interface MarketplaceParticipationsResponse {
ListMarketplaceParticipationsResponse: {
ListMarketplaceParticipationsResult: [
{
NextToken: [string]
ListParticipations: Array<{
Participation: [
{
MarketplaceId: [string]
SellerId: [string]
HasSellerSuspendedListings: [string]
},
]
ListMarketplaceParticipationsResult: {
NextToken: string
ListParticipations: {
Participation: Array<{
MarketplaceId: string
SellerId: string
HasSellerSuspendedListings: 'Yes' | 'No'
}>
ListMarketplaces: Array<{
Marketplace: [
{
MarketplaceId: [string]
Name: [string]
DefaultCountryCode: [string]
DefaultCurrencyCode: [string]
DefaultLanguageCode: [string]
DomainName: [string]
},
]
}
ListMarketplaces: {
Marketplace: Array<{
MarketplaceId: string
Name: string
DefaultCountryCode: string
DefaultCurrencyCode: string
DefaultLanguageCode: string
DomainName: string
}>
},
]
ResponseMetadata: [{ RequestId: [string] }]
}
}
ResponseMetadata: { RequestId: string }
}
}

const MarketplaceParticipations = Type.Object({
participations: Type.Array(
Type.Object({
marketplaceId: Type.String(),
sellerId: Type.String(),
hasSellerSuspendedListings: Type.Boolean(),
}),
),
marketplaces: Type.Array(
Type.Object({
marketplaceId: Type.String(),
name: Type.String(),
defaultCountryCode: Type.String(),
defaultCurrencyCode: Type.String(),
defaultLanguageCode: Type.String(),
domainName: Type.String(),
}),
),
})

// Unfortunately typebox creates an ugly type from Static<typeof X> (not a regular object with keys),
// it's not something that we can expose from this library, so I've added a manually created interface
interface MarketplaceParticipations {
participations: Array<{
marketplaceId: string
sellerId: string
hasSellerSuspendedListings: boolean
}>
marketplaces: Array<{
marketplaceId: string
name: string
defaultCountryCode: string
defaultCurrencyCode: string
defaultLanguageCode: string
domainName: string
}>
}

export class Sellers {
constructor(private httpClient: HttpClient) {}

// TODO: transform response
async listMarketplaceParticipations() {
return this.httpClient.request('POST', {
async listMarketplaceParticipations(): Promise<MarketplaceParticipations> {
const response: MarketplaceParticipationsResponse = await this.httpClient.request('POST', {
resource: Resource.Sellers,
version: '2011-07-01',
action: 'ListMarketplaceParticipations',
parameters: {},
})

const data = response.ListMarketplaceParticipationsResponse.ListMarketplaceParticipationsResult

const result: MarketplaceParticipations = {
participations: ensureArray(data.ListParticipations.Participation).map((x) => ({
marketplaceId: x.MarketplaceId,
sellerId: x.SellerId,
hasSellerSuspendedListings: parseBoolean(x.HasSellerSuspendedListings),
})),
marketplaces: ensureArray(data.ListMarketplaces.Marketplace).map((x) => ({
marketplaceId: x.MarketplaceId,
name: x.Name,
defaultCountryCode: x.DefaultCountryCode,
defaultCurrencyCode: x.DefaultCurrencyCode,
defaultLanguageCode: x.DefaultLanguageCode,
domainName: x.DomainName,
})),
}

if (new Ajv().validate(MarketplaceParticipations, result)) {
return result
}

throw new Error('TODO for now')
}
}

0 comments on commit 6b24657

Please sign in to comment.