From 8b10bc9464a46adc17b1d291385041b03fd7da25 Mon Sep 17 00:00:00 2001 From: gigobyte Date: Thu, 7 May 2020 20:23:37 +0300 Subject: [PATCH] feat: add the rest of the sellers api --- src/parsing.ts | 27 +++++ src/sections/sellers.ts | 113 +++++++++++++----- .../sellers-list-market-participation.test.ts | 10 ++ test/unit/__snapshots__/parsing.test.ts.snap | 2 + test/unit/__snapshots__/sellers.test.ts.snap | 16 +++ test/unit/parsing.test.ts | 28 ++++- test/unit/sellers.test.ts | 42 ++++++- 7 files changed, 208 insertions(+), 30 deletions(-) diff --git a/src/parsing.ts b/src/parsing.ts index c21784c2..b1dc4e7e 100644 --- a/src/parsing.ts +++ b/src/parsing.ts @@ -35,3 +35,30 @@ export const mwsBoolean = Codec.custom({ encode: (x) => x, schema: () => ({ type: 'string', enum: ['Yes', 'No'] }), }) + +export enum ServiceStatus { + Green = 'GREEN', + Yellow = 'YELLOW', + Red = 'RED', +} + +export const serviceStatus = Codec.custom({ + decode: (x) => { + switch (x) { + case 'GREEN': + return Right(ServiceStatus.Green) + case 'YELLOW': + return Right(ServiceStatus.Yellow) + case 'RED': + return Right(ServiceStatus.Red) + default: + return Left( + `Expected a string with a value of "GREEN", "YELLOW" or "RED", but received a string with value ${JSON.stringify( + x, + )}`, + ) + } + }, + encode: (x) => x, + schema: () => ({ type: 'string', enum: ['GREEN', 'YELLOW', 'RED'] }), +}) diff --git a/src/sections/sellers.ts b/src/sections/sellers.ts index e9a7aef9..b1baa427 100644 --- a/src/sections/sellers.ts +++ b/src/sections/sellers.ts @@ -2,39 +2,60 @@ import { Codec, GetInterface, optional, string } from 'purify-ts' import { ParsingError } from '../error' import { HttpClient, RequestMeta, Resource } from '../http' -import { ensureArray, mwsBoolean } from '../parsing' +import { ensureArray, mwsBoolean, serviceStatus } from '../parsing' + +const SELLERS_API_VERSION = '2011-07-01' const MarketplaceParticipations = Codec.interface({ - ListMarketplaceParticipationsResponse: Codec.interface({ - ListMarketplaceParticipationsResult: Codec.interface({ - NextToken: optional(string), - ListParticipations: Codec.interface({ - Participation: ensureArray( - Codec.interface({ - MarketplaceId: string, - SellerId: string, - HasSellerSuspendedListings: mwsBoolean, - }), - ), + NextToken: optional(string), + ListParticipations: Codec.interface({ + Participation: ensureArray( + Codec.interface({ + MarketplaceId: string, + SellerId: string, + HasSellerSuspendedListings: mwsBoolean, }), - ListMarketplaces: Codec.interface({ - Marketplace: ensureArray( - Codec.interface({ - MarketplaceId: string, - Name: string, - DefaultCountryCode: string, - DefaultCurrencyCode: string, - DefaultLanguageCode: string, - DomainName: string, - }), - ), + ), + }), + ListMarketplaces: Codec.interface({ + Marketplace: ensureArray( + Codec.interface({ + MarketplaceId: string, + Name: string, + DefaultCountryCode: string, + DefaultCurrencyCode: string, + DefaultLanguageCode: string, + DomainName: string, }), + ), + }), +}) + +const MarketplaceParticipationsResponse = Codec.interface({ + ListMarketplaceParticipationsResponse: Codec.interface({ + ListMarketplaceParticipationsResult: MarketplaceParticipations, + }), +}) + +const MarketplaceParticipationsByNextTokenResponse = Codec.interface({ + ListMarketplaceParticipationsByNextTokenResponse: Codec.interface({ + ListMarketplaceParticipationsByNextTokenResult: MarketplaceParticipations, + }), +}) + +const ServiceStatusResponse = Codec.interface({ + GetServiceStatusResponse: Codec.interface({ + GetServiceStatusResult: Codec.interface({ + Status: serviceStatus, + Timestamp: string, }), }), }) -type MarketplaceParticipationsResponse = GetInterface -type MarketplaceParticipations = MarketplaceParticipationsResponse['ListMarketplaceParticipationsResponse']['ListMarketplaceParticipationsResult'] +type MarketplaceParticipations = GetInterface +type ServiceStatusResponse = GetInterface< + typeof ServiceStatusResponse +>['GetServiceStatusResponse']['GetServiceStatusResult'] export class Sellers { constructor(private httpClient: HttpClient) {} @@ -42,12 +63,12 @@ export class Sellers { async listMarketplaceParticipations(): Promise<[MarketplaceParticipations, RequestMeta]> { const [response, meta] = await this.httpClient.request('POST', { resource: Resource.Sellers, - version: '2011-07-01', + version: SELLERS_API_VERSION, action: 'ListMarketplaceParticipations', parameters: {}, }) - return MarketplaceParticipations.decode(response).caseOf({ + return MarketplaceParticipationsResponse.decode(response).caseOf({ Right: (x) => [ x.ListMarketplaceParticipationsResponse.ListMarketplaceParticipationsResult, meta, @@ -57,4 +78,42 @@ export class Sellers { }, }) } + + async listMarketplaceParticipationsByNextToken( + nextToken: string, + ): Promise<[MarketplaceParticipations, RequestMeta]> { + const [response, meta] = await this.httpClient.request('POST', { + resource: Resource.Sellers, + version: SELLERS_API_VERSION, + action: 'ListMarketplaceParticipationsByNextToken', + parameters: { NextToken: nextToken }, + }) + + return MarketplaceParticipationsByNextTokenResponse.decode(response).caseOf({ + Right: (x) => [ + x.ListMarketplaceParticipationsByNextTokenResponse + .ListMarketplaceParticipationsByNextTokenResult, + meta, + ], + Left: (error) => { + throw new ParsingError(error) + }, + }) + } + + async getServiceStatus(): Promise<[ServiceStatusResponse, RequestMeta]> { + const [response, meta] = await this.httpClient.request('POST', { + resource: Resource.Sellers, + version: SELLERS_API_VERSION, + action: 'GetServiceStatus', + parameters: {}, + }) + + return ServiceStatusResponse.decode(response).caseOf({ + Right: (x) => [x.GetServiceStatusResponse.GetServiceStatusResult, meta], + Left: (error) => { + throw new ParsingError(error) + }, + }) + } } diff --git a/test/integration/sellers-list-market-participation.test.ts b/test/integration/sellers-list-market-participation.test.ts index 80985c2c..1d4b959e 100644 --- a/test/integration/sellers-list-market-participation.test.ts +++ b/test/integration/sellers-list-market-participation.test.ts @@ -27,5 +27,15 @@ describe(`${Sellers.name}`, () => { expect.objectContaining({ MarketplaceId: amazonMarketplaces.CA.id }), ) }) + + itci('should be able to query service status', async () => { + expect.assertions(1) + + const sellers = new Sellers(httpClient) + + const [marketplaceParticipations] = await sellers.getServiceStatus() + + expect(marketplaceParticipations.Status).toMatch(/GREEN|YELLOW|RED/) + }) }) /* eslint-enable jest/no-standalone-expect */ diff --git a/test/unit/__snapshots__/parsing.test.ts.snap b/test/unit/__snapshots__/parsing.test.ts.snap index 537a0bd3..835dac30 100644 --- a/test/unit/__snapshots__/parsing.test.ts.snap +++ b/test/unit/__snapshots__/parsing.test.ts.snap @@ -1,3 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`mwsBoolean decodes any other string as a failure 1`] = `"Expected a string with a value of either \\"Yes\\" or \\"No\\", but received a string with value \\"YES\\""`; + +exports[`serviceStatus decodes any other string as a failure 1`] = `"Expected a string with a value of \\"GREEN\\", \\"YELLOW\\" or \\"RED\\", but received a string with value \\"Green\\""`; diff --git a/test/unit/__snapshots__/sellers.test.ts.snap b/test/unit/__snapshots__/sellers.test.ts.snap index d3a15a19..7b615ec8 100644 --- a/test/unit/__snapshots__/sellers.test.ts.snap +++ b/test/unit/__snapshots__/sellers.test.ts.snap @@ -1,5 +1,21 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`sellers getServiceStatus returns a parsed model when the response is valid 1`] = ` +Array [ + Object { + "Status": "GREEN", + "Timestamp": "2020-05-06T08:22:23.582Z", + }, + Object { + "quotaMax": 1000, + "quotaRemaining": 999, + "quotaResetOn": "2020-05-06T10:22:23.582Z", + "requestId": "0", + "timestamp": "2020-05-06T08:22:23.582Z", + }, +] +`; + exports[`sellers listMarketplaceParticipations returns a parsed model when the response is valid 1`] = ` Array [ Object { diff --git a/test/unit/parsing.test.ts b/test/unit/parsing.test.ts index f692d3c8..37ef72e0 100644 --- a/test/unit/parsing.test.ts +++ b/test/unit/parsing.test.ts @@ -1,6 +1,6 @@ import { number, Right } from 'purify-ts' -import { ensureArray, mwsBoolean } from '../../src/parsing' +import { ensureArray, mwsBoolean, ServiceStatus, serviceStatus } from '../../src/parsing' describe('ensureArray', () => { it('acts like an idenity if the value to be decoded is already an array', () => { @@ -35,3 +35,29 @@ describe('mwsBoolean', () => { expect(mwsBoolean.decode('YES')).toMatchSnapshot() }) }) + +describe('serviceStatus', () => { + it('decodes the string "GREEN"', () => { + expect.assertions(1) + + expect(serviceStatus.decode('GREEN')).toStrictEqual(Right(ServiceStatus.Green)) + }) + + it('decodes the string "YELLOW"', () => { + expect.assertions(1) + + expect(serviceStatus.decode('YELLOW')).toStrictEqual(Right(ServiceStatus.Yellow)) + }) + + it('decodes the string "RED"', () => { + expect.assertions(1) + + expect(serviceStatus.decode('RED')).toStrictEqual(Right(ServiceStatus.Red)) + }) + + it('decodes any other string as a failure', () => { + expect.assertions(1) + + expect(serviceStatus.decode('Green')).toMatchSnapshot() + }) +}) diff --git a/test/unit/sellers.test.ts b/test/unit/sellers.test.ts index 1140046b..36dd6275 100644 --- a/test/unit/sellers.test.ts +++ b/test/unit/sellers.test.ts @@ -9,7 +9,7 @@ const httpConfig = { sellerId: '', } -const mockMws = new MWS( +const mockMwsMarketplaceParticipations = new MWS( new HttpClient(httpConfig, () => Promise.resolve({ data: ` @@ -60,6 +60,26 @@ const mockMws = new MWS( ), ) +const mockMwsServiceStatus = new MWS( + new HttpClient(httpConfig, () => + Promise.resolve({ + data: ` + + GREEN + 2020-05-06T08:22:23.582Z + + `, + headers: { + 'x-mws-request-id': '0', + 'x-mws-timestamp': '2020-05-06T08:22:23.582Z', + 'x-mws-quota-max': '1000', + 'x-mws-quota-remaining': '999', + 'x-mws-quota-resetson': '2020-05-06T10:22:23.582Z', + }, + }), + ), +) + const mockMwsFail = new MWS( new HttpClient(httpConfig, () => Promise.resolve({ data: '', headers: {} })), ) @@ -69,7 +89,9 @@ describe('sellers', () => { it('returns a parsed model when the response is valid', async () => { expect.assertions(1) - expect(await mockMws.sellers.listMarketplaceParticipations()).toMatchSnapshot() + expect( + await mockMwsMarketplaceParticipations.sellers.listMarketplaceParticipations(), + ).toMatchSnapshot() }) it('throws a parsing error when the response is not valid', async () => { @@ -80,4 +102,20 @@ describe('sellers', () => { ) }) }) + + describe('getServiceStatus', () => { + it('returns a parsed model when the response is valid', async () => { + expect.assertions(1) + + expect(await mockMwsServiceStatus.sellers.getServiceStatus()).toMatchSnapshot() + }) + + it('throws a parsing error when the response is not valid', async () => { + expect.assertions(1) + + await expect(() => mockMwsFail.sellers.getServiceStatus()).rejects.toStrictEqual( + new ParsingError(''), + ) + }) + }) })