wip on app2

This commit is contained in:
2022-06-23 12:26:48 +02:00
parent 6b3b010631
commit d8f80f3d02
44 changed files with 545 additions and 358 deletions

View File

@@ -1,3 +1,4 @@
export function fetchResults<T>(uri: string, params: {item_per_page?: number}): Promise<T[]>;
export function makeFetch<T, B>(method: "GET"|"POST"|"PATCH"|"DELETE", url: string, body: B, options: {[key: string]: string}): Promise<T>;

View File

@@ -1,110 +0,0 @@
/**
* Generic api method that can be adapted to any fetch request
*/
const makeFetch = (method, url, body, options) => {
let opts = {
method: method,
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: (body !== null) ? JSON.stringify(body) : null
};
if (typeof options !== 'undefined') {
opts = Object.assign(opts, options);
}
return fetch(url, opts)
.then(response => {
if (response.ok) {
return response.json();
}
if (response.status === 422) {
return response.json().then(response => {
throw ValidationException(response)
});
}
if (response.status === 403) {
throw AccessException(response);
}
throw {
name: 'Exception',
sta: response.status,
txt: response.statusText,
err: new Error(),
violations: response.body
};
});
}
/**
* Fetch results with certain parameters
*/
const _fetchAction = (page, uri, params) => {
const item_per_page = 50;
if (params === undefined) {
params = {};
}
let url = uri + '?' + new URLSearchParams({ item_per_page, page, ...params });
return fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
}).then(response => {
if (response.ok) { return response.json(); }
throw new Error({ m: response.statusText });
});
};
const fetchResults = async (uri, params) => {
let promises = [],
page = 1;
let firstData = await _fetchAction(page, uri, params);
promises.push(Promise.resolve(firstData.results));
if (firstData.pagination.more) {
do {
page = ++page;
promises.push(_fetchAction(page, uri, params).then(r => Promise.resolve(r.results)));
} while (page * firstData.pagination.items_per_page < firstData.count)
}
return Promise.all(promises).then(values => values.flat());
};
const fetchScopes = () => {
return fetchResults('/api/1.0/main/scope.json');
};
/**
* Error objects to be thrown
*/
const ValidationException = (response) => {
const error = {};
error.name = 'ValidationException';
error.violations = response.violations.map((violation) => `${violation.title}: ${violation.propertyPath}`);
error.titles = response.violations.map((violation) => violation.title);
error.propertyPaths = response.violations.map((violation) => violation.propertyPath);
return error;
}
const AccessException = (response) => {
const error = {};
error.name = 'AccessException';
error.violations = ['You are not allowed to perform this action'];
return error;
}
export {
makeFetch,
fetchResults,
fetchScopes
}

View File

@@ -0,0 +1,164 @@
import {Scope} from 'ChillMainAssets/types';
export type body = {[key: string]: boolean|string|number|null};
export type fetchOption = {[key: string]: boolean|string|number|null};
export interface Params {
[key: string]: number|string
}
export interface PaginationResponse<T> {
pagination: {
more: boolean;
items_per_page: number;
};
results: T[];
count: number;
}
export interface FetchParams {
[K: string]: string|number|null;
};
export interface ValidationExceptionInterface {
name: 'ValidationException';
error: object;
violations: string[];
titles: string[];
propertyPaths: string[];
}
export interface ValidationErrorResponse {
violations: {
title: string;
propertyPath: string;
}[];
}
export interface AccessExceptionInterface {
name: 'AccessException';
violations: string[];
}
/**
* Generic api method that can be adapted to any fetch request
*/
export const makeFetch = <T, Q>(method: 'POST'|'GET'|'PUT'|'PATCH'|'DELETE', url: string, body?: body | T | null, options?: FetchParams): Promise<Q> => {
let opts = {
method: method,
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: (body !== null || typeof body !== 'undefined') ? JSON.stringify(body) : null
};
if (typeof options !== 'undefined') {
opts = Object.assign(opts, options);
}
return fetch(url, opts)
.then(response => {
if (response.ok) {
return response.json();
}
if (response.status === 422) {
return response.json().then(response => {
throw ValidationException(response)
});
}
if (response.status === 403) {
throw AccessException(response);
}
throw {
name: 'Exception',
sta: response.status,
txt: response.statusText,
err: new Error(),
violations: response.body
};
});
}
/**
* Fetch results with certain parameters
*/
function _fetchAction<T>(page: number, uri: string, params?: FetchParams): Promise<PaginationResponse<T>> {
const item_per_page: number = 50;
let searchParams = new URLSearchParams();
searchParams.append('item_per_page', item_per_page.toString());
searchParams.append('page', page.toString());
if (params !== undefined) {
Object.keys(params).forEach(key => {
let v = params[key];
if (typeof v === 'string') {
searchParams.append(key, v);
} else if (typeof v === 'number') {
searchParams.append(key, v.toString());
} else if (v === null) {
searchParams.append(key, '');
}
});
}
let url = uri + '?' + searchParams.toString();
return fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
}).then((response) => {
if (response.ok) { return response.json(); }
throw new Error(response.statusText);
});
};
export const fetchResults = async<T> (uri: string, params?: FetchParams): Promise<T[]> => {
let promises: Promise<T[]>[] = [],
page = 1;
let firstData: PaginationResponse<T> = await _fetchAction(page, uri, params) as PaginationResponse<T>;
promises.push(Promise.resolve(firstData.results));
if (firstData.pagination.more) {
do {
page = ++page;
promises.push(
_fetchAction<T>(page, uri, params).then(r => Promise.resolve(r.results))
);
} while (page * firstData.pagination.items_per_page < firstData.count)
}
return Promise.all(promises).then((values) => values.flat());
};
export const fetchScopes = (): Promise<Scope[]> => {
return fetchResults('/api/1.0/main/scope.json');
};
/**
* Error objects to be thrown
*/
const ValidationException = (response: ValidationErrorResponse): ValidationExceptionInterface => {
const error = {} as ValidationExceptionInterface;
error.name = 'ValidationException';
error.violations = response.violations.map((violation) => `${violation.title}: ${violation.propertyPath}`);
error.titles = response.violations.map((violation) => violation.title);
error.propertyPaths = response.violations.map((violation) => violation.propertyPath);
return error;
}
const AccessException = (response: Response): AccessExceptionInterface => {
const error = {} as AccessExceptionInterface;
error.name = 'AccessException';
error.violations = ['You are not allowed to perform this action'];
return error;
}