Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions clients/javascript/lib/accountClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NitteiBaseClient } from './baseClient'
import { IdempotentRequest, NitteiBaseClient } from './baseClient'
import type {
AccountSearchEventsRequestBody,
SearchEventsAPIResponse,
Expand All @@ -9,6 +9,8 @@ import type { CreateAccountRequestBody } from './gen_types/CreateAccountRequestB
import type { CreateAccountResponseBody } from './gen_types/CreateAccountResponseBody'
import { convertEventDates } from './helpers/datesConverters'

const ACCOUNT_SEARCH_EVENTS_ENDPOINT = '/account/events/search'

/**
* Client for the account endpoints
* This is an admin client
Expand Down Expand Up @@ -83,9 +85,10 @@ export class NitteiAccountClient extends NitteiBaseClient {
* @param params - search parameters, check {@link AccountSearchEventsRequestBody} for more details
* @returns - the events found
*/
@IdempotentRequest(ACCOUNT_SEARCH_EVENTS_ENDPOINT)
public async searchEventsInAccount(params: AccountSearchEventsRequestBody) {
const res = await this.post<SearchEventsAPIResponse>(
'/account/events/search',
ACCOUNT_SEARCH_EVENTS_ENDPOINT,
params
)

Expand Down
40 changes: 38 additions & 2 deletions clients/javascript/lib/baseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,7 +336,7 @@ export const createAxiosInstanceFrontend = (
axiosRetry(axiosClient, {
retries: args.retry.maxRetries ?? 3,
retryDelay: axiosRetry.exponentialDelay,
retryCondition: isNetworkOrIdempotentRequestError, // Retry on network errors or idempotent requests (GET, PUT, DELETE)
retryCondition: canRequestBeRetried,
shouldResetTimeout: true,
})
}
Expand Down Expand Up @@ -419,10 +419,46 @@ export const createAxiosInstanceBackend = async (
axiosRetry(axiosClient, {
retries: args.retry.maxRetries ?? 3,
retryDelay: axiosRetry.exponentialDelay,
retryCondition: isNetworkOrIdempotentRequestError, // Retry on network errors or idempotent requests (GET, PUT, DELETE)
retryCondition: canRequestBeRetried,
shouldResetTimeout: true,
})
}

return axiosClient
}

// Idempotent endpoints (populated by the decorator)
const idempotentEndpoints = new Set<string>()

/**
* Mark the method as idempotent, which effectively adds it to the list of endpoints that can be retried without side-effects
* @param endpoint - the endpoint to mark as idempotent (url)
* @returns the decorator function
*/
export function IdempotentRequest(endpoint: string) {
// Register this endpoint as idempotent
idempotentEndpoints.add(endpoint)

return (
_target: unknown,
_propertyKey: string | symbol,
descriptor: PropertyDescriptor
): PropertyDescriptor => descriptor
}

/**
* Internal function for checking if an HTTP request can be retried without side-effects
*/
const canRequestBeRetried = (error: AxiosError): boolean => {
// Default condition
if (isNetworkOrIdempotentRequestError(error)) {
return true
}

// Allow some POST requests to be retried (e.g. searches)
if (idempotentEndpoints.has(error.config?.url ?? '')) {
return true
}

return false
}
7 changes: 5 additions & 2 deletions clients/javascript/lib/eventClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NitteiBaseClient } from './baseClient'
import { IdempotentRequest, NitteiBaseClient } from './baseClient'
import type {
CreateBatchEventsAPIResponse,
CreateBatchEventsRequestBody,
Expand Down Expand Up @@ -28,6 +28,8 @@ export type Timespan = {
endTime: Date
}

const EVENT_SEARCH_ENDPOINT = '/events/search'

/**
* Client for the events' endpoints
* This is an admin client (usually backend)
Expand Down Expand Up @@ -151,11 +153,12 @@ export class NitteiEventClient extends NitteiBaseClient {
* @param options - options - see {@link SearchEventsRequestBody} for more details
* @returns - the events found
*/
@IdempotentRequest(EVENT_SEARCH_ENDPOINT)
public async searchEvents(
options: SearchEventsRequestBody
): Promise<SearchEventsAPIResponse> {
const res = await this.post<SearchEventsAPIResponse>(
'/events/search',
EVENT_SEARCH_ENDPOINT,
options
)

Expand Down
4 changes: 3 additions & 1 deletion clients/javascript/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"sourceMap": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"resolveJsonModule": true
"resolveJsonModule": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
},
"include": ["lib/**/*", "tests/**/*", "scripts/**/*"],
"exclude": ["node_modules"]
Expand Down