Skip to content

Commit

Permalink
Merge pull request #118 from fingerprintjs/split-schema-INTER-861
Browse files Browse the repository at this point in the history
Split schema
  • Loading branch information
ilfa authored Sep 6, 2024
2 parents 1c2ec7f + e17d927 commit 43e8ed4
Show file tree
Hide file tree
Showing 111 changed files with 2,600 additions and 2,143 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Code of demo application is in [src](src) folder.
The schema is currently created separately from the API code. This is a bad practice, but we will fix it in future API versions.
To validate that schema matches the actual API implementation we use a [special validation script](/bin/validateSchema.ts).

- It validates the schema against JSON examples file from [examples](/examples) folder. Note that some files in the examples folder appear unused like `get_event_extra_fields.json`, but they are downloaded and used for validation by individual SDK repositories.
- It validates the schema against JSON examples file from [examples](/schemas/paths/examples) folder. Note that some files in the examples folder appear unused like `get_event_extra_fields.json`, but they are downloaded and used for validation by individual SDK repositories.
- It also generates fresh identification events using the TEST_SUBSCRIPTION [env variable](./.env.example), retrieves fresh Server API responses and validates the schema against those.
- You can run `pnpm run validateSchema` to validate the schema locally.
- You can create a `.env` file according to `.env.example` and set the TEST_SUBSCRIPTIONS variable to include your personal Fingerprint subscription.
Expand Down
70 changes: 38 additions & 32 deletions bin/validateSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type GetRelatedVisitorsArgs = {
visitorId: string;
subscription: TestSubscription;
};

function getRelatedVisitors({ visitorId, subscription }: GetRelatedVisitorsArgs) {
const regionPrefix = subscription.region === 'us' ? '' : `${subscription.region}.`;
return fetch(`https://${regionPrefix}api.fpjs.io/related-visitors?visitor_id=${visitorId}`, {
Expand Down Expand Up @@ -99,6 +100,7 @@ type UpdateEventArgs = {
subscription: TestSubscription;
payload: any;
};

function updateEventRequest({ requestId, subscription, payload }: UpdateEventArgs) {
const regionPrefix = subscription.region === 'us' ? '' : `${subscription.region}.`;
return fetch(`https://${regionPrefix}api.fpjs.io/events/${requestId}`, {
Expand All @@ -118,13 +120,13 @@ async function validateEventResponseSchema(testSubscriptions: TestSubscription[]

// Validate against example files
[
'./examples/get_event_200.json',
'./examples/get_event_200_all_errors.json',
'./examples/get_event_200_botd_failed_error.json',
'./examples/get_event_200_botd_too_many_requests_error.json',
'./examples/get_event_200_identification_failed_error.json',
'./examples/get_event_200_identification_too_many_requests_error.json',
'./examples/get_event_200_identification_too_many_requests_error_all_fields.json',
'./schemas/paths/examples/get_event_200.json',
'./schemas/paths/examples/get_event_200_all_errors.json',
'./schemas/paths/examples/get_event_200_botd_failed_error.json',
'./schemas/paths/examples/get_event_200_botd_too_many_requests_error.json',
'./schemas/paths/examples/get_event_200_identification_failed_error.json',
'./schemas/paths/examples/get_event_200_identification_too_many_requests_error.json',
'./schemas/paths/examples/get_event_200_identification_too_many_requests_error_all_fields.json',
].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
Expand Down Expand Up @@ -165,7 +167,10 @@ export async function validateVisitsResponseSchema(testSubscriptions: TestSubscr
const visitsResponseValidator = ajv.compile(visitsResponseSchema);

// Validate against example files
['./examples/get_visits_200_limit_1.json', './examples/get_visits_200_limit_500.json'].forEach((examplePath) =>
[
'./schemas/paths/examples/get_visits_200_limit_1.json',
'./schemas/paths/examples/get_visits_200_limit_500.json',
].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
jsonName: examplePath,
Expand Down Expand Up @@ -205,7 +210,7 @@ async function validateWebhookSchema() {
const webhookValidator = ajv.compile(webhookSchema);

// Validate against example file
['./examples/webhook.json'].forEach((examplePath) =>
['./schemas/paths/examples/webhook.json'].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
jsonName: examplePath,
Expand All @@ -226,12 +231,12 @@ async function validateCommonError403Schema(testSubscriptions: TestSubscription[

// Validate against example file
[
'./examples/get_event_403_error.json',
'./examples/shared/403_error_feature_not_enabled.json',
'./examples/shared/403_error_token_not_found.json',
'./examples/shared/403_error_token_required.json',
'./examples/shared/403_error_wrong_region.json',
'./examples/update_event_403_error.json',
'./schemas/paths/examples/get_event_403_error.json',
'./schemas/paths/examples/shared/403_error_feature_not_enabled.json',
'./schemas/paths/examples/shared/403_error_token_not_found.json',
'./schemas/paths/examples/shared/403_error_token_required.json',
'./schemas/paths/examples/shared/403_error_wrong_region.json',
'./schemas/paths/examples/update_event_403_error.json',
].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
Expand Down Expand Up @@ -321,13 +326,14 @@ async function validateEventError404Schema(testSubscriptions: TestSubscription[]
const eventError404Validator = ajv.compile(eventError404Schema);

// Validate against example file
['./examples/get_event_404_error.json', './examples/update_event_404_error.json'].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
jsonName: examplePath,
validator: eventError404Validator,
schemaName: 'EventError404Schema',
})
['./schemas/paths/examples/get_event_404_error.json', './schemas/paths/examples/update_event_404_error.json'].forEach(
(examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
jsonName: examplePath,
validator: eventError404Validator,
schemaName: 'EventError404Schema',
})
);

const nonExistentRequestId = 'non-existent-request-id';
Expand Down Expand Up @@ -385,7 +391,7 @@ async function validateGetVisitsError403Schema(testSubscriptions: TestSubscripti
const visitsError403Validator = ajv.compile(visitsError403Schema);

// Validate against example file
['./examples/get_visits_403_error.json'].forEach((examplePath) =>
['./schemas/paths/examples/get_visits_403_error.json'].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
jsonName: examplePath,
Expand Down Expand Up @@ -427,7 +433,7 @@ async function validateGetVisitsError429Schema() {
const visitsError429Validator = ajv.compile(visitsError429Schema);

// Validate against example file
['./examples/get_visits_429_too_many_requests_error.json'].forEach((examplePath) =>
['./schemas/paths/examples/get_visits_429_too_many_requests_error.json'].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
jsonName: examplePath,
Expand All @@ -449,7 +455,7 @@ async function validateErrorCommon429Response() {
const errorCommon429ResponseValidator = ajv.compile(errorCommon429ResponseSchema);

// Validate against example file
['./examples/shared/429_error_too_many_requests.json'].forEach((examplePath) =>
['./schemas/paths/examples/shared/429_error_too_many_requests.json'].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
jsonName: examplePath,
Expand All @@ -470,8 +476,8 @@ async function validateErrorVisitor400Response(testSubscriptions: TestSubscripti

// Validate against example file
[
'./examples/shared/400_error_incorrect_visitor_id.json',
'./examples/shared/400_error_empty_visitor_id.json',
'./schemas/paths/examples/shared/400_error_incorrect_visitor_id.json',
'./schemas/paths/examples/shared/400_error_empty_visitor_id.json',
].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
Expand Down Expand Up @@ -525,7 +531,7 @@ async function validateErrorVisitor404Response(testSubscriptions: TestSubscripti
const visitorError404Validator = ajv.compile(visitorError404Schema);

// Validate against example file
['./examples/shared/404_error_visitor_not_found.json'].forEach((examplePath) =>
['./schemas/paths/examples/shared/404_error_visitor_not_found.json'].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
jsonName: examplePath,
Expand Down Expand Up @@ -579,8 +585,8 @@ async function validateRelatedVisitorsResponseSchema(testSubscriptions: TestSubs

// Validate against example file
[
'./examples/related-visitors/get_related_visitors_200_empty.json',
'./examples/related-visitors/get_related_visitors_200.json',
'./schemas/paths/examples/related-visitors/get_related_visitors_200_empty.json',
'./schemas/paths/examples/related-visitors/get_related_visitors_200.json',
].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
Expand Down Expand Up @@ -612,7 +618,7 @@ async function validateUpdateEventError400Schema(testSubscriptions: TestSubscrip
const updateEvent400ErrorValidator = ajv.compile(updateEvent400ErrorSchema);

// Validate against example file
['./examples/update_event_400_error.json'].forEach((examplePath) =>
['./schemas/paths/examples/update_event_400_error.json'].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
jsonName: examplePath,
Expand Down Expand Up @@ -659,7 +665,7 @@ async function validateUpdateEventError409Schema(testSubscriptions: TestSubscrip
const updateEvent409ErrorValidator = ajv.compile(updateEvent409ErrorSchema);

// Validate against example file
['./examples/update_event_409_error.json'].forEach((examplePath) =>
['./schemas/paths/examples/update_event_409_error.json'].forEach((examplePath) =>
validateJson({
json: JSON.parse(fs.readFileSync(examplePath).toString()),
jsonName: examplePath,
Expand Down
16 changes: 16 additions & 0 deletions schemas/components/schemas/ASN.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
type: object
additionalProperties: false
properties:
asn:
type: string
example: '7922'
network:
type: string
example: 73.136.0.0/13
name:
type: string
example: COMCAST-7922
required:
- asn
- network
title: ASN
21 changes: 21 additions & 0 deletions schemas/components/schemas/BotdDetectionResult.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
type: object
additionalProperties: false
description: Stores bot detection result
properties:
result:
type: string
description: |
Bot detection result:
* `notDetected` - the visitor is not a bot
* `good` - good bot detected, such as Google bot, Baidu Spider, AlexaBot and so on
* `bad` - bad bot detected, such as Selenium, Puppeteer, Playwright, headless browsers, and so on
enum:
- notDetected
- good
- bad
example: bad
type:
type: string
example: selenium
required:
- result
42 changes: 42 additions & 0 deletions schemas/components/schemas/BotdResult.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
type: object
additionalProperties: false
description: Contains all the information from Bot Detection product
properties:
ip:
type: string
format: ipv4
example: 8.8.8.8
description: IP address of the requesting browser or bot.
time:
title: Time
description: >-
Time in UTC when the request from the JS agent was made. We recommend to
treat requests that are older than 2 minutes as malicious. Otherwise,
request replay attacks are possible
type: string
format: date-time
example: '2022-06-09T22:58:36Z'
url:
description: Page URL from which identification request was sent.
type: string
example: https://example.com/login
userAgent:
type: string
example: >-
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML,
like Gecko) Chrome/111.0.0.0 Safari/537.36
requestId:
type: string
example: 1681392853693.lRiBBD
linkedId:
type: string
example: Automatic tests bot
bot:
$ref: BotdDetectionResult.yaml
required:
- bot
- url
- ip
- time
- userAgent
- requestId
37 changes: 37 additions & 0 deletions schemas/components/schemas/BrowserDetails.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
type: object
additionalProperties: false
properties:
browserName:
type: string
example: Chrome
browserMajorVersion:
type: string
example: '101'
browserFullVersion:
type: string
example: 101.0.4951
os:
type: string
example: Windows
osVersion:
type: string
example: '10'
device:
type: string
example: Other
userAgent:
type: string
example: >-
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/101.0.4951.41 Safari/537.36
botProbability:
type: integer
required:
- browserFullVersion
- browserMajorVersion
- browserName
- device
- os
- osVersion
- userAgent
title: BrowserDetails
14 changes: 14 additions & 0 deletions schemas/components/schemas/ClonedAppResult.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
type: object
additionalProperties: false
properties:
result:
type: boolean
description: >
Android specific cloned application detection. There are 2 values: •
`true` - Presence of app cloners work detected (e.g. fully cloned
application found or launch of it inside of a not main working profile
detected). • `false` - No signs of cloned application detected or the
client is not Android.
example: false
required:
- result
20 changes: 20 additions & 0 deletions schemas/components/schemas/Confidence.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
type: object
additionalProperties: false
properties:
score:
description: >-
The confidence score is a floating-point number between 0 and 1 that
represents the probability of accurate identification.
type: number
format: float
minimum: 0
maximum: 1
revision:
description: >-
The revision name of the method used to calculate the Confidence score.
This field is only present for customers who opted in to an alternative
calculation method.
type: string
required:
- score
title: Confidence
11 changes: 11 additions & 0 deletions schemas/components/schemas/DataCenter.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type: object
additionalProperties: false
properties:
result:
type: boolean
name:
type: string
example: DediPath
required:
- result
title: DataCenter
11 changes: 11 additions & 0 deletions schemas/components/schemas/DeveloperToolsResult.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
type: object
additionalProperties: false
properties:
result:
type: boolean
description: >
`true` if the browser is Chrome with DevTools open or Firefox with
Developer Tools open, `false` otherwise.
example: false
required:
- result
12 changes: 12 additions & 0 deletions schemas/components/schemas/EmulatorResult.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
type: object
additionalProperties: false
properties:
result:
type: boolean
description: >
Android specific emulator detection. There are 2 values: • `true` -
Emulated environment detected (e.g. launch inside of AVD) • `false` - No
signs of emulated environment detected or the client is not Android.
example: false
required:
- result
Loading

0 comments on commit 43e8ed4

Please sign in to comment.