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

Commit

Permalink
feat: making requests mvp
Browse files Browse the repository at this point in the history
  • Loading branch information
gigobyte committed May 2, 2020
1 parent 9cacd4f commit f6138f9
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 6 deletions.
31 changes: 31 additions & 0 deletions package-lock.json

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

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
"test:watch": "jest --watchAll"
},
"types": "lib/index.d.ts",
"dependencies": {
"axios": "^0.19.2"
},
"devDependencies": {
"@scaleleap/semantic-release-config": "1.1.8",
"@scaleleap/utils": "1.6.6",
Expand Down
81 changes: 81 additions & 0 deletions src/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import axios from 'axios'
import { URLSearchParams } from 'url'

import { sign } from './sign'

export interface MWSOptions {
endpoint: string
awsAccessKeyId: string
mwsAuthToken: string
sellerId: string
secret: string
httpClient?: HttpClient
}

type HttpMethod = 'GET' | 'POST'
type Parameters = Record<string, string | string[]>

export enum Resource {
Sellers = 'Sellers',
}

interface Request {
url: string
method: HttpMethod
headers: Record<string, string>
}

interface ClientInfo extends MWSOptions {
resource: Resource
version: string
action: string
parameters: Parameters
}

const canonicalizeParameters = (parameters: Parameters): string => {
const sp = new URLSearchParams(parameters)
sp.sort()
return sp.toString().replace(/\+/g, '%20')
}

export class HttpClient {
static withDefaults() {
return new HttpClient(({ url, method, headers }) =>
axios({ method, url, headers }).then((response) => response.data),
)
}

// eslint-disable-next-line no-useless-constructor, no-empty-function
constructor(private fetch: <T>(meta: Request) => Promise<T>) {}

request(method: HttpMethod, info: ClientInfo) {
const host = info.endpoint.replace('https://', '')
const url = `${info.endpoint}/${info.resource}/${info.version}/`

const parameters = {
AWSAccessKeyId: info.awsAccessKeyId,
Action: info.action,
MWSAuthToken: info.mwsAuthToken,
SellerId: info.sellerId,
SignatureMethod: 'HmacSHA256',
SignatureVersion: '2',
Timestamp: new Date().toISOString(),
Version: info.version,
...info.parameters,
}

const parametersForSigning = canonicalizeParameters(parameters)
const queryStringToSign = `${method}\n${host}\n/${info.resource}/${info.version}\n${parametersForSigning}`

const signature = sign(queryStringToSign, info.secret)
const parametersWithSignature = { ...parameters, signature }

return this.fetch({
url: `${url}?${canonicalizeParameters(parametersWithSignature)}`,
method,
headers: {
'user-agent': '@scaleleap/amazon-mws-api-sdk/1.0.0 (Language=JavaScript)',
},
})
}
}
4 changes: 1 addition & 3 deletions src/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { helloWorld } from '.'

describe('integration', () => {
it('should pass', () => {
expect.assertions(1)

expect(helloWorld()).toBe('hello world')
expect(true).toBe(true)
})
})
5 changes: 2 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
export function helloWorld() {
return 'hello world'
}
export { MWS } from './mws'
export { Sellers } from './sections/sellers'
23 changes: 23 additions & 0 deletions src/mws.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/* eslint-disable no-underscore-dangle */
import { HttpClient, MWSOptions } from './http'
import { Sellers } from './sections/sellers'

export class MWS {
private httpClient: HttpClient

private _sellers!: Sellers

constructor(private options: MWSOptions) {
this.httpClient = options.httpClient ?? HttpClient.withDefaults()
}

get sellers() {
if (!this._sellers) {
this._sellers = new Sellers(this.options, this.httpClient)
}

return this._sellers
}
}

/* eslint-enable no-underscore-dangle */
17 changes: 17 additions & 0 deletions src/sections/sellers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { HttpClient, MWSOptions, Resource } from '../http'

export class Sellers {
// eslint-disable-next-line no-useless-constructor, no-empty-function
constructor(private options: MWSOptions, private httpClient: HttpClient) {}

// TODO: type and parse response
listMarketplaceParticipations() {
return this.httpClient.request('GET', {
...this.options,
resource: Resource.Sellers,
version: '2011-07-01',
action: 'ListMarketplaceParticipations',
parameters: {},
})
}
}
7 changes: 7 additions & 0 deletions src/sign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import crypto from 'crypto'

export const sign = (queryString: string, secret: string): string => {
const hmac = crypto.createHmac('sha256', secret)
hmac.update(queryString, 'utf8')
return hmac.digest('base64')
}
Empty file added src/utils.ts
Empty file.

0 comments on commit f6138f9

Please sign in to comment.