Upload feature (a): BFF endpoints + category config + tests
- Domain/Documents: server-owned category config per wizard + authoritative
type/size validation (DocumentRules).
- Data/DocumentStore: in-memory metadata store (no file bytes/PII) + audit log;
user delete (owner-scoped, 409 once linked), admin delete (role seam via
X-Admin header), link-on-submit, poll-by-localId status.
- Program.cs: GET /uploads/categories, POST /uploads (multipart, excluded from
OpenAPI — hand-written on FE), GET /uploads/status, DELETE /uploads/{id},
DELETE /admin/uploads/{id}. Submit links digital docs + records post-delivery.
- Contracts extended (DocumentRefDto on registratie/herregistratie submit);
regenerated NSwag client + swagger.json (drift check stays green).
- Tests: 16 new (endpoints + DocumentRules); dotnet test 44/44.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -446,6 +446,177 @@ export class ApiClient {
|
||||
}
|
||||
return Promise.resolve<ReferentieResponse>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return OK
|
||||
*/
|
||||
categories(wizardId: string): Promise<UploadCategoriesDto> {
|
||||
let url_ = this.baseUrl + "/api/v1/uploads/categories?";
|
||||
if (wizardId === undefined || wizardId === null)
|
||||
throw new globalThis.Error("The parameter 'wizardId' must be defined and cannot be null.");
|
||||
else
|
||||
url_ += "wizardId=" + encodeURIComponent("" + wizardId) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processCategories(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processCategories(response: Response): Promise<UploadCategoriesDto> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UploadCategoriesDto;
|
||||
return result200;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<UploadCategoriesDto>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param localIds (optional)
|
||||
* @return OK
|
||||
*/
|
||||
status(localIds?: string | undefined): Promise<UploadStatusDto> {
|
||||
let url_ = this.baseUrl + "/api/v1/uploads/status?";
|
||||
if (localIds === null)
|
||||
throw new globalThis.Error("The parameter 'localIds' cannot be null.");
|
||||
else if (localIds !== undefined)
|
||||
url_ += "localIds=" + encodeURIComponent("" + localIds) + "&";
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Accept": "application/json"
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processStatus(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processStatus(response: Response): Promise<UploadStatusDto> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 200) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result200: any = null;
|
||||
result200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as UploadStatusDto;
|
||||
return result200;
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<UploadStatusDto>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return No Content
|
||||
*/
|
||||
uploads(documentId: string): Promise<void> {
|
||||
let url_ = this.baseUrl + "/api/v1/uploads/{documentId}";
|
||||
if (documentId === undefined || documentId === null)
|
||||
throw new globalThis.Error("The parameter 'documentId' must be defined.");
|
||||
url_ = url_.replace("{documentId}", encodeURIComponent("" + documentId));
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processUploads(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processUploads(response: Response): Promise<void> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return;
|
||||
});
|
||||
} else if (status === 404) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("Not Found", status, _responseText, _headers);
|
||||
});
|
||||
} else if (status === 409) {
|
||||
return response.text().then((_responseText) => {
|
||||
let result409: any = null;
|
||||
result409 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver) as ProblemDetails;
|
||||
return throwException("Conflict", status, _responseText, _headers, result409);
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<void>(null as any);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return No Content
|
||||
*/
|
||||
uploads2(documentId: string): Promise<void> {
|
||||
let url_ = this.baseUrl + "/api/v1/admin/uploads/{documentId}";
|
||||
if (documentId === undefined || documentId === null)
|
||||
throw new globalThis.Error("The parameter 'documentId' must be defined.");
|
||||
url_ = url_.replace("{documentId}", encodeURIComponent("" + documentId));
|
||||
url_ = url_.replace(/[?&]$/, "");
|
||||
|
||||
let options_: RequestInit = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
}
|
||||
};
|
||||
|
||||
return this.http.fetch(url_, options_).then((_response: Response) => {
|
||||
return this.processUploads2(_response);
|
||||
});
|
||||
}
|
||||
|
||||
protected processUploads2(response: Response): Promise<void> {
|
||||
const status = response.status;
|
||||
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
|
||||
if (status === 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return;
|
||||
});
|
||||
} else if (status === 403) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("Forbidden", status, _responseText, _headers);
|
||||
});
|
||||
} else if (status === 404) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("Not Found", status, _responseText, _headers);
|
||||
});
|
||||
} else if (status !== 200 && status !== 204) {
|
||||
return response.text().then((_responseText) => {
|
||||
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
|
||||
});
|
||||
}
|
||||
return Promise.resolve<void>(null as any);
|
||||
}
|
||||
}
|
||||
|
||||
export interface AantekeningDto {
|
||||
@@ -477,6 +648,23 @@ export interface DashboardViewDto {
|
||||
decisions?: HerregistratieDecisionsDto;
|
||||
}
|
||||
|
||||
export interface DocumentCategoryDto {
|
||||
categoryId?: string | undefined;
|
||||
label?: string | undefined;
|
||||
description?: string | undefined;
|
||||
required?: boolean;
|
||||
acceptedTypes?: string[] | undefined;
|
||||
maxSizeMb?: number;
|
||||
multiple?: boolean;
|
||||
allowPostDelivery?: boolean;
|
||||
}
|
||||
|
||||
export interface DocumentRefDto {
|
||||
categoryId?: string | undefined;
|
||||
channel?: string | undefined;
|
||||
documentId?: string | undefined;
|
||||
}
|
||||
|
||||
export interface DuoDiplomaDto {
|
||||
id?: string | undefined;
|
||||
naam?: string | undefined;
|
||||
@@ -498,6 +686,7 @@ export interface HerregistratieDecisionsDto {
|
||||
|
||||
export interface HerregistratieRequest {
|
||||
uren?: number;
|
||||
documents?: DocumentRefDto[] | undefined;
|
||||
}
|
||||
|
||||
export interface IntakePolicyDto {
|
||||
@@ -541,6 +730,7 @@ export interface ReferentieResponse {
|
||||
|
||||
export interface RegistratieRequest {
|
||||
diplomaHerkomst?: string | undefined;
|
||||
documents?: DocumentRefDto[] | undefined;
|
||||
}
|
||||
|
||||
export interface RegistrationDto {
|
||||
@@ -560,6 +750,20 @@ export interface RegistrationStatusDto {
|
||||
doorgehaaldOp?: string | undefined;
|
||||
}
|
||||
|
||||
export interface UploadCategoriesDto {
|
||||
categories?: DocumentCategoryDto[] | undefined;
|
||||
}
|
||||
|
||||
export interface UploadStatusDto {
|
||||
results?: UploadStatusItemDto[] | undefined;
|
||||
}
|
||||
|
||||
export interface UploadStatusItemDto {
|
||||
localId?: string | undefined;
|
||||
status?: string | undefined;
|
||||
documentId?: string | undefined;
|
||||
}
|
||||
|
||||
export class SwaggerException extends Error {
|
||||
override message: string;
|
||||
status: number;
|
||||
|
||||
Reference in New Issue
Block a user