Skip to content

Commit

Permalink
feat(commons): Add withEventConsistencyHandling interceptor to config…
Browse files Browse the repository at this point in the history
…ure automatic header handling for a client intstance
  • Loading branch information
Lukas Fritze committed Mar 20, 2024
1 parent c23e0af commit 9fdbd96
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 5 deletions.
13 changes: 13 additions & 0 deletions packages/commons/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,16 @@ instance.
const client = new APIClient({ baseURL });
const authenticatedClient = withToken(client, token);
```

#### withEventConsistencyHandling

To opt in into the
[event consistency handling](https://developer.mittwald.de/docs/v2/api/intro/#eventual-consistency)
you might use `withEventConsistencyHandling`. This will set automatically handle
the `etag` response header and set its value as `if-event-reached` request
header for GET requests:

```ts
const client = new APIClient({ baseURL });
const authenticatedClient = withEventConsistencyHandling(client);
```
39 changes: 39 additions & 0 deletions packages/commons/src/interceptors/consistencyHandling.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { AxiosResponseHeaders, AxiosInstance, AxiosRequestConfig } from "axios";
import ApiClientBase from "../core/ApiClientBase.js";

function isMutatingRequest(request: AxiosRequestConfig): boolean {
return ["post", "put", "delete", "patch"].includes(
request.method?.toLowerCase() ?? "",
);
}

function configureConsistencyHandlingInterceptor(axios: AxiosInstance): void {
let lastEventId: string | undefined = undefined;

axios.interceptors.request.use((config) => {
if (lastEventId !== undefined && !isMutatingRequest(config)) {
config.headers["if-event-reached"] = lastEventId;
}
return config;
});

axios.interceptors.response.use((response) => {
const headers = response.headers as AxiosResponseHeaders;

if (headers.has("etag") && isMutatingRequest(response.config)) {
lastEventId = headers.get("etag") as string;
}
return response;
});
}

export function withEventConsistencyHandling<
T extends ApiClientBase | AxiosInstance,
>(clientOrAxiosInstance: T): T {
const axiosInstance: AxiosInstance =
clientOrAxiosInstance instanceof ApiClientBase
? clientOrAxiosInstance.axios
: clientOrAxiosInstance;
configureConsistencyHandlingInterceptor(axiosInstance);
return clientOrAxiosInstance;
}
1 change: 1 addition & 0 deletions packages/commons/src/interceptors/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./accessToken.js";
export * from "./consistencyHandling.js";
6 changes: 6 additions & 0 deletions packages/mittwald/src/v2/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
AxiosHeaders,
CreateAxiosDefaults,
withAccessToken,
withEventConsistencyHandling,
} from "@mittwald/api-client-commons";
import MittwaldApiV2Client from "../generated/v2/client.js";
import { MittwaldAPIClientVersion } from "../version.js";
Expand Down Expand Up @@ -66,6 +67,11 @@ export class MittwaldAPIClient extends MittwaldApiV2Client {

throw ApiClientError.fromResponse("Login failed", authResult);
}

public withEventConsistencyHandling() {
withEventConsistencyHandling(this);
return this;
}
}

export default MittwaldAPIClient;
11 changes: 6 additions & 5 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
{
"compilerOptions": {
"esModuleInterop": true,
"stripInternal": true,
"target": "ES2022",
"declaration": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"lib": ["ES2022", "dom"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"strict": true,
"skipLibCheck": true
"stripInternal": true,
"target": "ES2022"
}
}

0 comments on commit 9fdbd96

Please sign in to comment.