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

Commit

Permalink
feat: use purify as a json schema generator
Browse files Browse the repository at this point in the history
  • Loading branch information
gigobyte committed May 4, 2020
1 parent be87cc7 commit 1c91708
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 111 deletions.
11 changes: 8 additions & 3 deletions package-lock.json

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

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@
"types": "lib/index.d.ts",
"dependencies": {
"@scaleleap/amazon-marketplaces": "^4.0.2",
"@sinclair/typebox": "^0.9.13",
"@sinclair/typebox": "^0.9.14",
"ajv": "^6.12.2",
"axios": "^0.19.2",
"fast-xml-parser": "^3.16.0"
"fast-xml-parser": "^3.16.0",
"purify-ts": "^0.16.0-beta.0"
},
"devDependencies": {
"@scaleleap/config": "1.6.1",
Expand Down
41 changes: 30 additions & 11 deletions src/parsing.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
/** A collection of parsing utils */
/** A collection of parsing codecs */
import { array, Codec } from 'purify-ts/Codec'
import { Left, Right } from 'purify-ts/Either'

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

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')
}
return Codec.custom({
decode: (x) => {
const arrayX = Array.isArray(x) ? x : [x]
return array(codec).decode(arrayX)
},
encode: (x) => x,
schema: () => ({
oneOf: [schema, { type: 'array', items: [schema], minItems: 1 }],
}),
})
}

export const mwsBoolean = Codec.custom<boolean>({
decode: (x) => {
switch (x) {
case 'Yes':
return Right(true)
case 'No':
return Right(false)
default:
return Left('TODO, but should be library-specific error')
}
},
encode: (x) => x,
schema: () => ({ type: 'string', enum: ['Yes', 'No'] }),
})
78 changes: 23 additions & 55 deletions src/sections/sellers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { Type } from '@sinclair/typebox'
import Ajv from 'ajv'
import { Codec, GetInterface, string } from 'purify-ts'

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

interface MarketplaceParticipationsResponse {
ListMarketplaceParticipationsResponse: {
Expand Down Expand Up @@ -30,43 +29,27 @@ interface MarketplaceParticipationsResponse {
}
}

const MarketplaceParticipations = Type.Object({
participations: Type.Array(
Type.Object({
marketplaceId: Type.String(),
sellerId: Type.String(),
hasSellerSuspendedListings: Type.Boolean(),
const MarketplaceParticipations = Codec.interface({
participations: ensureArray(
Codec.interface({
marketplaceId: string,
sellerId: string,
hasSellerSuspendedListings: mwsBoolean,
}),
),
marketplaces: Type.Array(
Type.Object({
marketplaceId: Type.String(),
name: Type.String(),
defaultCountryCode: Type.String(),
defaultCurrencyCode: Type.String(),
defaultLanguageCode: Type.String(),
domainName: Type.String(),
marketplaces: ensureArray(
Codec.interface({
marketplaceId: string,
name: string,
defaultCountryCode: string,
defaultCurrencyCode: string,
defaultLanguageCode: string,
domainName: 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
}>
}
type MarketplaceParticipations = GetInterface<typeof MarketplaceParticipations>

export class Sellers {
constructor(private httpClient: HttpClient) {}
Expand All @@ -84,26 +67,11 @@ export class Sellers {

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, meta]
}

throw new Error('TODO for now')
return MarketplaceParticipations.decode(data).caseOf({
Right: (x) => [x, meta],
Left: (error) => {
throw new Error(error)
},
})
}
}
40 changes: 0 additions & 40 deletions test/unit/parsing.test.ts

This file was deleted.

0 comments on commit 1c91708

Please sign in to comment.