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
15 changes: 11 additions & 4 deletions src/Bones.UI/core/composableFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class ComposableFactory {
return ComposableFactory.customGet(service, service.get, applyFactory);
}

public static getMany<TDetails extends TInfos, TInfos, TFilter>(service: { getMany(filter?: TFilter): Promise<TInfos[]> } & INotifyService<TDetails>, applyFactory?: () => (entities: Ref<TInfos[]>) => void) {
public static getMany<TDetails extends TInfos, TInfos, TFilter>(service: { getMany(filter?: TFilter, controller?: AbortController): Promise<TInfos[]> } & INotifyService<TDetails>, applyFactory?: () => (entities: Ref<TInfos[]>) => void) {
return ComposableFactory.customGetMany(service, service.getMany, applyFactory);
}

Expand Down Expand Up @@ -115,9 +115,11 @@ export class ComposableFactory {
* Warning : read the code before using this method, the first argument in the method is used to create a filter
* The last argument passed to the getMany composable can be a custom filter function that will override the default filter
* */
public static customGetMany<TDetails extends TInfos, TInfos, TArgs extends any[]>(service: INotifyService<TDetails>, method: (...args: TArgs) => Promise<TInfos[]>, applyFactory?: () => (entities: Ref<TInfos[]>) => void) {
public static customGetMany<TDetails extends TInfos, TInfos, TArgs extends any[]>(service: INotifyService<TDetails>, method: (...args: [...TArgs, AbortController]) => Promise<TInfos[]>, applyFactory?: () => (entities: Ref<TInfos[]>) => void) {
return () => {
const apply = applyFactory ? applyFactory() : () => { };

let cancellationToken = new AbortController();
let subscribersIds: number[] = [];

onUnmounted(() => {
Expand All @@ -129,6 +131,11 @@ export class ComposableFactory {
const entities = ref<TInfos[]>([]) as Ref<TInfos[]>;

const getMany = async (...args: [...TArgs, ((el: TDetails) => boolean)?]) => {

// abort previous request if any
cancellationToken.abort();
cancellationToken = new AbortController();

fetching.value = true;

let customFilter: ((el: TDetails) => boolean) | undefined = undefined
Expand All @@ -140,7 +147,7 @@ export class ComposableFactory {
let actualArgs = args as unknown as TArgs;

try {
entities.value = await method(...actualArgs);
entities.value = await method(...actualArgs, cancellationToken);
if (apply) apply(entities)
}
finally {
Expand All @@ -153,7 +160,7 @@ export class ComposableFactory {
subscribersIds.push(service.subscribe("reset", async () => {
fetching.value = true;
try {
entities.value = await method(...actualArgs);
entities.value = await method(...actualArgs, cancellationToken);
if (apply) apply(entities)
}
finally {
Expand Down
29 changes: 24 additions & 5 deletions src/Bones.UI/core/serviceFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,24 @@ export class ServiceFactory<TDetailsDTO, TDetails> {
}

addGetMany<TInfosDTO, TInfos, TFilter>(url: string | (() => string), entity: new (dto: TInfosDTO) => TInfos)
: { getMany: (filter?: TFilter) => Promise<TInfos[]> } {
: { getMany: (filter?: TFilter, controller?: AbortController) => Promise<TInfos[]> } {

const getMany = async (filter?: TFilter) => {
const getMany = async (filter?: TFilter, controller?: AbortController) => {
const realUrl = typeof url === "string" ? url : url();

let response;

// If the service is configured to use GET as POST to prevent issues with large query strings,
// we send the filter as a POST request with a "_method" parameter to indicate it's a GET request.
if (ServiceFactory.getAsPost && filter) {
response = await ServiceFactory.http.post(buildURL(realUrl, { "_method": "GET" }), filter);
response = await ServiceFactory.http.post(buildURL(realUrl, { "_method": "GET" }), filter, {
signal: controller?.signal
});
}
else {
response = await ServiceFactory.http.get(buildURL(realUrl, filter));
response = await ServiceFactory.http.get(buildURL(realUrl, filter), {
signal: controller?.signal
});
}

const dtos: TInfosDTO[] = response.data;
Expand All @@ -76,7 +80,7 @@ export class ServiceFactory<TDetailsDTO, TDetails> {
return { get };
}

static addCustom<T extends string, TArgs extends any[], TResultDTO, TResult>(name: T, call: (axios: AxiosInstance, ...args: TArgs) => Promise<AxiosResponse>, mapper: (dto: TResultDTO) => TResult): Record<T, (...args: TArgs) => Promise<TResult>> {
static addCustom<T extends string, TArgs extends any[], TResultDTO, TResult>(name: T, call: (axios: AxiosInstance, ...args: TArgs) => Promise<AxiosResponse>, mapper: (dto: TResultDTO) => TResult) {

const fetch = async (...args: TArgs) => {
const response = await call(ServiceFactory.http, ...args);
Expand All @@ -90,6 +94,21 @@ export class ServiceFactory<TDetailsDTO, TDetails> {
return { [name]: fetch } as Record<T, (...args: TArgs) => Promise<TResult>>;
}


static addCustomCancellable<T extends string, TArgs extends any[], TResultDTO, TResult>(name: T, call: (axios: AxiosInstance, ...args: [...TArgs, AbortController]) => Promise<AxiosResponse>, mapper: (dto: TResultDTO) => TResult) {

const fetch = async (...args: [...TArgs, AbortController]) => {
const response = await call(ServiceFactory.http, ...args);
const dto: TResultDTO = response.data;

const result = mapper(dto);

return result;
}

return { [name]: fetch } as Record<T, (...args: [...TArgs, AbortController]) => Promise<TResult>>;
}

addCreate<TCreateDTO>(url: string | (() => string))
: { create: (dto: TCreateDTO) => Promise<TDetails> } {

Expand Down
2 changes: 1 addition & 1 deletion tests/Bones.UI.Tests/services/testUserService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const AccountLoginFactory = new ServiceFactory<TestUserDetailsDTO, TestUserDetai
ServiceFactory.addCustom("logout", axios => axios.get(TEST_USERS_URL), (dto: TestUserDetailsDTO) => new TestUserDetails(dto)),
ServiceFactory.addCustom("current", axios => axios.get(TEST_USERS_URL), (dto: TestUserDetailsDTO) => new TestUserDetails(dto)),
ServiceFactory.addCustom("complexCurrent", (axios, p1: string, p2: number) => axios.get(TEST_USERS_URL), (dto: TestUserDetailsDTO) => new TestUserDetails(dto)),
ServiceFactory.addCustom("complexGetMany", (axios, p1: string, p2: number) => axios.get(TEST_USERS_URL), (dto: TestUserDetailsDTO) => new Array(5).map(a => new TestUserDetails(dto))),
ServiceFactory.addCustomCancellable("complexGetMany", (axios, p1: string, p2: number, controller: AbortController) => axios.get(TEST_USERS_URL, { signal: controller.signal }), (dto: TestUserDetailsDTO) => new Array(5).map(a => new TestUserDetails(dto))),
));

export const useTestUsersSync = ComposableFactory.sync<TestUserDetails, TestUserInfos>(testUserServiceFactory);
Expand Down