events & initialize

This commit is contained in:
Julian Krauser 2025-02-06 09:38:30 +01:00
parent 3f7fc8a147
commit afbcae4add
5 changed files with 73 additions and 9 deletions

View file

@ -6,7 +6,7 @@ Zur Verfügung gestellt werden Requests mit den benötigten Models, Types und En
Der WebApiClient realisiert den automatischen refresh von Zugangs-Tokens. Der WebApiClient realisiert den automatischen refresh von Zugangs-Tokens.
Beispiele:\ Beispiele:
1. Verwendung des Clients: 1. Verwendung des Clients:
``` ts ``` ts
import { WebApiClient, AdminRequests } from "@ff-admin/webapi-client" import { WebApiClient, AdminRequests } from "@ff-admin/webapi-client"
@ -15,7 +15,10 @@ let myClient = new WebApiClient({
serverAdress: "", serverAdress: "",
webapiToken: "" webapiToken: ""
}); });
myClient.initialize() // asynchrone Funktion - muss vollständig ausgeführt sein, bevor irgendwelche Anfragen gestellt werden
```
2. Nutzung der Requests
```ts
let data = AdminRequests.MemberRequests.getAllMembers(myClient) let data = AdminRequests.MemberRequests.getAllMembers(myClient)
.then((res) => { .then((res) => {
console.log(res) console.log(res)
@ -49,12 +52,24 @@ AdminRequests.MemberRequests.updateMember(myClient, {
.then(() => {}) .then(() => {})
.catch(() => {}); .catch(() => {});
``` ```
3. Zugriff auf Typen, Models und Enums:
2. Zugriff auf Typen, Models und Enums:
``` ts ``` ts
import { AdminModels, AdminTypes, AdminEnums } from "@ff-admin/webapi-client" import { AdminModels, AdminTypes, AdminEnums } from "@ff-admin/webapi-client"
let model: AdminModels.MemberModels.CreateMemberViewModel; let model: AdminModels.MemberModels.CreateMemberViewModel;
let type: AdminTypes.DynamicQueries.DynamicQueryStructure let type: AdminTypes.DynamicQueries.DynamicQueryStructure
let enum = AdminEnums.NewsletterType.NewsletterConfigType.mail let enum = AdminEnums.NewsletterType.NewsletterConfigType.mail
```
4. http Events:
Es können Events wie Fehler oder Informationen direkt aus der Middleware des Clients angezapft werden.
``` ts
import { WebApiClient, AdminRequests } from "@ff-admin/webapi-client"
let myClient = new WebApiClient({
serverAdress: "",
webapiToken: ""
});
myClient.initialize()
client.httpEvents.on(<"error" | "info">, (data) => {})
``` ```

View file

@ -12,6 +12,12 @@ export class BaseClient extends Config {
return this.httpInstance.http; return this.httpInstance.http;
} }
get httpEvents() {
return this.httpInstance.httpEvents;
}
public async initialize(): Promise<void> {}
public refreshToken(): Promise<void> { public refreshToken(): Promise<void> {
return new Promise<void>(async (resolve, reject) => { return new Promise<void>(async (resolve, reject) => {
reject("api token refresh not implemented"); reject("api token refresh not implemented");

View file

@ -5,8 +5,8 @@ export class WebApiClient extends BaseClient {
super({ serverAdress, webapiToken }); super({ serverAdress, webapiToken });
} }
get http() { public async initialize(): Promise<void> {
return this.httpInstance.http; await this.refreshToken();
} }
public refreshToken(): Promise<void> { public refreshToken(): Promise<void> {

View file

@ -1,9 +1,16 @@
import axios, { AxiosInstance } from "axios"; import axios, { AxiosInstance } from "axios";
import { BaseClient } from "./clients/clientBase"; import { BaseClient } from "./clients/clientBase";
import { EventEmitterReadonly, TypedEventEmitter } from "./utils/typedEmitter";
type HttpEvents = {
info: [arg1: string];
error: [arg1: { code: string; message: string }];
};
export default class HTTP { export default class HTTP {
public http: AxiosInstance; public readonly http: AxiosInstance;
private client: BaseClient; protected readonly httpEmitter: TypedEventEmitter<HttpEvents>;
protected client: BaseClient;
constructor(client: BaseClient) { constructor(client: BaseClient) {
this.client = client; this.client = client;
@ -15,15 +22,20 @@ export default class HTTP {
Expires: "0", Expires: "0",
}, },
}); });
this.httpEmitter = new TypedEventEmitter<HttpEvents>();
this.setRequestInterceptor(); this.setRequestInterceptor();
this.setResponseInterceptor(); this.setResponseInterceptor();
} }
get httpEvents(): EventEmitterReadonly<HttpEvents> {
return this.httpEmitter;
}
private setRequestInterceptor() { private setRequestInterceptor() {
this.http.interceptors.request.use( this.http.interceptors.request.use(
(config) => { (config) => {
if (config.headers && config.headers.Authorization == "") { if (config.headers && !config.headers.Authorization) {
config.headers.Authorization = `Bearer ${this.client.accessToken}`; config.headers.Authorization = `Bearer ${this.client.accessToken}`;
} }
@ -53,11 +65,16 @@ export default class HTTP {
return await this.client return await this.client
.refreshToken() .refreshToken()
.then(() => { .then(() => {
this.httpEmitter.emit("info", "successfully refreshed token");
return this.http(originalRequest); return this.http(originalRequest);
}) })
.catch(() => {}); .catch(() => {});
} }
this.httpEmitter.emit("error", {
code: error.code,
message: error.message,
});
return Promise.reject(error); return Promise.reject(error);
} }
); );

26
src/utils/typedEmitter.ts Normal file
View file

@ -0,0 +1,26 @@
import EventEmitter = require("node:events");
export interface EventEmitterReadonly<TEvents extends Record<string, any>> {
on<TEventName extends keyof TEvents & string>(eventName: TEventName, handler: (...eventArg: TEvents[TEventName]) => void): void;
off<TEventName extends keyof TEvents & string>(eventName: TEventName, handler: (...eventArg: TEvents[TEventName]) => void): void;
}
export interface EventEmitterFull<TEvents extends Record<string, any>> extends EventEmitterReadonly<TEvents> {
emit<TEventName extends keyof TEvents & string>(eventName: TEventName, ...eventArg: TEvents[TEventName]): void;
}
export class TypedEventEmitter<TEvents extends Record<string, any>> implements EventEmitterFull<TEvents> {
private emitter = new EventEmitter();
emit<TEventName extends keyof TEvents & string>(eventName: TEventName, ...eventArg: TEvents[TEventName]) {
this.emitter.emit(eventName, ...(eventArg as []));
}
on<TEventName extends keyof TEvents & string>(eventName: TEventName, handler: (...eventArg: TEvents[TEventName]) => void) {
this.emitter.on(eventName, handler as any);
}
off<TEventName extends keyof TEvents & string>(eventName: TEventName, handler: (...eventArg: TEvents[TEventName]) => void) {
this.emitter.off(eventName, handler as any);
}
}