Skip to content

Commit

Permalink
feat: add httpFallback
Browse files Browse the repository at this point in the history
  • Loading branch information
pismenskiy authored Jul 17, 2020
1 parent fc9a6b3 commit 73473a6
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/main/ts/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { createHttpPipe, IHttpPipeOpts, IHttpHeaders } from './pipes/http'
export { createHttpPipeFallback } from './pipes/httpFallback'
export { createMaskerPipe, panMaskerPipe } from './pipes/masker'
export { createFlpPipeline, eventifyPipe } from './pipes/flp'
export { createDeviceInfoPipe, getDeviceInfo } from './pipes/deviceInfo'
Expand Down
20 changes: 20 additions & 0 deletions src/main/ts/pipes/httpFallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { IPipe, IPipeOutput, ITransmittable } from '../interfaces'
import { IPromise } from '@qiwi/substrate'
import { createHttpPipe, IHttpPipeOpts } from './http'
import { executeFailproof } from '../utils'

export const type = 'http-fallback'

export const createHttpPipeFallback = (opts: IHttpPipeOpts[]): IPipe => {
if (opts.length === 0) {
throw new Error('createHttpPipeFallback opts must not be empty')
}

const httpPipes = opts.map(createHttpPipe)
return {
type,
execute (transmittable : ITransmittable): IPromise<IPipeOutput> {
return executeFailproof(transmittable, httpPipes)
},
}
}
15 changes: 15 additions & 0 deletions src/main/ts/utils/executeFailproof.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { IPipe, IPipeOutput, ITransmittable } from '../interfaces'
import { identity } from '.'

export async function executeFailproof (transmittable: ITransmittable, pipeline: IPipe[]): Promise<IPipeOutput> {
const pipe = pipeline.shift() as IPipe
const [err, succ] = await pipe.execute(transmittable, identity)

if (pipeline.length === 0) {
return succ
? [null, succ]
: [err, null]
}

return executeFailproof(transmittable, pipeline)
}
1 change: 1 addition & 0 deletions src/main/ts/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import set from 'lodash.set'
export { deepMap } from './deepmap'
export { executeFailproof } from './executeFailproof'
export { clone } from './clone'
export { set }

Expand Down
2 changes: 1 addition & 1 deletion src/test/ts/pipes/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('httpPipe', () => {
expect(httpPipe.execute).toEqual(expect.any(Function))
})

it('returns remote data if succeeds', () => {
it('returns remote data if succeeds', async () => {
const httpPipe = createHttpPipe({ url: 'https://reqres.in/api/users/2', method: HttpMethod.GET })
const transmittable: ITransmittable = { data: null, meta: { history: [] } }

Expand Down
67 changes: 67 additions & 0 deletions src/test/ts/pipes/httpFallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { HttpMethod } from '@qiwi/substrate-types'
import { createHttpPipeFallback, ITransmittable } from '../../../main/ts'

import 'cross-fetch/polyfill'

const noop = () => { /* noop */ }

describe('httpPipe', () => {
it('factory returns IPipe', () => {
const httpPipeFallback = createHttpPipeFallback([{ url: 'https://reqres.in/api/users/2', method: HttpMethod.GET }])
expect(httpPipeFallback.type).toBe('http-fallback')
expect(httpPipeFallback.execute).toEqual(expect.any(Function))
})

it('returns remote data if succeeds', async () => {
const httpPipe = createHttpPipeFallback([{ url: 'https://reqres.in/api/users/2', method: HttpMethod.GET }])
const transmittable: ITransmittable = { data: null, meta: { history: [] } }

return expect(httpPipe.execute(transmittable, noop))
.resolves.toMatchObject([null, {
data: {
id: 2,
email: '[email protected]',
first_name: 'Janet',
last_name: 'Weaver',
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg',
},
}])
})

it('uses fallback urls', () => {
const httpPipe = createHttpPipeFallback([
{ url: 'https://reqres.in/api/users/23', method: HttpMethod.GET },
{ url: 'https://reqres.in/api/unknown/23', method: HttpMethod.GET },
{ url: 'https://reqres.in/api/users/2', method: HttpMethod.GET },
])
const transmittable: ITransmittable = { data: null, meta: { history: [] } }

return expect(httpPipe.execute(transmittable, noop))
.resolves.toMatchObject([null, {
data: {
id: 2,
email: '[email protected]',
first_name: 'Janet',
last_name: 'Weaver',
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg',
},
}])
})

it('handle errors', () => {
const httpPipe = createHttpPipeFallback([
{ url: 'https://reqres.in/api/users/23', method: HttpMethod.GET },
{ url: 'https://reqres.in/api/unknown/23', method: HttpMethod.GET },
{ url: 'https://reqres.in/api/users/23', method: HttpMethod.GET },
])
const transmittable: ITransmittable = { data: null, meta: { history: [] } }

return expect(httpPipe.execute(transmittable, noop))
.resolves.toEqual([new Error('Not Found'), null])
})

it('throw error when opts is empty', () => {
return expect(() => createHttpPipeFallback([]))
.toThrow(new Error('createHttpPipeFallback opts must not be empty'))
})
})
23 changes: 17 additions & 6 deletions src/test/ts/pipes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ describe('pipes', () => {

describe('pipes', () => {
describe('execute', () => {
it('execute works correctly', async (done) => {
it('execute works correctly', async () => {
const upper: IPipe = {
type: 'upper',
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async execute (data, next: ICallable) {
data.data.message = data.data.message.toUpperCase()
await next([null, data])
await next([null, data.data])
data.data.message = data.data.message.toUpperCase()
return Promise.resolve([null, data])
},
Expand All @@ -51,13 +51,24 @@ describe('pipes', () => {

const pipeline2: TPipeline = [upper, foo]

const [err, data] = await execute(createTransmittable({
const res = await execute(createTransmittable({
message: 'baz',
}), pipeline2)

console.log(err, JSON.stringify(data))

done()
expect(res).toMatchObject([
null,
{
data: { message: 'FOO' },
err: null,
meta:
{
history:
[
{ pipelineId: '{0-upper}{1-foo}', pipeId: '1', pipeType: 'foo', err: null, res: false },
{ pipelineId: '{0-upper}{1-foo}', pipeId: '0', pipeType: 'upper', err: null, res: true }],
},
},
])
})
})
})

0 comments on commit 73473a6

Please sign in to comment.