Skip to content

Commit

Permalink
Merge pull request #143 from fingerprintjs/feat/open-api-v1.3.0
Browse files Browse the repository at this point in the history
OpenAPI schema sync
  • Loading branch information
TheUnderScorer authored Oct 23, 2024
2 parents 216b6f4 + 943357d commit 9cf016a
Show file tree
Hide file tree
Showing 31 changed files with 1,602 additions and 266 deletions.
5 changes: 5 additions & 0 deletions .changeset/floppy-mice-count.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@fingerprintjs/fingerprintjs-pro-server-api': minor
---

**related-visitors**: Add GET `/related-visitors` endpoint
5 changes: 5 additions & 0 deletions .changeset/orange-taxis-fetch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@fingerprintjs/fingerprintjs-pro-server-api': minor
---

**visitors**: Add the confidence field to the VPN Detection Smart Signal
9 changes: 9 additions & 0 deletions .changeset/pre.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"mode": "pre",
"tag": "test",
"initialVersions": {
"@fingerprintjs/fingerprintjs-pro-server-api": "5.1.0",
"fingerprintjs-pro-server-api-node-sdk-example": "1.0.0"
},
"changesets": []
}
5 changes: 5 additions & 0 deletions .changeset/ten-taxis-joke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@fingerprintjs/fingerprintjs-pro-server-api': minor
---

Deprecate `getVisitorHistory` method. Use `getVisits` instead.
5 changes: 5 additions & 0 deletions .changeset/three-files-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@fingerprintjs/fingerprintjs-pro-server-api': minor
---

**events**: Add `antiDetectBrowser` detection method to the `tampering` Smart Signal.
2 changes: 1 addition & 1 deletion .schema-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v1.1.0
v1.3.0
47 changes: 47 additions & 0 deletions example/relatedVisitors.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import {
FingerprintJsServerApiClient,
Region,
isRelatedVisitorsError,
} from '@fingerprintjs/fingerprintjs-pro-server-api'
import { config } from 'dotenv'
config()

const apiKey = process.env.API_KEY
const visitorId = process.env.VISITOR_ID
const envRegion = process.env.REGION

if (!visitorId) {
console.error('Visitor ID not defined')
process.exit(1)
}

if (!apiKey) {
console.error('API key not defined')
process.exit(1)
}

let region = Region.Global
if (envRegion === 'eu') {
region = Region.EU
} else if (envRegion === 'ap') {
region = Region.AP
}

const client = new FingerprintJsServerApiClient({ region, apiKey })

try {
const relatedVisitors = await client.getRelatedVisitors({
visitor_id: visitorId,
})

console.log(JSON.stringify(relatedVisitors, null, 2))
} catch (error) {
if (isRelatedVisitorsError(error)) {
console.log(`error ${error.statusCode}: `, error.message)
// You can also access the raw response
console.log(error.response.statusText)
} else {
console.log('unknown error: ', error)
}
process.exit(1)
}
120 changes: 115 additions & 5 deletions resources/fingerprint-server-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -510,11 +510,13 @@ paths:
publicVPN: false
auxiliaryMobile: false
osMismatch: false
confidence: high
proxy:
result: false
tampering:
result: false
anomalyScore: 0
antiDetectBrowser: false
clonedApp:
result: false
factoryReset:
Expand Down Expand Up @@ -576,6 +578,69 @@ paths:
responses:
default:
description: The server doesn't validate the answer.
/related-visitors:
get:
tags:
- Fingerprint
operationId: getRelatedVisitors
summary: Get Related Visitors
description: >
This API will enable you to find if one or more web and in-app browser
sessions originated from the same mobile device.
When requested, this API will search identification events within the
past 6 months to find the visitor IDs that belong to the same mobile
device as the given visitor ID.
Please visit the
[Overview](https://dev.fingerprint.com/reference/related-visitors-api)
page to learn more about this API.
parameters:
- name: visitor_id
description: >-
The [visitor
ID](https://dev.fingerprint.com/docs/js-agent#visitorid) for which
you want to find the other visitor IDs that originated from the same
mobile device.
in: query
required: true
schema:
type: string
responses:
'200':
description: OK.
content:
application/json:
schema:
$ref: '#/components/schemas/RelatedVisitorsResponse'
'400':
description: >-
Bad request. The visitor ID parameter is missing or in the wrong
format.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorVisitor400Response'
'403':
description: Forbidden. Access to this API is denied.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorCommon403Response'
'404':
description: >-
Not found. The visitor ID cannot be found in this application's
data.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorVisitor404Response'
'429':
description: Too Many Requests. The request is throttled.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorCommon429Response'
components:
securitySchemes:
ApiKeyHeader:
Expand Down Expand Up @@ -1145,10 +1210,22 @@ components:
- publicVPN
- auxiliaryMobile
- osMismatch
confidence:
type: string
enum:
- low
- medium
- high
description: >-
A confidence rating for the VPN detection result — "low", "medium",
or "high". Depends on the combination of results returned from all
VPN detection methods.
example: low
required:
- result
- originTimezone
- methods
- confidence
ProxyResult:
type: object
additionalProperties: false
Expand All @@ -1168,21 +1245,32 @@ components:
result:
type: boolean
description: >-
Flag indicating whether browser tampering was detected according to
our internal thresholds.
Flag indicating browser tampering was detected. This happens when
either of these conditions is true:
* There are inconsistencies in the browser configuration that cross our internal tampering thresholds (indicated by `anomalyScore`).
* The browser signature resembles one of "anti-detect" browsers specifically designed to evade identification and fingerprinting, for example, Incognition (indicated by `antiDetectBrowser`).
example: false
anomalyScore:
type: number
description: >-
Confidence score (`0.0 - 1.0`) for the tampering detection. Values
above `0.5` suggest that we're reasonably sure there was a tampering
attempt. Values below `0.5` are genuine browsers.
Confidence score (`0.0 - 1.0`) for tampering detection. Values above
`0.5` indicate that there was a tampering attempt. Values below
`0.5` indicate genuine browsers.
example: 0
minimum: 0
maximum: 1
antiDetectBrowser:
type: boolean
description: >-
Is `true` if the identified browser resembles one of "anti-detect"
browsers, for example, Incognition. Anti-detect browsers try to
evade identification by masking or manipulating their fingerprint to
imitate legitimate browser configurations.
example: false
required:
- result
- anomalyScore
- antiDetectBrowser
HighActivityResult:
type: object
additionalProperties: false
Expand Down Expand Up @@ -2224,3 +2312,25 @@ components:
- timestamp
- url
- visitorFound
RelatedVisitor:
type: object
additionalProperties: false
properties:
visitorId:
description: >-
Visitor ID of a browser that originates from the same mobile device
as the input visitor ID.
type: string
example: Ibk1527CUFmcnjLwIs4A
required:
- visitorId
RelatedVisitorsResponse:
type: object
additionalProperties: false
properties:
relatedVisitors:
type: array
items:
$ref: '#/components/schemas/RelatedVisitor'
required:
- relatedVisitors
124 changes: 0 additions & 124 deletions src/errors/apiErrors.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,3 @@
import {
CommonResponse429,
DeleteVisit400Response,
DeleteVisit403Response,
DeleteVisit404Response,
EventResponse403,
EventResponse404,
UpdateEventResponse400,
UpdateEventResponse403,
UpdateEventResponse404,
UpdateEventResponse409,
VisitorsResponse403,
VisitorsResponse429,
} from '../types'

export class SdkError extends Error {
constructor(
message: string,
Expand Down Expand Up @@ -49,112 +34,3 @@ export class ApiError<Code extends number = number, Body = unknown> extends SdkE
return new ApiError('Unknown error', undefined, response.status, response.statusText, response)
}
}

export class DeleteVisit404Error extends ApiError<404, DeleteVisit404Response> {
constructor(body: DeleteVisit404Response, response: Response) {
super(body.error?.message ?? 'Visit not found', body, 404, body.error?.code ?? 'VisitorNotFound', response)
}
}

export class DeleteVisit403Error extends ApiError<403, DeleteVisit403Response> {
constructor(body: DeleteVisit403Response, response: Response) {
super(body.error?.message ?? 'Forbidden', body, 403, body.error?.code ?? 'Forbidden', response)
}
}

export class DeleteVisit400Error extends ApiError<400, DeleteVisit400Response> {
constructor(body: DeleteVisit400Response, response: Response) {
super(body.error?.message ?? 'Visit not found', body, 400, body.error?.code ?? 'RequestCannotBeParsed', response)
}
}

export class CommonError429 extends ApiError<429, CommonResponse429> {
readonly retryAfter: number = 0

constructor(body: CommonResponse429, response: Response) {
super(body.error?.message ?? 'Too many requests', body, 429, body.error?.code ?? 'TooManyRequests', response)

this.retryAfter = getRetryAfter(response)
}
}

export class VisitorsError429 extends ApiError<429, VisitorsResponse429> {
readonly retryAfter: number = 0

constructor(body: VisitorsResponse429, response: Response) {
super(body.error, body, 429, 'TooManyRequests', response)
this.retryAfter = getRetryAfter(response)
}
}

export class VisitorsError403 extends ApiError<403, VisitorsResponse403> {
constructor(body: VisitorsResponse403, response: Response) {
super(body.error, body, 403, 'Forbidden', response)
}
}

export class EventError403 extends ApiError<403, EventResponse403> {
constructor(body: EventResponse403, response: Response) {
super(body.error?.message ?? 'Forbidden', body, 403, body.error?.code ?? 'Forbidden', response)
}
}

export class EventError404 extends ApiError<404, EventResponse404> {
constructor(body: EventResponse404, response: Response) {
super(body.error?.message ?? 'request id is not found', body, 404, body.error?.code ?? 'RequestNotFound', response)
}
}

export class UpdateEventError400 extends ApiError<400, UpdateEventResponse400> {
constructor(body: UpdateEventResponse400, response: Response) {
super(body.error?.message ?? 'Bad request', body, 400, body.error?.code ?? 'BadRequest', response)
}
}

export class UpdateEventError403 extends ApiError<403, UpdateEventResponse403> {
constructor(body: UpdateEventResponse403, response: Response) {
super(body.error?.message ?? 'Forbidden', body, 403, body.error?.code ?? 'Forbidden', response)
}
}

export class UpdateEventError404 extends ApiError<404, UpdateEventResponse404> {
constructor(body: UpdateEventResponse404, response: Response) {
super(body.error?.message ?? 'Request id is not found', body, 404, body.error?.code ?? 'RequestNotFound', response)
}
}

export class UpdateEventError409 extends ApiError<409, UpdateEventResponse409> {
constructor(body: UpdateEventResponse409, response: Response) {
super(body.error?.message ?? 'Conflict', body, 409, body.error?.code ?? 'Conflict', response)
}
}

export const DELETE_VISITS_ERRORS = [
DeleteVisit404Error,
DeleteVisit403Error,
DeleteVisit400Error,
CommonError429,
] as const
export function isDeleteVisitorsError(error: unknown): error is (typeof DELETE_VISITS_ERRORS)[number]['prototype'] {
return DELETE_VISITS_ERRORS.some((errorConstructor) => error instanceof errorConstructor)
}

export const VISITOR_ERRORS = [VisitorsError403, VisitorsError429] as const
export function isVisitorsError(error: unknown): error is (typeof VISITOR_ERRORS)[number]['prototype'] {
return VISITOR_ERRORS.some((errorConstructor) => error instanceof errorConstructor)
}

export const EVENT_ERRORS = [EventError403, EventError404] as const
export function isEventError(error: unknown): error is (typeof EVENT_ERRORS)[number]['prototype'] {
return EVENT_ERRORS.some((errorConstructor) => error instanceof errorConstructor)
}

export const UPDATE_EVENT_ERRORS = [UpdateEventError400, UpdateEventError403, UpdateEventError404, UpdateEventError409]
export function isUpdateEventError(error: unknown): error is (typeof UPDATE_EVENT_ERRORS)[number]['prototype'] {
return UPDATE_EVENT_ERRORS.some((errorConstructor) => error instanceof errorConstructor)
}

function getRetryAfter(response: Response) {
const retryAfter = parseInt(response.headers.get('retry-after') ?? '')
return Number.isNaN(retryAfter) ? 0 : retryAfter
}
Loading

0 comments on commit 9cf016a

Please sign in to comment.