-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
CAS-7032 Add filter, risk and log endpoint clients
- Loading branch information
Showing
18 changed files
with
740 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { Configuration } from '../../configuraton'; | ||
import { InternalServerError } from '../../errors'; | ||
import { FilterResult } from '../../models'; | ||
import { CommandFilterService } from '../../command/command.module'; | ||
import { | ||
FailoverResponsePrepareService, | ||
FailoverStrategy, | ||
} from '../../failover/failover.module'; | ||
import { FilterPayload } from '../../payload/payload.module'; | ||
import { APIService } from './api.service'; | ||
import AbortController from 'abort-controller'; | ||
|
||
const handleFailover = ( | ||
userId: string, | ||
reason: string, | ||
configuration: Configuration, | ||
err?: Error | ||
): FilterResult => { | ||
// Have to check it this way to make sure TS understands | ||
// that this.failoverStrategy is of type Verdict, | ||
// not FailoverStrategyType. | ||
if (configuration.failoverStrategy === FailoverStrategy.throw) { | ||
throw err; | ||
} | ||
|
||
return FailoverResponsePrepareService.call( | ||
userId, | ||
reason, | ||
configuration.failoverStrategy | ||
); | ||
}; | ||
|
||
const isTimeoutError = (e: Error) => e.name === 'AbortError'; | ||
|
||
export const APIFilterService = { | ||
call: async ( | ||
options: FilterPayload, | ||
configuration: Configuration | ||
): Promise<FilterResult> => { | ||
const controller = new AbortController(); | ||
const command = CommandFilterService.call( | ||
controller, | ||
options, | ||
configuration | ||
); | ||
|
||
let processedResponse; | ||
try { | ||
processedResponse = await APIService.call( | ||
controller, | ||
command, | ||
configuration | ||
); | ||
} catch (e) { | ||
if (isTimeoutError(e)) { | ||
return handleFailover(options.user.id, 'timeout', configuration, e); | ||
} else if (e instanceof InternalServerError) { | ||
return handleFailover(options.user.id, 'server error', configuration); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
|
||
return processedResponse; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { Configuration } from '../../configuraton'; | ||
import { CommandLogService } from '../../command/command.module'; | ||
import { LogPayload } from '../../payload/payload.module'; | ||
import { APIService } from './api.service'; | ||
import AbortController from 'abort-controller'; | ||
|
||
export const APILogService = { | ||
call: async ( | ||
options: LogPayload, | ||
configuration: Configuration | ||
): Promise<void> => { | ||
const controller = new AbortController(); | ||
const command = CommandLogService.call( | ||
controller, | ||
options, | ||
configuration | ||
); | ||
|
||
APIService.call(controller, command, configuration); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import { Configuration } from '../../configuraton'; | ||
import { InternalServerError } from '../../errors'; | ||
import { RiskResult } from '../../models'; | ||
import { CommandRiskService } from '../../command/command.module'; | ||
import { | ||
FailoverResponsePrepareService, | ||
FailoverStrategy, | ||
} from '../../failover/failover.module'; | ||
import { RiskPayload } from '../../payload/risk_payload.module'; | ||
import { APIService } from './api.service'; | ||
import AbortController from 'abort-controller'; | ||
|
||
const handleFailover = ( | ||
userId: string, | ||
reason: string, | ||
configuration: Configuration, | ||
err?: Error | ||
): RiskResult => { | ||
// Have to check it this way to make sure TS understands | ||
// that this.failoverStrategy is of type Verdict, | ||
// not FailoverStrategyType. | ||
if (configuration.failoverStrategy === FailoverStrategy.throw) { | ||
throw err; | ||
} | ||
|
||
return FailoverResponsePrepareService.call( | ||
userId, | ||
reason, | ||
configuration.failoverStrategy | ||
); | ||
}; | ||
|
||
const isTimeoutError = (e: Error) => e.name === 'AbortError'; | ||
|
||
export const APIRiskService = { | ||
call: async ( | ||
options: RiskPayload, | ||
configuration: Configuration | ||
): Promise<RiskResult> => { | ||
const controller = new AbortController(); | ||
const command = CommandRiskService.call( | ||
controller, | ||
options, | ||
configuration | ||
); | ||
|
||
let processedResponse; | ||
try { | ||
processedResponse = await APIService.call( | ||
controller, | ||
command, | ||
configuration | ||
); | ||
} catch (e) { | ||
if (isTimeoutError(e)) { | ||
return handleFailover(options.user.id, 'timeout', configuration, e); | ||
} else if (e instanceof InternalServerError) { | ||
return handleFailover(options.user.id, 'server error', configuration); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
|
||
return processedResponse; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Configuration } from '../../configuraton'; | ||
import { ContextSanitizeService } from '../../context/context.module'; | ||
import { FilterPayload } from '../../payload/payload.module'; | ||
import { CommandGenerateService } from './command-generate.service'; | ||
|
||
export const CommandFilterService = { | ||
call: (controller, options: FilterPayload, configuration: Configuration) => { | ||
const context = ContextSanitizeService.call(options.context); | ||
return CommandGenerateService.call( | ||
controller, | ||
'filter', | ||
{ ...options, ...{ context } }, | ||
'POST', | ||
configuration | ||
); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Configuration } from '../../configuraton'; | ||
import { ContextSanitizeService } from '../../context/context.module'; | ||
import { LogPayload } from '../../payload/payload.module'; | ||
import { CommandGenerateService } from './command-generate.service'; | ||
|
||
export const CommandLogService = { | ||
call: (controller, options: LogPayload, configuration: Configuration) => { | ||
const context = ContextSanitizeService.call(options.context); | ||
return CommandGenerateService.call( | ||
controller, | ||
'log', | ||
{ ...options, ...{ context } }, | ||
'POST', | ||
configuration | ||
); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Configuration } from '../../configuraton'; | ||
import { ContextSanitizeService } from '../../context/context.module'; | ||
import { RiskPayload } from '../../payload/payload.module'; | ||
import { CommandGenerateService } from './command-generate.service'; | ||
|
||
export const CommandRiskService = { | ||
call: (controller, options: RiskPayload, configuration: Configuration) => { | ||
const context = ContextSanitizeService.call(options.context); | ||
return CommandGenerateService.call( | ||
controller, | ||
'risk', | ||
{ ...options, ...{ context } }, | ||
'POST', | ||
configuration | ||
); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { RiskPolicy } from './risk-policy'; | ||
import { Verdict } from './verdict'; | ||
|
||
export type FilterResult = { | ||
|
||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { RiskPolicy } from './risk-policy'; | ||
import { Verdict } from './verdict'; | ||
|
||
export type LogResult = { | ||
|
||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { RiskPolicy } from './risk-policy'; | ||
import { Verdict } from './verdict'; | ||
|
||
export type RiskResult = { | ||
|
||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { IncomingHttpHeaders } from 'http2'; | ||
|
||
export type FilterPayload = { | ||
request_token?: string; | ||
event?: string; | ||
user?: { | ||
id: string; | ||
email: string; | ||
}; | ||
properties?: object; | ||
created_at?: string; | ||
context?: { | ||
ip: string; | ||
headers: IncomingHttpHeaders; | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { IncomingHttpHeaders } from 'http2'; | ||
|
||
export type LogPayload = { | ||
request_token?: string; | ||
event?: string; | ||
status?: string; | ||
user?: { | ||
id: string; | ||
email: string; | ||
}; | ||
properties?: object; | ||
created_at?: string; | ||
context?: { | ||
ip: string; | ||
headers: IncomingHttpHeaders; | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { IncomingHttpHeaders } from 'http2'; | ||
|
||
export type RiskPayload = { | ||
request_token?: string; | ||
event?: string; | ||
status?: string; | ||
user?: { | ||
id: string; | ||
email: string; | ||
}; | ||
properties?: object; | ||
created_at?: string; | ||
context?: { | ||
ip: string; | ||
headers: IncomingHttpHeaders; | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import { APIFilterService } from '../../../src/api/api.module'; | ||
import { Configuration } from '../../../src/configuraton'; | ||
import { EVENTS } from '../../../src/events'; | ||
import MockDate from 'mockdate'; | ||
import fetchMock from 'fetch-mock'; | ||
|
||
describe('APIFilterService', () => { | ||
beforeEach(() => { | ||
MockDate.set(new Date('2021-01-25T00:00:00.000Z')); | ||
}); | ||
|
||
afterEach(() => { | ||
MockDate.reset(); | ||
}); | ||
|
||
const sampleRequestData = { | ||
event: EVENTS.LOGIN_SUCCEEDED, | ||
created_at: 'now', | ||
user_id: 'userid', | ||
user_traits: { | ||
email: 'myemail', | ||
updated_at: 'today', | ||
}, | ||
context: { | ||
ip: '8.8.8.8', | ||
headers: {}, | ||
}, | ||
}; | ||
|
||
describe('call', () => { | ||
it('handles allow response', async () => { | ||
const fetch = fetchMock.sandbox().mock('*', { | ||
action: 'allow', | ||
device_token: 'device_token', | ||
user_id: 'user_id', | ||
}); | ||
|
||
const config = new Configuration({ | ||
apiSecret: 'test', | ||
overrideFetch: fetch, | ||
logger: { info: () => {} }, | ||
}); | ||
|
||
const response = await APIFilterService.call( | ||
sampleRequestData, | ||
config | ||
); | ||
expect(response).toHaveProperty('action', 'allow'); | ||
expect(response).toHaveProperty('device_token', 'device_token'); | ||
expect(response).toHaveProperty('user_id', 'user_id'); | ||
}); | ||
|
||
it('handles deny response without risk policy', async () => { | ||
const fetch = fetchMock.sandbox().mock('*', { | ||
action: 'deny', | ||
device_token: 'device_token', | ||
user_id: 'user_id', | ||
}); | ||
|
||
const config = new Configuration({ | ||
apiSecret: 'test', | ||
overrideFetch: fetch, | ||
logger: { info: () => {} }, | ||
}); | ||
|
||
const response = await APIFilterService.call( | ||
sampleRequestData, | ||
config | ||
); | ||
expect(response).toHaveProperty('action', 'deny'); | ||
expect(response).toHaveProperty('device_token', 'device_token'); | ||
expect(response).toHaveProperty('user_id', 'user_id'); | ||
}); | ||
|
||
it('handles deny response with risk policy', async () => { | ||
const fetch = fetchMock.sandbox().mock('*', { | ||
action: 'deny', | ||
device_token: 'device_token', | ||
user_id: 'user_id', | ||
risk_policy: { | ||
id: 'q-rbeMzBTdW2Fd09sbz55A', | ||
revision_id: 'pke4zqO2TnqVr-NHJOAHEg', | ||
name: 'Block Users from X', | ||
type: 'bot', | ||
}, | ||
}); | ||
|
||
const config = new Configuration({ | ||
apiSecret: 'test', | ||
overrideFetch: fetch, | ||
logger: { info: () => {} }, | ||
}); | ||
|
||
const response = await APIFilterService.call( | ||
sampleRequestData, | ||
config | ||
); | ||
expect(response).toHaveProperty('action', 'deny'); | ||
expect(response).toHaveProperty('device_token', 'device_token'); | ||
expect(response).toHaveProperty('user_id', 'user_id'); | ||
expect(response.risk_policy).toHaveProperty( | ||
'id', | ||
'q-rbeMzBTdW2Fd09sbz55A' | ||
); | ||
expect(response.risk_policy).toHaveProperty( | ||
'revision_id', | ||
'pke4zqO2TnqVr-NHJOAHEg' | ||
); | ||
expect(response.risk_policy).toHaveProperty('type', 'bot'); | ||
expect(response.risk_policy).toHaveProperty('name', 'Block Users from X'); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.