feat(frontend): LearningObjectService en LearningPathService geïmplementeerd.
This commit is contained in:
parent
8b0fc4263f
commit
3c3fddb7d0
24 changed files with 375 additions and 84 deletions
10
frontend/src/services/api-client/api-client.ts
Normal file
10
frontend/src/services/api-client/api-client.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import axios from "axios";
|
||||
import { apiConfig } from "@/config.ts";
|
||||
|
||||
const apiClient = axios.create({
|
||||
baseURL: apiConfig.baseUrl,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
export default apiClient;
|
10
frontend/src/services/api-client/api-exceptions.d.ts
vendored
Normal file
10
frontend/src/services/api-client/api-exceptions.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
import type {AxiosResponse} from "axios";
|
||||
|
||||
export class HttpErrorStatusException extends Error {
|
||||
public readonly statusCode: number;
|
||||
|
||||
constructor(response: AxiosResponse<any, any>) {
|
||||
super(`${response.statusText} (${response.status})`);
|
||||
this.statusCode = response.status;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import {type Params, RestEndpoint} from "@/services/api-client/endpoints/rest-endpoint.ts";
|
||||
import {RemoteResource} from "@/services/api-client/remote-resource.ts";
|
||||
|
||||
export class DeleteEndpoint<PP extends Params, QP extends Params, R> extends RestEndpoint<PP, QP, undefined, R> {
|
||||
readonly method = "GET";
|
||||
|
||||
public delete(pathParams: PP, queryParams: QP): RemoteResource<R> {
|
||||
return super.request(pathParams, queryParams, undefined);
|
||||
}
|
||||
}
|
10
frontend/src/services/api-client/endpoints/get-endpoint.ts
Normal file
10
frontend/src/services/api-client/endpoints/get-endpoint.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import {type Params, RestEndpoint} from "@/services/api-client/endpoints/rest-endpoint.ts";
|
||||
import {RemoteResource} from "@/services/api-client/remote-resource.ts";
|
||||
|
||||
export class GetEndpoint<PP extends Params, QP extends Params, R> extends RestEndpoint<PP, QP, undefined, R> {
|
||||
readonly method = "GET";
|
||||
|
||||
public get(pathParams: PP, queryParams: QP): RemoteResource<R> {
|
||||
return super.request(pathParams, queryParams, undefined);
|
||||
}
|
||||
}
|
10
frontend/src/services/api-client/endpoints/post-endpoint.ts
Normal file
10
frontend/src/services/api-client/endpoints/post-endpoint.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
import {type Params, RestEndpoint} from "@/services/api-client/endpoints/rest-endpoint.ts";
|
||||
import {RemoteResource} from "@/services/api-client/remote-resource.ts";
|
||||
|
||||
export class PostEndpoint<PP extends Params, QP extends Params, B, R> extends RestEndpoint<PP, QP, B, R> {
|
||||
readonly method = "POST";
|
||||
|
||||
public post(pathParams: PP, queryParams: QP, body: B): RemoteResource<R> {
|
||||
return super.request(pathParams, queryParams, body);
|
||||
}
|
||||
}
|
30
frontend/src/services/api-client/endpoints/rest-endpoint.ts
Normal file
30
frontend/src/services/api-client/endpoints/rest-endpoint.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import {RemoteResource} from "@/services/api-client/remote-resource.ts";
|
||||
import apiClient from "@/services/api-client/api-client.ts";
|
||||
import {HttpErrorStatusException} from "@/services/api-client/api-exceptions";
|
||||
|
||||
export abstract class RestEndpoint<PP extends Params, QP extends Params, B, R> {
|
||||
public abstract readonly method: "GET" | "POST" | "PUT" | "DELETE";
|
||||
constructor(public readonly url: string) {
|
||||
}
|
||||
|
||||
protected request(pathParams: PP, queryParams: QP, body: B): RemoteResource<R> {
|
||||
let urlFilledIn = this.url;
|
||||
urlFilledIn.replace(/:(\w+)([/$])/g, (_, key, after) =>
|
||||
(key in pathParams ? encodeURIComponent(pathParams[key]) : `:${key}`) + after
|
||||
);
|
||||
return new RemoteResource(async () => {
|
||||
const response = await apiClient.request<R>({
|
||||
url: urlFilledIn,
|
||||
method: this.method,
|
||||
params: queryParams,
|
||||
data: body,
|
||||
});
|
||||
if (response.status / 100 !== 2) {
|
||||
throw new HttpErrorStatusException(response);
|
||||
}
|
||||
return response.data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export type Params = {[key: string]: string | number | boolean};
|
70
frontend/src/services/api-client/remote-resource.ts
Normal file
70
frontend/src/services/api-client/remote-resource.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
export class RemoteResource<T> {
|
||||
static NOT_LOADED: NotLoadedState = {type: "notLoaded"};
|
||||
static LOADING: LoadingState = {type: "loading"};
|
||||
|
||||
private state: NotLoadedState | LoadingState | ErrorState | SuccessState<T> = RemoteResource.NOT_LOADED;
|
||||
|
||||
constructor(private readonly requestFn: () => Promise<T>) {
|
||||
}
|
||||
|
||||
public async request(): Promise<T | undefined> {
|
||||
this.state = RemoteResource.LOADING;
|
||||
try {
|
||||
let resource = await this.requestFn();
|
||||
this.state = {
|
||||
type: "success",
|
||||
data: resource
|
||||
};
|
||||
return resource;
|
||||
} catch (e: any) {
|
||||
this.state = {
|
||||
type: "error",
|
||||
errorCode: e.statusCode,
|
||||
message: e.message,
|
||||
error: e
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public startRequestInBackground(): RemoteResource<T> {
|
||||
this.request().then();
|
||||
return this;
|
||||
}
|
||||
|
||||
public get data(): T | undefined {
|
||||
if (this.state.type === "success") {
|
||||
return this.state.data;
|
||||
}
|
||||
}
|
||||
|
||||
public map<U>(mappingFn: (content: T) => U): RemoteResource<U> {
|
||||
return new RemoteResource<U>(async () => {
|
||||
await this.request();
|
||||
if (this.state.type === "success") {
|
||||
return mappingFn(this.state.data);
|
||||
} else if (this.state.type === "error") {
|
||||
throw this.state.error;
|
||||
} else {
|
||||
throw new Error("Fetched resource, but afterwards, it was neither in a success nor in an error state. " +
|
||||
"This should never happen.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
type NotLoadedState = {
|
||||
type: "notLoaded"
|
||||
};
|
||||
type LoadingState = {
|
||||
type: "loading"
|
||||
};
|
||||
type ErrorState = {
|
||||
type: "error",
|
||||
errorCode?: number,
|
||||
message?: string,
|
||||
error: any
|
||||
};
|
||||
type SuccessState<T> = {
|
||||
type: "success",
|
||||
data: T
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue