@dflat-core/web 1.0.50 → 1.0.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -69,14 +69,15 @@ yarn tsc -p tsconfig.spec.json # contrôle des types des tests
69
69
 
70
70
  ```ts
71
71
  describeBaseComponent('BaseCountryComponent', (app, route) => new TestCountryComponent(app, route), {
72
- entityKey: 'country', path: '/sys/country', title: 'Pays', permissionKey: 'system.country', sections: []
72
+ entityKey: 'country', path: '/sys/country', title: 'Pays', permissionKey: 'system.country', requestsSections: true
73
73
  });
74
74
  ```
75
75
 
76
76
  Elle vérifie le contrat de `BaseComponent` sans rendre de gabarit : création dans un contexte
77
77
  d'injection, clé d'entité, lien, titre et permission, façade demandée au registre avec la clé de
78
78
  l'entité, `ngOnInit` (fil d'Ariane, chargement branché sur `dataService.search`, abonnement aux
79
- notifications, onglets), `Search` / `refresh`, mise à jour sur notification, `ngOnDestroy`.
79
+ notifications, demande des onglets au registre `EntitySections`), `Search` / `refresh`, mise à
80
+ jour sur notification, `ngOnDestroy`.
80
81
  Elle n'utilise que les fonctions globales de l'outil de test (`describe`, `it`, `beforeEach`,
81
82
  `expect`) et `TestBed` : elle ne dépend pas de l'outil. `BaseComponentTestContext` fournit les
82
83
  services du composant (vraies classes du socle, doublures pour `DataService` et `Router`).
@@ -86,8 +87,21 @@ services du composant (vraies classes du socle, doublures pour `DataService` et
86
87
  - `dflat -g ts` génère, pour chaque composant `Base<X>Component` d'un module,
87
88
  `<x>.component.generated.spec.ts` (la même suite, avec les valeurs attendues du générateur) et,
88
89
  par module, `<module>-components.generated.spec.ts` (`describeGeneratedModule` : chaque
89
- composant a sa route, clés d'entité uniques, éléments de menu vers une route existante et
90
- porteurs d'une permission du module).
90
+ composant a sa route, clés d'entité uniques, onglets des entités liées attendus par entité et
91
+ pointant vers des entités et composants du module, éléments de menu vers une route existante
92
+ et porteurs d'une permission du module).
93
+
94
+ Les onglets des entités liées (sections `@relations` des formulaires) sont déclarés pour tout le
95
+ module par le service généré `<Module>GeneratedSectionService`
96
+ (`<module>-sections.generated.ts`), seul fichier généré qui importe les composants des onglets.
97
+ Le module Angular `<module>-ux` l'enregistre dans son constructeur,
98
+ `EntitySections.register(injector.get(<Module>GeneratedSectionService))`, et `Base<X>Component`
99
+ demande ses onglets par la clé de son entité
100
+ (`EntitySections.registerSections(this.getEntityKey(), this.facade)`), sans importer d'autre
101
+ composant : pas de dépendance circulaire entre composants. Le service n'est interrogé qu'une
102
+ fois par entité : la définition des onglets est gardée dans le cache d'initialisation des écrans
103
+ (`InitCacheService`, `DataService.getCachedValue`), comme les descripteurs de filtre et le filtre
104
+ vide, et vidée avec eux ; l'enregistrement sur la façade est fait à chaque initialisation.
91
105
 
92
106
  Dans le package, Vitest est lancé directement (`vitest.config.mts`, `tools/vitest.setup.ts`), sans
93
107
  `angular.json` : les décorateurs sont compilés en mode JIT et les gabarits remplacés par un
@@ -7,6 +7,7 @@ import * as i3 from "@dflat-core/web/core-services";
7
7
  import { InitCacheService } from "@dflat-core/web/core-services";
8
8
  import * as i2 from "@angular/common/http";
9
9
  import { HttpParams } from "@angular/common/http";
10
+ import { defer, of } from "rxjs";
10
11
  import { map } from "rxjs/operators";
11
12
  var DataService = class DataService {
12
13
  constructor(env, httpClient, headerService) {
@@ -55,6 +56,9 @@ var DataService = class DataService {
55
56
  getCached(entityKey, action) {
56
57
  return this.initCache.get(`${entityKey}/${action}`, () => this.httpClient.get(this.baseUrl + `/${entityKey}/${action}`, {}));
57
58
  }
59
+ getCachedValue(entityKey, name, factory) {
60
+ return this.initCache.get(`${entityKey}/${name}`, () => defer(() => of(factory())));
61
+ }
58
62
  clearCached(entityKey) {
59
63
  this.initCache.clear(Utilities.IsNil(entityKey) ? null : `${entityKey}/`);
60
64
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dflat-core-web-core-data.mjs","names":["i1.DataService"],"sources":["../../core-data/src/lib/services/data-service.ts","../../core-data/src/lib/core-data.module.ts","../../core-data/src/lib/services/entity-data-service.ts"],"sourcesContent":["import { EnvironmentBase, Utilities } from '@dflat-core/web/core';\r\nimport { ActionDescriptor, ColumnDescriptor, EntityViewModelWithFields, EntityWithSpecifics, ExtendedProperty, FilterDescriptor, PagedList, SaveResult, SaveResults, TrackRecordProperty } from '@dflat-core/web/core-model';\r\nimport { HeadersService, InitCacheService } from '@dflat-core/web/core-services';\r\nimport { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';\r\nimport { inject, Injectable, Type } from '@angular/core';\r\nimport { Observable } from 'rxjs';\r\nimport { map } from 'rxjs/operators';\r\n\r\n\r\n@Injectable()\r\nexport class DataService {\r\n\r\n baseUrl = this.env.apiUrl;\r\n httpClient: HttpClient;\r\n data: any[];\r\n private initCache = inject(InitCacheService);\r\n constructor(\r\n private env: EnvironmentBase,\r\n httpClient: HttpClient,\r\n private headerService: HeadersService) {\r\n this.httpClient = httpClient\r\n }\r\n\r\n getFormattedParams(params: any): any {\r\n // Point d'entrée public : conserve le comportement historique\r\n // (stringify uniquement quand params racine est un tableau).\r\n if (Array.isArray(params)) {\r\n if (params.length > 0 && typeof (params[0]) === 'number') {\r\n return JSON.stringify(params);\r\n }\r\n const result = params.map(entity => this.formatValueRecursive(entity));\r\n return JSON.stringify(result);\r\n }\r\n\r\n return this.formatValueRecursive(params);\r\n }\r\n\r\n // Transforme récursivement un objet/tableau/valeur, sans jamais stringifier.\r\n // Utilisée pour descendre dans les propriétés imbriquées (ex: un tableau\r\n // d'objets contenu dans une propriété d'un objet racine), qui étaient\r\n // auparavant copiées telles quelles et donc jamais nettoyées de leurs Date.\r\n private formatValueRecursive(val: any): any {\r\n if (val === undefined || val === null) {\r\n return val;\r\n }\r\n\r\n if (val instanceof Date) {\r\n return `${Utilities.Format2Digits(val.getFullYear())}-${Utilities.Format2Digits(val.getMonth() + 1)}-${Utilities.Format2Digits(val.getDate())}T00:00:00.000Z`;\r\n }\r\n\r\n if (Array.isArray(val)) {\r\n // Tableau de nombres : on le laisse tel quel (comme au niveau racine)\r\n if (val.length > 0 && typeof (val[0]) === 'number') {\r\n return val;\r\n }\r\n return val.map(item => this.formatValueRecursive(item));\r\n }\r\n\r\n if (typeof val === 'object') {\r\n const result: any = {};\r\n for (let key in val) {\r\n if (val.hasOwnProperty(key)) {\r\n const propVal = val[key];\r\n if (propVal !== undefined && propVal !== null) {\r\n result[key] = this.formatValueRecursive(propVal);\r\n }\r\n }\r\n }\r\n return result;\r\n }\r\n\r\n // primitive (string, number, boolean...) : inchangé\r\n return val;\r\n }\r\n\r\n\r\n getUrlQueryParams(filter: any): HttpParams {\r\n let requestParams = new HttpParams();\r\n let formattedFilter = this.getFormattedParams(filter);\r\n for (let key in formattedFilter) {\r\n if (formattedFilter.hasOwnProperty(key)) {\r\n let vals = formattedFilter[key];\r\n if (Array.isArray(vals) && vals.length > 0)\r\n {\r\n for (let key2 in vals) {\r\n requestParams = requestParams.append(key, vals[key2]);\r\n }\r\n }\r\n else if (typeof vals === 'object') {\r\n requestParams = requestParams.set(key, JSON.stringify(vals));\r\n }\r\n else\r\n {\r\n requestParams = requestParams.set(key, vals);\r\n }\r\n }\r\n }\r\n return requestParams;\r\n }\r\n\r\n /*getUrlQueryArrayParams(filter: any): HttpParams {\r\n let requestParams = new HttpParams();\r\n let formattedFilter = this.getFormattedParams(filter);\r\n for (let key in formattedFilter) {\r\n if (formattedFilter.hasOwnProperty(key)) {\r\n let vals = formattedFilter[key];\r\n for (let key2 in vals) {\r\n requestParams = requestParams.append(key, vals[key2]);\r\n }\r\n }\r\n }\r\n return requestParams;\r\n }*/\r\n\r\n /**\r\n * Appel GET sans paramètre gardé dans le cache d'initialisation (InitCacheService) : données\r\n * chargées avant la recherche, identiques d'une ouverture de l'écran à l'autre. Le cache est\r\n * vidé au redémarrage de l'API (horodatage du jeton d'accès) et au changement d'utilisateur.\r\n */\r\n getCached<T>(entityKey: string, action: string): Observable<T> {\r\n return this.initCache.get<T>(`${entityKey}/${action}`,\r\n () => this.httpClient.get<T>(this.baseUrl + `/${entityKey}/${action}`, {}));\r\n }\r\n\r\n /** Vide le cache d'initialisation d'une entité, ou tout le cache. */\r\n clearCached(entityKey?: string): void {\r\n this.initCache.clear(Utilities.IsNil(entityKey) ? null : `${entityKey}/`);\r\n }\r\n\r\n getViewFilterDescriptors(entityKey: string): Observable<FilterDescriptor[]> {\r\n return this.getCached<FilterDescriptor[]>(entityKey, 'GetFilterDescriptors');\r\n }\r\n\r\n getViewColumns(entityKey: string): Observable<ColumnDescriptor[]> {\r\n return this.getCached<ColumnDescriptor[]>(entityKey, 'GetGridColumns');\r\n }\r\n\r\n search<TData>(entityKey: string, filter: any): Observable<PagedList<TData>> {\r\n let _url = this.baseUrl + `/${entityKey}/Search`;\r\n let requestParams = this.getUrlQueryParams(filter);\r\n return this.httpClient.get<PagedList<TData>>(_url, { params: requestParams });\r\n }\r\n\r\n getDetails<TData>(entityKey: string, id: number): Observable<TData> {\r\n let _url = this.baseUrl + `/${entityKey}/GetDetails?`;\r\n if (id === undefined || id === null)\r\n throw new Error(\"The parameter 'id' must be defined and cannot be null.\");\r\n else\r\n _url += \"id=\" + encodeURI(\"\" + id);\r\n\r\n _url = _url.replace(/[?&]$/, \"\");\r\n\r\n return this.httpClient.get<TData>(_url);\r\n }\r\n\r\n getEmptyFilter<T>(entityKey: string): Observable<T> {\r\n return this.getCached<T>(entityKey, 'GetEmptyFilter');\r\n }\r\n\r\n /**\r\n * fonction \"async\" liant le front(Angular) et le back(C#)\r\n * - Principalement appelée par \"src.app.core.layout.BaseEditorComponent.loadEntityViewModel()\" lorsque l'on cherche à afficher un Objet de type \"EntityViewModel\"\r\n * - C'est elle qui lance l'appel à la fonction \"back\" BaseFilterController.GetEntityViewModel<TData>(id).\r\n * - Puis elle crée un observable avec la réponse qu'elle obtient du \"back\"\r\n *\r\n * @async\r\n * @function\r\n * @name getEntityViewModel<TData>\r\n * @param {string} entityKey\r\n * @param {number} id\r\n * @returns {(Observable)EntityViewModelWithFields<TData>}\r\n * @memberof DataService\r\n * @summary Demande les données d'une entité au back(C#) à travers \"getEntityViewModel\".\r\n * @see {@link src.app.core.layout.BaseEditorComponent.loadEntityViewModel()} for more information\r\n * @throws new Error(\"The parameter 'id' must be defined and cannot be null.\") if (id === undefined ou id === null)\r\n */\r\n getEntityViewModel<TData>(entityKey: string, id: number): Observable<EntityViewModelWithFields<TData>> {\r\n let _url = this. baseUrl + `/${entityKey}/GetEntityViewModel?`;\r\n if (id === undefined || id === null)\r\n throw new Error(\"The parameter 'id' must be defined and cannot be null.\");\r\n else if (id === 0)\r\n // Formulaire vide (id 0) : donnée d'initialisation, gardée en cache.\r\n return this.getCached<EntityViewModelWithFields<TData>>(entityKey, 'GetEntityViewModel?id=0');\r\n else\r\n _url += \"id=\" + encodeURI(\"\" + id);\r\n\r\n _url = _url.replace(/[?&]$/, \"\");\r\n\r\n return this.httpClient.get(_url).pipe(\r\n map((resp: Response) => <EntityViewModelWithFields<TData>>resp));\r\n }\r\n\r\n getExtendedProperties(entityKey: string, entityIds: number[]): Observable<ExtendedProperty[]> {\r\n let _url = this.baseUrl + `/${entityKey}/GetExtendedProperties`;\r\n\r\n let requestParams = this.getUrlQueryParams({ entityIds: entityIds });\r\n return this.httpClient.get<ExtendedProperty[]>(_url, { params: requestParams });\r\n }\r\n\r\n getExtendedActions (entityKey: string) : Observable<ActionDescriptor[]> {\r\n return this.getCached<ActionDescriptor[]>(entityKey, 'GetExtendedActions');\r\n }\r\n \r\n getTrackRecordProperties(entityKey: string, entityIds: number[]): Observable<TrackRecordProperty[]> {\r\n let _url = this.baseUrl + `/${entityKey}/GetTrackRecordProperties`;\r\n\r\n let requestParams = this.getUrlQueryParams({ entityIds: entityIds });\r\n return this.httpClient.get<TrackRecordProperty[]>(_url, { params: requestParams });\r\n }\r\n\r\n saveEntity<TData>(entityKey: string, entity: TData): Observable<SaveResult> {\r\n let _url = this.baseUrl + `/${entityKey}/SaveEntity`;\r\n return this.httpClient.post<SaveResult>(_url, this.getFormattedParams(entity));\r\n }\r\n\r\n deleteEntity<TData>(entityKey: string, entity: TData): Observable<SaveResult> {\r\n let _url = this.baseUrl + `/${entityKey}/DeleteEntity`;\r\n return this.httpClient.post<SaveResult>(_url, this.getFormattedParams(entity));\r\n }\r\n\r\n deleteEntities<TData>(entityKey: string, entities: TData[]): Observable<SaveResults<TData>> {\r\n let _url = this.baseUrl + `/${entityKey}/DeleteEntities`;\r\n return this.httpClient.post<SaveResults<TData>>(_url, this.getFormattedParams(entities));\r\n }\r\n\r\n saveEntities<TData>(entityKey: string, entities: TData[]): Observable<SaveResults<TData>> {\r\n let _url = this.baseUrl + `/${entityKey}/saveEntities`;\r\n let toSave = [];\r\n entities.forEach(entity => {\r\n toSave.push(this.getFormattedParams(entity));\r\n });\r\n return this.httpClient.post<SaveResults<TData>>(_url, JSON.stringify(toSave));\r\n }\r\n\r\n\r\n\r\n SaveEntityWithSpecifics<TData>(entityKey: string, DataToSave: EntityWithSpecifics<TData>): Observable<{ success: boolean, rowVersion?: Date }> {\r\n let _url = this.baseUrl + `/${entityKey}/SaveEntityWithSpecifics`;\r\n return this.httpClient.post<{ success: boolean, rowVersion?: Date }>(_url, this.getFormattedParams(DataToSave));\r\n }\r\n\r\n exportAll<TData>(entityKey: string, filter: any) {\r\n let _url = this.baseUrl + `/${entityKey}/ExportAll`;\r\n let requestParams = this.getUrlQueryParams(filter);\r\n\r\n return this.httpClient.get(_url, { observe: 'response', params: requestParams, withCredentials: true, responseType: 'blob' })\r\n .pipe(map((r: HttpResponse<Blob>) => this.getFileInfo(r)))\r\n }\r\n\r\n exportResults<TData>(entityKey: string, exportModel: any) {\r\n let _url = this.baseUrl + `/${entityKey}/ExportResults`;\r\n const _body = JSON.stringify(exportModel);\r\n\r\n return this.httpClient.post(_url, _body, { observe: 'response', responseType: 'blob' })\r\n .pipe(map((r: HttpResponse<Blob>) => this.getFileInfo(r)));\r\n }\r\n\r\n public getFileInfo(resp: HttpResponse<Blob>): { fileBlob: Blob, fileName: string } {\r\n var type = resp.headers.get(\"Content-Type\");\r\n let b = new Blob([resp.body], { type: type });\r\n return {\r\n fileBlob: b,\r\n fileName: (resp.headers.get(\"Content-Disposition\") || \"\").split(\";\")[1].trim().split(\"=\")[1].replace(/['\"]/g, '')\r\n };\r\n }\r\n\r\n uploadFileToExtract(file: File, inputId: number, correlationId: string) {\r\n return this.uploadFiles(\"/DataImport/PostImportFile\", [file], { inputId: inputId, correlationId: correlationId });\r\n }\r\n\r\n uploadFilesWithParams(files: File[], customParams?: {}, forceMultipleFiles: boolean = false): Observable<any> {\r\n\r\n const _url = this.baseUrl + \"/DataImport/PostFiles\";\r\n\r\n let formData = new FormData();\r\n if (files.length == 1 && !forceMultipleFiles) {\r\n formData.append('file', files[0]);\r\n }\r\n else {\r\n for (var i = 0; i < files.length; i++) {\r\n formData.append('files', files[i]);\r\n }\r\n }\r\n\r\n formData.append('data', JSON.stringify((customParams as any).dataModel));\r\n let params = new HttpParams();\r\n\r\n return this.httpClient.post(_url, formData, {\r\n params,\r\n reportProgress: true,\r\n observe: 'body',\r\n headers: this.headerService.authFileUploadHeaders()\r\n });\r\n }\r\n\r\n uploadFiles(url: string, files: File[], customParams?: {}, forceMultipleFiles: boolean = false): Observable<any> {\r\n\r\n const _url = this.baseUrl + url;\r\n\r\n let formData = new FormData();\r\n if (files.length == 1 && !forceMultipleFiles) {\r\n formData.append('file', files[0]);\r\n }\r\n else {\r\n for (var i = 0; i < files.length; i++) {\r\n formData.append('files', files[i]);\r\n }\r\n }\r\n\r\n if (customParams) {\r\n for (let p in customParams) {\r\n if (customParams.hasOwnProperty(p)) {\r\n formData.append(p, customParams[p]);\r\n }\r\n }\r\n }\r\n\r\n let params = new HttpParams();\r\n\r\n\r\n return this.httpClient.post(_url, formData, {\r\n params,\r\n reportProgress: true,\r\n observe: 'body',\r\n headers: this.headerService.authFileUploadHeaders()\r\n });\r\n }\r\n\r\n uploadFilesWithEvents(url: string, files: File[], customParams?: {}, forceMultipleFiles: boolean = false): Observable<any> {\r\n\r\n const _url = this.baseUrl + url;\r\n\r\n let formData = new FormData();\r\n if (files.length == 1 && !forceMultipleFiles) {\r\n formData.append('file', files[0]);\r\n }\r\n else {\r\n for (var i = 0; i < files.length; i++) {\r\n formData.append('files', files[i]);\r\n }\r\n }\r\n\r\n if (customParams) {\r\n for (let p in customParams) {\r\n if (customParams.hasOwnProperty(p)) {\r\n formData.append(p, customParams[p]);\r\n }\r\n }\r\n }\r\n\r\n let params = new HttpParams();\r\n return this.httpClient.post<any>(_url, formData, {\r\n params,\r\n reportProgress: true,\r\n observe: 'events',\r\n headers: this.headerService.authFileUploadHeaders()\r\n });\r\n\r\n }\r\n}\r\n","import { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\n\r\nimport { DataService } from './services/data-service';\r\n\r\n\r\n@NgModule({\r\n imports: [CommonModule],\r\n providers: [DataService]\r\n})\r\nexport class CoreDataModule {}\r\n","import { ActionDescriptor, ColumnDescriptor, EntityViewModel, EntityViewModelWithFields, EntityWithSpecifics, ExportModel, ExtendedProperty, FilterDescriptor, FilterViewModel, PagedList, SaveResult, SaveResults, TrackRecordProperty } from '@dflat-core/web/core-model';\r\n\r\nimport { DataService } from './data-service';\r\nimport { HttpResponse } from '@angular/common/http';\r\nimport { Directive } from '@angular/core';\r\nimport { Observable } from 'rxjs';\r\nimport { map } from 'rxjs/operators';\r\n\r\n@Directive()\r\nexport abstract class EntityDataService<TData extends EntityViewModel, TFilter extends FilterViewModel> \r\n{\r\n abstract getEntityKey(): string;\r\n\r\n constructor(protected dataService : DataService) {}\r\n deleteEntities (sources:TData[]) : Observable<SaveResults<TData>> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/DeleteEntities`;\r\n const requestParams = this.dataService.getFormattedParams(sources);\r\n return this.dataService.httpClient.post<SaveResults<TData>>(_url, requestParams);\r\n }\r\n\r\n deleteEntity (source:TData) : Observable<SaveResult> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/DeleteEntity`;\r\n const requestParams = this.dataService.getFormattedParams(source);\r\n return this.dataService.httpClient.post<SaveResult>(_url, requestParams);\r\n }\r\n\r\n exportAll (filter:TFilter) {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/ExportAll`;\r\n const requestParams = this.dataService.getFormattedParams(filter);\r\n return this.dataService.httpClient.get(_url, { observe: 'response', params: requestParams, withCredentials: true, responseType: 'blob' }).pipe(map((r: HttpResponse<Blob>) => this.dataService.getFileInfo(r)));\r\n }\r\n\r\n exportResults (model:ExportModel<TFilter>) {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/ExportResults`;\r\n const requestParams = this.dataService.getFormattedParams(model);\r\n return this.dataService.httpClient.get(_url, { observe: 'response', params: requestParams, withCredentials: true, responseType: 'blob' }).pipe(map((r: HttpResponse<Blob>) => this.dataService.getFileInfo(r)));\r\n }\r\n\r\n getAlternateEntityViewModel (id:number) : Observable<EntityViewModelWithFields<TData>> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetAlternateEntityViewModel`;\r\n const requestParams = this.dataService.getUrlQueryParams({ id:id });\r\n return this.dataService.httpClient.get<EntityViewModelWithFields<TData>>(_url, { params: requestParams });\r\n }\r\n\r\n getCacheManager () : Observable<TData[]> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetCacheManager`;\r\n return this.dataService.httpClient.get<TData[]>(_url, {});\r\n }\r\n\r\n getColumns () : Observable<ColumnDescriptor[]> {\r\n return this.dataService.getCached<ColumnDescriptor[]>(this.getEntityKey(), 'GetColumns');\r\n }\r\n\r\n getDetails (id:number) : Observable<TData> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetDetails`;\r\n const requestParams = this.dataService.getUrlQueryParams({ id:id });\r\n return this.dataService.httpClient.get<TData>(_url, { params: requestParams });\r\n }\r\n\r\n getEmptyFilter () : Observable<TFilter> {\r\n return this.dataService.getCached<TFilter>(this.getEntityKey(), 'GetEmptyFilter');\r\n }\r\n\r\n getEntityViewModel (id:number) : Observable<EntityViewModelWithFields<TData>> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetEntityViewModel`;\r\n const requestParams = this.dataService.getUrlQueryParams({ id:id });\r\n return this.dataService.httpClient.get<EntityViewModelWithFields<TData>>(_url, { params: requestParams });\r\n }\r\n\r\n getExtendedActions () : Observable<ActionDescriptor[]> {\r\n return this.dataService.getCached<ActionDescriptor[]>(this.getEntityKey(), 'GetExtendedActions');\r\n }\r\n\r\n getExtendedProperties (entityIds:number[]) : Observable<ExtendedProperty[]> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetExtendedProperties`;\r\n const requestParams = this.dataService.getUrlQueryParams({ entityIds:entityIds });\r\n return this.dataService.httpClient.get<ExtendedProperty[]>(_url, { params: requestParams });\r\n }\r\n\r\n getFilterDescriptors () : Observable<FilterDescriptor[]> {\r\n return this.dataService.getCached<FilterDescriptor[]>(this.getEntityKey(), 'GetFilterDescriptors');\r\n }\r\n\r\n getSortColumn (col:string) : Observable<string> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetSortColumn`;\r\n const requestParams = this.dataService.getUrlQueryParams({ col:col });\r\n return this.dataService.httpClient.get<string>(_url, { params: requestParams });\r\n }\r\n\r\n getTrackRecordProperties (entityIds:number[]) : Observable<TrackRecordProperty[]> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetTrackRecordProperties`;\r\n const requestParams = this.dataService.getUrlQueryParams({ entityIds:entityIds });\r\n return this.dataService.httpClient.get<TrackRecordProperty[]>(_url, { params: requestParams });\r\n }\r\n \r\n saveEntities (sources:TData[]) : Observable<SaveResults<TData>> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/SaveEntities`;\r\n const requestParams = this.dataService.getFormattedParams(sources);\r\n return this.dataService.httpClient.post<SaveResults<TData>>(_url, requestParams);\r\n }\r\n\r\n saveEntity (source:TData) : Observable<SaveResult> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/SaveEntity`;\r\n const requestParams = this.dataService.getFormattedParams(source);\r\n return this.dataService.httpClient.post<SaveResult>(_url, requestParams);\r\n }\r\n\r\n saveEntityWithSpecifics (source:EntityWithSpecifics<TData>) : Observable<any> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/SaveEntityWithSpecifics`;\r\n const requestParams = this.dataService.getFormattedParams(source);\r\n return this.dataService.httpClient.post<any>(_url, requestParams);\r\n }\r\n\r\n search (filter:TFilter) : Observable<PagedList<TData>> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/Search`;\r\n const requestParams = this.dataService.getFormattedParams(filter);\r\n return this.dataService.httpClient.get<PagedList<TData>>(_url, { params: requestParams });\r\n }\r\n\r\n toggleFavorite (filter:TFilter) : Observable<any> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/ToggleFavorite`;\r\n const requestParams = this.dataService.getFormattedParams(filter);\r\n return this.dataService.httpClient.post<any>(_url, requestParams);\r\n }\r\n\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"dflat-core-web-core-data.mjs","names":["i1.DataService"],"sources":["../../core-data/src/lib/services/data-service.ts","../../core-data/src/lib/core-data.module.ts","../../core-data/src/lib/services/entity-data-service.ts"],"sourcesContent":["import { EnvironmentBase, Utilities } from '@dflat-core/web/core';\r\nimport { ActionDescriptor, ColumnDescriptor, EntityViewModelWithFields, EntityWithSpecifics, ExtendedProperty, FilterDescriptor, PagedList, SaveResult, SaveResults, TrackRecordProperty } from '@dflat-core/web/core-model';\r\nimport { HeadersService, InitCacheService } from '@dflat-core/web/core-services';\r\nimport { HttpClient, HttpParams, HttpResponse } from '@angular/common/http';\r\nimport { inject, Injectable, Type } from '@angular/core';\r\nimport { defer, Observable, of } from 'rxjs';\r\nimport { map } from 'rxjs/operators';\r\n\r\n\r\n@Injectable()\r\nexport class DataService {\r\n\r\n baseUrl = this.env.apiUrl;\r\n httpClient: HttpClient;\r\n data: any[];\r\n private initCache = inject(InitCacheService);\r\n constructor(\r\n private env: EnvironmentBase,\r\n httpClient: HttpClient,\r\n private headerService: HeadersService) {\r\n this.httpClient = httpClient\r\n }\r\n\r\n getFormattedParams(params: any): any {\r\n // Point d'entrée public : conserve le comportement historique\r\n // (stringify uniquement quand params racine est un tableau).\r\n if (Array.isArray(params)) {\r\n if (params.length > 0 && typeof (params[0]) === 'number') {\r\n return JSON.stringify(params);\r\n }\r\n const result = params.map(entity => this.formatValueRecursive(entity));\r\n return JSON.stringify(result);\r\n }\r\n\r\n return this.formatValueRecursive(params);\r\n }\r\n\r\n // Transforme récursivement un objet/tableau/valeur, sans jamais stringifier.\r\n // Utilisée pour descendre dans les propriétés imbriquées (ex: un tableau\r\n // d'objets contenu dans une propriété d'un objet racine), qui étaient\r\n // auparavant copiées telles quelles et donc jamais nettoyées de leurs Date.\r\n private formatValueRecursive(val: any): any {\r\n if (val === undefined || val === null) {\r\n return val;\r\n }\r\n\r\n if (val instanceof Date) {\r\n return `${Utilities.Format2Digits(val.getFullYear())}-${Utilities.Format2Digits(val.getMonth() + 1)}-${Utilities.Format2Digits(val.getDate())}T00:00:00.000Z`;\r\n }\r\n\r\n if (Array.isArray(val)) {\r\n // Tableau de nombres : on le laisse tel quel (comme au niveau racine)\r\n if (val.length > 0 && typeof (val[0]) === 'number') {\r\n return val;\r\n }\r\n return val.map(item => this.formatValueRecursive(item));\r\n }\r\n\r\n if (typeof val === 'object') {\r\n const result: any = {};\r\n for (let key in val) {\r\n if (val.hasOwnProperty(key)) {\r\n const propVal = val[key];\r\n if (propVal !== undefined && propVal !== null) {\r\n result[key] = this.formatValueRecursive(propVal);\r\n }\r\n }\r\n }\r\n return result;\r\n }\r\n\r\n // primitive (string, number, boolean...) : inchangé\r\n return val;\r\n }\r\n\r\n\r\n getUrlQueryParams(filter: any): HttpParams {\r\n let requestParams = new HttpParams();\r\n let formattedFilter = this.getFormattedParams(filter);\r\n for (let key in formattedFilter) {\r\n if (formattedFilter.hasOwnProperty(key)) {\r\n let vals = formattedFilter[key];\r\n if (Array.isArray(vals) && vals.length > 0)\r\n {\r\n for (let key2 in vals) {\r\n requestParams = requestParams.append(key, vals[key2]);\r\n }\r\n }\r\n else if (typeof vals === 'object') {\r\n requestParams = requestParams.set(key, JSON.stringify(vals));\r\n }\r\n else\r\n {\r\n requestParams = requestParams.set(key, vals);\r\n }\r\n }\r\n }\r\n return requestParams;\r\n }\r\n\r\n /*getUrlQueryArrayParams(filter: any): HttpParams {\r\n let requestParams = new HttpParams();\r\n let formattedFilter = this.getFormattedParams(filter);\r\n for (let key in formattedFilter) {\r\n if (formattedFilter.hasOwnProperty(key)) {\r\n let vals = formattedFilter[key];\r\n for (let key2 in vals) {\r\n requestParams = requestParams.append(key, vals[key2]);\r\n }\r\n }\r\n }\r\n return requestParams;\r\n }*/\r\n\r\n /**\r\n * Appel GET sans paramètre gardé dans le cache d'initialisation (InitCacheService) : données\r\n * chargées avant la recherche, identiques d'une ouverture de l'écran à l'autre. Le cache est\r\n * vidé au redémarrage de l'API (horodatage du jeton d'accès) et au changement d'utilisateur.\r\n */\r\n getCached<T>(entityKey: string, action: string): Observable<T> {\r\n return this.initCache.get<T>(`${entityKey}/${action}`,\r\n () => this.httpClient.get<T>(this.baseUrl + `/${entityKey}/${action}`, {}));\r\n }\r\n\r\n /**\r\n * Valeur d'initialisation d'une entité calculée par le front (ex. définition de ses onglets,\r\n * voir EntitySections), gardée dans le même cache que les appels de getCached et sous la même\r\n * portée (clé de l'entité) : `factory` n'est appelée qu'une fois, jusqu'à ce que le cache soit\r\n * vidé (clearCached, redémarrage de l'API, changement d'utilisateur).\r\n */\r\n getCachedValue<T>(entityKey: string, name: string, factory: () => T): Observable<T> {\r\n return this.initCache.get<T>(`${entityKey}/${name}`, () => defer(() => of(factory())));\r\n }\r\n\r\n /** Vide le cache d'initialisation d'une entité, ou tout le cache. */\r\n clearCached(entityKey?: string): void {\r\n this.initCache.clear(Utilities.IsNil(entityKey) ? null : `${entityKey}/`);\r\n }\r\n\r\n getViewFilterDescriptors(entityKey: string): Observable<FilterDescriptor[]> {\r\n return this.getCached<FilterDescriptor[]>(entityKey, 'GetFilterDescriptors');\r\n }\r\n\r\n getViewColumns(entityKey: string): Observable<ColumnDescriptor[]> {\r\n return this.getCached<ColumnDescriptor[]>(entityKey, 'GetGridColumns');\r\n }\r\n\r\n search<TData>(entityKey: string, filter: any): Observable<PagedList<TData>> {\r\n let _url = this.baseUrl + `/${entityKey}/Search`;\r\n let requestParams = this.getUrlQueryParams(filter);\r\n return this.httpClient.get<PagedList<TData>>(_url, { params: requestParams });\r\n }\r\n\r\n getDetails<TData>(entityKey: string, id: number): Observable<TData> {\r\n let _url = this.baseUrl + `/${entityKey}/GetDetails?`;\r\n if (id === undefined || id === null)\r\n throw new Error(\"The parameter 'id' must be defined and cannot be null.\");\r\n else\r\n _url += \"id=\" + encodeURI(\"\" + id);\r\n\r\n _url = _url.replace(/[?&]$/, \"\");\r\n\r\n return this.httpClient.get<TData>(_url);\r\n }\r\n\r\n getEmptyFilter<T>(entityKey: string): Observable<T> {\r\n return this.getCached<T>(entityKey, 'GetEmptyFilter');\r\n }\r\n\r\n /**\r\n * fonction \"async\" liant le front(Angular) et le back(C#)\r\n * - Principalement appelée par \"src.app.core.layout.BaseEditorComponent.loadEntityViewModel()\" lorsque l'on cherche à afficher un Objet de type \"EntityViewModel\"\r\n * - C'est elle qui lance l'appel à la fonction \"back\" BaseFilterController.GetEntityViewModel<TData>(id).\r\n * - Puis elle crée un observable avec la réponse qu'elle obtient du \"back\"\r\n *\r\n * @async\r\n * @function\r\n * @name getEntityViewModel<TData>\r\n * @param {string} entityKey\r\n * @param {number} id\r\n * @returns {(Observable)EntityViewModelWithFields<TData>}\r\n * @memberof DataService\r\n * @summary Demande les données d'une entité au back(C#) à travers \"getEntityViewModel\".\r\n * @see {@link src.app.core.layout.BaseEditorComponent.loadEntityViewModel()} for more information\r\n * @throws new Error(\"The parameter 'id' must be defined and cannot be null.\") if (id === undefined ou id === null)\r\n */\r\n getEntityViewModel<TData>(entityKey: string, id: number): Observable<EntityViewModelWithFields<TData>> {\r\n let _url = this. baseUrl + `/${entityKey}/GetEntityViewModel?`;\r\n if (id === undefined || id === null)\r\n throw new Error(\"The parameter 'id' must be defined and cannot be null.\");\r\n else if (id === 0)\r\n // Formulaire vide (id 0) : donnée d'initialisation, gardée en cache.\r\n return this.getCached<EntityViewModelWithFields<TData>>(entityKey, 'GetEntityViewModel?id=0');\r\n else\r\n _url += \"id=\" + encodeURI(\"\" + id);\r\n\r\n _url = _url.replace(/[?&]$/, \"\");\r\n\r\n return this.httpClient.get(_url).pipe(\r\n map((resp: Response) => <EntityViewModelWithFields<TData>>resp));\r\n }\r\n\r\n getExtendedProperties(entityKey: string, entityIds: number[]): Observable<ExtendedProperty[]> {\r\n let _url = this.baseUrl + `/${entityKey}/GetExtendedProperties`;\r\n\r\n let requestParams = this.getUrlQueryParams({ entityIds: entityIds });\r\n return this.httpClient.get<ExtendedProperty[]>(_url, { params: requestParams });\r\n }\r\n\r\n getExtendedActions (entityKey: string) : Observable<ActionDescriptor[]> {\r\n return this.getCached<ActionDescriptor[]>(entityKey, 'GetExtendedActions');\r\n }\r\n \r\n getTrackRecordProperties(entityKey: string, entityIds: number[]): Observable<TrackRecordProperty[]> {\r\n let _url = this.baseUrl + `/${entityKey}/GetTrackRecordProperties`;\r\n\r\n let requestParams = this.getUrlQueryParams({ entityIds: entityIds });\r\n return this.httpClient.get<TrackRecordProperty[]>(_url, { params: requestParams });\r\n }\r\n\r\n saveEntity<TData>(entityKey: string, entity: TData): Observable<SaveResult> {\r\n let _url = this.baseUrl + `/${entityKey}/SaveEntity`;\r\n return this.httpClient.post<SaveResult>(_url, this.getFormattedParams(entity));\r\n }\r\n\r\n deleteEntity<TData>(entityKey: string, entity: TData): Observable<SaveResult> {\r\n let _url = this.baseUrl + `/${entityKey}/DeleteEntity`;\r\n return this.httpClient.post<SaveResult>(_url, this.getFormattedParams(entity));\r\n }\r\n\r\n deleteEntities<TData>(entityKey: string, entities: TData[]): Observable<SaveResults<TData>> {\r\n let _url = this.baseUrl + `/${entityKey}/DeleteEntities`;\r\n return this.httpClient.post<SaveResults<TData>>(_url, this.getFormattedParams(entities));\r\n }\r\n\r\n saveEntities<TData>(entityKey: string, entities: TData[]): Observable<SaveResults<TData>> {\r\n let _url = this.baseUrl + `/${entityKey}/saveEntities`;\r\n let toSave = [];\r\n entities.forEach(entity => {\r\n toSave.push(this.getFormattedParams(entity));\r\n });\r\n return this.httpClient.post<SaveResults<TData>>(_url, JSON.stringify(toSave));\r\n }\r\n\r\n\r\n\r\n SaveEntityWithSpecifics<TData>(entityKey: string, DataToSave: EntityWithSpecifics<TData>): Observable<{ success: boolean, rowVersion?: Date }> {\r\n let _url = this.baseUrl + `/${entityKey}/SaveEntityWithSpecifics`;\r\n return this.httpClient.post<{ success: boolean, rowVersion?: Date }>(_url, this.getFormattedParams(DataToSave));\r\n }\r\n\r\n exportAll<TData>(entityKey: string, filter: any) {\r\n let _url = this.baseUrl + `/${entityKey}/ExportAll`;\r\n let requestParams = this.getUrlQueryParams(filter);\r\n\r\n return this.httpClient.get(_url, { observe: 'response', params: requestParams, withCredentials: true, responseType: 'blob' })\r\n .pipe(map((r: HttpResponse<Blob>) => this.getFileInfo(r)))\r\n }\r\n\r\n exportResults<TData>(entityKey: string, exportModel: any) {\r\n let _url = this.baseUrl + `/${entityKey}/ExportResults`;\r\n const _body = JSON.stringify(exportModel);\r\n\r\n return this.httpClient.post(_url, _body, { observe: 'response', responseType: 'blob' })\r\n .pipe(map((r: HttpResponse<Blob>) => this.getFileInfo(r)));\r\n }\r\n\r\n public getFileInfo(resp: HttpResponse<Blob>): { fileBlob: Blob, fileName: string } {\r\n var type = resp.headers.get(\"Content-Type\");\r\n let b = new Blob([resp.body], { type: type });\r\n return {\r\n fileBlob: b,\r\n fileName: (resp.headers.get(\"Content-Disposition\") || \"\").split(\";\")[1].trim().split(\"=\")[1].replace(/['\"]/g, '')\r\n };\r\n }\r\n\r\n uploadFileToExtract(file: File, inputId: number, correlationId: string) {\r\n return this.uploadFiles(\"/DataImport/PostImportFile\", [file], { inputId: inputId, correlationId: correlationId });\r\n }\r\n\r\n uploadFilesWithParams(files: File[], customParams?: {}, forceMultipleFiles: boolean = false): Observable<any> {\r\n\r\n const _url = this.baseUrl + \"/DataImport/PostFiles\";\r\n\r\n let formData = new FormData();\r\n if (files.length == 1 && !forceMultipleFiles) {\r\n formData.append('file', files[0]);\r\n }\r\n else {\r\n for (var i = 0; i < files.length; i++) {\r\n formData.append('files', files[i]);\r\n }\r\n }\r\n\r\n formData.append('data', JSON.stringify((customParams as any).dataModel));\r\n let params = new HttpParams();\r\n\r\n return this.httpClient.post(_url, formData, {\r\n params,\r\n reportProgress: true,\r\n observe: 'body',\r\n headers: this.headerService.authFileUploadHeaders()\r\n });\r\n }\r\n\r\n uploadFiles(url: string, files: File[], customParams?: {}, forceMultipleFiles: boolean = false): Observable<any> {\r\n\r\n const _url = this.baseUrl + url;\r\n\r\n let formData = new FormData();\r\n if (files.length == 1 && !forceMultipleFiles) {\r\n formData.append('file', files[0]);\r\n }\r\n else {\r\n for (var i = 0; i < files.length; i++) {\r\n formData.append('files', files[i]);\r\n }\r\n }\r\n\r\n if (customParams) {\r\n for (let p in customParams) {\r\n if (customParams.hasOwnProperty(p)) {\r\n formData.append(p, customParams[p]);\r\n }\r\n }\r\n }\r\n\r\n let params = new HttpParams();\r\n\r\n\r\n return this.httpClient.post(_url, formData, {\r\n params,\r\n reportProgress: true,\r\n observe: 'body',\r\n headers: this.headerService.authFileUploadHeaders()\r\n });\r\n }\r\n\r\n uploadFilesWithEvents(url: string, files: File[], customParams?: {}, forceMultipleFiles: boolean = false): Observable<any> {\r\n\r\n const _url = this.baseUrl + url;\r\n\r\n let formData = new FormData();\r\n if (files.length == 1 && !forceMultipleFiles) {\r\n formData.append('file', files[0]);\r\n }\r\n else {\r\n for (var i = 0; i < files.length; i++) {\r\n formData.append('files', files[i]);\r\n }\r\n }\r\n\r\n if (customParams) {\r\n for (let p in customParams) {\r\n if (customParams.hasOwnProperty(p)) {\r\n formData.append(p, customParams[p]);\r\n }\r\n }\r\n }\r\n\r\n let params = new HttpParams();\r\n return this.httpClient.post<any>(_url, formData, {\r\n params,\r\n reportProgress: true,\r\n observe: 'events',\r\n headers: this.headerService.authFileUploadHeaders()\r\n });\r\n\r\n }\r\n}\r\n","import { CommonModule } from '@angular/common';\r\nimport { NgModule } from '@angular/core';\r\n\r\nimport { DataService } from './services/data-service';\r\n\r\n\r\n@NgModule({\r\n imports: [CommonModule],\r\n providers: [DataService]\r\n})\r\nexport class CoreDataModule {}\r\n","import { ActionDescriptor, ColumnDescriptor, EntityViewModel, EntityViewModelWithFields, EntityWithSpecifics, ExportModel, ExtendedProperty, FilterDescriptor, FilterViewModel, PagedList, SaveResult, SaveResults, TrackRecordProperty } from '@dflat-core/web/core-model';\r\n\r\nimport { DataService } from './data-service';\r\nimport { HttpResponse } from '@angular/common/http';\r\nimport { Directive } from '@angular/core';\r\nimport { Observable } from 'rxjs';\r\nimport { map } from 'rxjs/operators';\r\n\r\n@Directive()\r\nexport abstract class EntityDataService<TData extends EntityViewModel, TFilter extends FilterViewModel> \r\n{\r\n abstract getEntityKey(): string;\r\n\r\n constructor(protected dataService : DataService) {}\r\n deleteEntities (sources:TData[]) : Observable<SaveResults<TData>> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/DeleteEntities`;\r\n const requestParams = this.dataService.getFormattedParams(sources);\r\n return this.dataService.httpClient.post<SaveResults<TData>>(_url, requestParams);\r\n }\r\n\r\n deleteEntity (source:TData) : Observable<SaveResult> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/DeleteEntity`;\r\n const requestParams = this.dataService.getFormattedParams(source);\r\n return this.dataService.httpClient.post<SaveResult>(_url, requestParams);\r\n }\r\n\r\n exportAll (filter:TFilter) {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/ExportAll`;\r\n const requestParams = this.dataService.getFormattedParams(filter);\r\n return this.dataService.httpClient.get(_url, { observe: 'response', params: requestParams, withCredentials: true, responseType: 'blob' }).pipe(map((r: HttpResponse<Blob>) => this.dataService.getFileInfo(r)));\r\n }\r\n\r\n exportResults (model:ExportModel<TFilter>) {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/ExportResults`;\r\n const requestParams = this.dataService.getFormattedParams(model);\r\n return this.dataService.httpClient.get(_url, { observe: 'response', params: requestParams, withCredentials: true, responseType: 'blob' }).pipe(map((r: HttpResponse<Blob>) => this.dataService.getFileInfo(r)));\r\n }\r\n\r\n getAlternateEntityViewModel (id:number) : Observable<EntityViewModelWithFields<TData>> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetAlternateEntityViewModel`;\r\n const requestParams = this.dataService.getUrlQueryParams({ id:id });\r\n return this.dataService.httpClient.get<EntityViewModelWithFields<TData>>(_url, { params: requestParams });\r\n }\r\n\r\n getCacheManager () : Observable<TData[]> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetCacheManager`;\r\n return this.dataService.httpClient.get<TData[]>(_url, {});\r\n }\r\n\r\n getColumns () : Observable<ColumnDescriptor[]> {\r\n return this.dataService.getCached<ColumnDescriptor[]>(this.getEntityKey(), 'GetColumns');\r\n }\r\n\r\n getDetails (id:number) : Observable<TData> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetDetails`;\r\n const requestParams = this.dataService.getUrlQueryParams({ id:id });\r\n return this.dataService.httpClient.get<TData>(_url, { params: requestParams });\r\n }\r\n\r\n getEmptyFilter () : Observable<TFilter> {\r\n return this.dataService.getCached<TFilter>(this.getEntityKey(), 'GetEmptyFilter');\r\n }\r\n\r\n getEntityViewModel (id:number) : Observable<EntityViewModelWithFields<TData>> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetEntityViewModel`;\r\n const requestParams = this.dataService.getUrlQueryParams({ id:id });\r\n return this.dataService.httpClient.get<EntityViewModelWithFields<TData>>(_url, { params: requestParams });\r\n }\r\n\r\n getExtendedActions () : Observable<ActionDescriptor[]> {\r\n return this.dataService.getCached<ActionDescriptor[]>(this.getEntityKey(), 'GetExtendedActions');\r\n }\r\n\r\n getExtendedProperties (entityIds:number[]) : Observable<ExtendedProperty[]> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetExtendedProperties`;\r\n const requestParams = this.dataService.getUrlQueryParams({ entityIds:entityIds });\r\n return this.dataService.httpClient.get<ExtendedProperty[]>(_url, { params: requestParams });\r\n }\r\n\r\n getFilterDescriptors () : Observable<FilterDescriptor[]> {\r\n return this.dataService.getCached<FilterDescriptor[]>(this.getEntityKey(), 'GetFilterDescriptors');\r\n }\r\n\r\n getSortColumn (col:string) : Observable<string> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetSortColumn`;\r\n const requestParams = this.dataService.getUrlQueryParams({ col:col });\r\n return this.dataService.httpClient.get<string>(_url, { params: requestParams });\r\n }\r\n\r\n getTrackRecordProperties (entityIds:number[]) : Observable<TrackRecordProperty[]> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/GetTrackRecordProperties`;\r\n const requestParams = this.dataService.getUrlQueryParams({ entityIds:entityIds });\r\n return this.dataService.httpClient.get<TrackRecordProperty[]>(_url, { params: requestParams });\r\n }\r\n \r\n saveEntities (sources:TData[]) : Observable<SaveResults<TData>> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/SaveEntities`;\r\n const requestParams = this.dataService.getFormattedParams(sources);\r\n return this.dataService.httpClient.post<SaveResults<TData>>(_url, requestParams);\r\n }\r\n\r\n saveEntity (source:TData) : Observable<SaveResult> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/SaveEntity`;\r\n const requestParams = this.dataService.getFormattedParams(source);\r\n return this.dataService.httpClient.post<SaveResult>(_url, requestParams);\r\n }\r\n\r\n saveEntityWithSpecifics (source:EntityWithSpecifics<TData>) : Observable<any> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/SaveEntityWithSpecifics`;\r\n const requestParams = this.dataService.getFormattedParams(source);\r\n return this.dataService.httpClient.post<any>(_url, requestParams);\r\n }\r\n\r\n search (filter:TFilter) : Observable<PagedList<TData>> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/Search`;\r\n const requestParams = this.dataService.getFormattedParams(filter);\r\n return this.dataService.httpClient.get<PagedList<TData>>(_url, { params: requestParams });\r\n }\r\n\r\n toggleFavorite (filter:TFilter) : Observable<any> {\r\n const _url = this.dataService.baseUrl + `/${this.getEntityKey()}/ToggleFavorite`;\r\n const requestParams = this.dataService.getFormattedParams(filter);\r\n return this.dataService.httpClient.post<any>(_url, requestParams);\r\n }\r\n\r\n}\r\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { of } from "rxjs";
2
- import { ApplicationService, EntityFacadeRegistryService } from "@dflat-core/web/core-ux";
2
+ import { ApplicationService, EntityFacadeRegistryService, EntitySections } from "@dflat-core/web/core-ux";
3
3
  import { BreadcrumbService, NotificationMqService } from "@dflat-core/web/core-services";
4
4
  import { TestBed } from "@angular/core/testing";
5
5
  import { Title } from "@angular/platform-browser";
@@ -50,6 +50,9 @@ var DataServiceDouble = class {
50
50
  getExtendedActions() {
51
51
  return of([]);
52
52
  }
53
+ getCachedValue(entityKey, name, factory) {
54
+ return of(factory());
55
+ }
53
56
  asDataService() {
54
57
  return this;
55
58
  }
@@ -245,6 +248,31 @@ function describeBaseComponent(name, factory, expectations) {
245
248
  component.facade.sections.sections$.subscribe((list) => sections = list.map((s) => s.Name)).unsubscribe();
246
249
  expect(sections).toEqual(expectations.sections);
247
250
  });
251
+ if (expectations.requestsSections !== void 0) {
252
+ const title = expectations.requestsSections ? "ngOnInit demande les onglets de l'entité au registre du socle (EntitySections)" : "ngOnInit ne demande pas d'onglet au registre du socle (EntitySections)";
253
+ it(title, async () => {
254
+ const original = EntitySections.registerSections;
255
+ const requests = [];
256
+ EntitySections.registerSections = ((entityKey, facade) => {
257
+ requests.push({
258
+ entityKey,
259
+ facade
260
+ });
261
+ });
262
+ try {
263
+ component.ngOnInit();
264
+ await Promise.resolve();
265
+ await Promise.resolve();
266
+ } finally {
267
+ EntitySections.registerSections = original;
268
+ }
269
+ expect(requests.length).toBe(expectations.requestsSections ? 1 : 0);
270
+ if (expectations.requestsSections) {
271
+ expect(requests[0].entityKey).toBe(expectations.entityKey);
272
+ expect(requests[0].facade).toBe(component.facade);
273
+ }
274
+ });
275
+ }
248
276
  it("Search copie le filtre, remet la page à zéro et navigue vers l'URL du filtre", () => {
249
277
  component.ngOnInit();
250
278
  const loads = countCalls(component.facade.filter, "triggerSearch");
@@ -372,6 +400,29 @@ function describeGeneratedModule(name, module) {
372
400
  const mismatched = module.selectors.filter((selector) => !module.routes.some((route) => route.path === selector.key && route.component === selector.component)).map((selector) => selector.key);
373
401
  expect(mismatched).toEqual([]);
374
402
  });
403
+ if (module.sectionProvider) {
404
+ const provider = module.sectionProvider;
405
+ if (module.sections) {
406
+ const expected = module.sections;
407
+ it("déclare les onglets attendus de chaque entité, dans l'ordre", () => {
408
+ const declared = {};
409
+ provider.getEntityKeys().forEach((key) => declared[key] = provider.getSections(key).map((section) => section.title));
410
+ expect(declared).toEqual(expected);
411
+ });
412
+ }
413
+ it("ne déclare d'onglets que pour des entités du module, vers des composants générés du module", () => {
414
+ const keys = getConstants(module.entityKeys);
415
+ expect(provider.getEntityKeys().filter((key) => !keys.includes(key))).toEqual([]);
416
+ const unknown = provider.getEntityKeys().flatMap((key) => provider.getSections(key).map((section) => ({
417
+ key,
418
+ section
419
+ }))).filter(({ section }) => !module.components.some((component) => component === section.component)).map(({ key, section }) => `${key} : ${section.title}`);
420
+ expect(unknown).toEqual([]);
421
+ });
422
+ it("ne déclare aucun onglet pour une entité sans onglet", () => {
423
+ expect(provider.getSections("")).toEqual([]);
424
+ });
425
+ }
375
426
  it("fait pointer chaque élément de menu généré vers une route existante", () => {
376
427
  const links = paths.map((path) => module.getLink(path));
377
428
  const orphans = module.menuItems.filter((item) => !links.includes(item.link ?? "")).map((item) => item.link);
@@ -1 +1 @@
1
- {"version":3,"file":"dflat-core-web-core-ux-testing.mjs","names":[],"sources":["../../core-ux/testing/src/lib/doubles/data-service-double.ts","../../core-ux/testing/src/lib/doubles/entity-facade-registry-spy.ts","../../core-ux/testing/src/lib/doubles/router-double.ts","../../core-ux/testing/src/lib/base-component-test-context.ts","../../core-ux/testing/src/lib/count-calls.ts","../../core-ux/testing/src/lib/get-test-runner.ts","../../core-ux/testing/src/lib/describe-base-component.ts","../../core-ux/testing/src/lib/describe-generated-module.ts"],"sourcesContent":["import { DataService } from '@dflat-core/web/core-data';\r\nimport {\r\n ActionDescriptor,\r\n ColumnDescriptor,\r\n EntityViewModel,\r\n EntityViewModelWithFields,\r\n FilterDescriptor,\r\n FilterViewModel,\r\n PagedList\r\n} from '@dflat-core/web/core-model';\r\nimport { Observable, of } from 'rxjs';\r\n\r\n/**\r\n * Doublure de DataService : aucune requête HTTP. Fournit les données d'initialisation d'une\r\n * façade (colonnes, filtre vide, formulaire, actions) et enregistre les recherches et lectures\r\n * de détail demandées par le composant testé.\r\n */\r\nexport class DataServiceDouble {\r\n /** Recherches demandées (search), dans l'ordre. */\r\n readonly searchCalls: { entityKey: string; filter: FilterViewModel }[] = [];\r\n\r\n /** Lectures de détail demandées (getDetails), dans l'ordre. */\r\n readonly detailsCalls: { entityKey: string; id: number }[] = [];\r\n\r\n /** Résultat renvoyé par search. */\r\n searchResult: PagedList<EntityViewModel> = { Values: [], TotalRowCount: 0 };\r\n\r\n /** Entité renvoyée par getDetails. */\r\n details: EntityViewModel = {};\r\n\r\n /** Enregistre la recherche et renvoie searchResult. */\r\n search<TData>(entityKey: string, filter: FilterViewModel): Observable<PagedList<TData>> {\r\n this.searchCalls.push({ entityKey, filter });\r\n return of(this.searchResult as PagedList<TData>);\r\n }\r\n\r\n /** Enregistre la lecture et renvoie details. */\r\n getDetails<TData>(entityKey: string, id: number): Observable<TData> {\r\n this.detailsCalls.push({ entityKey, id });\r\n return of(this.details as TData);\r\n }\r\n\r\n /** Paramètres d'URL d'un filtre : copie du filtre, sans mise en forme. */\r\n getFormattedParams(params: object): object {\r\n return { ...params };\r\n }\r\n\r\n /** Colonnes de la grille : aucune. */\r\n getViewColumns(): Observable<ColumnDescriptor[]> {\r\n return of([]);\r\n }\r\n\r\n /** Critères du filtre : aucun. */\r\n getViewFilterDescriptors(): Observable<FilterDescriptor[]> {\r\n return of([]);\r\n }\r\n\r\n /** Filtre vide : première page de 25 lignes. */\r\n getEmptyFilter<TFilter>(): Observable<TFilter> {\r\n return of({ Offset: 0, Take: 25 } as TFilter);\r\n }\r\n\r\n /** Formulaire vide, sans champ. */\r\n getEntityViewModel<TData>(): Observable<EntityViewModelWithFields<TData>> {\r\n return of({} as EntityViewModelWithFields<TData>);\r\n }\r\n\r\n /** Actions étendues : aucune. */\r\n getExtendedActions(): Observable<ActionDescriptor[]> {\r\n return of([]);\r\n }\r\n\r\n /** La doublure, vue comme le DataService qu'elle remplace. */\r\n asDataService(): DataService {\r\n return this as unknown as DataService;\r\n }\r\n}\r\n","import { EntityViewModel } from '@dflat-core/web/core-model';\r\nimport { EntityFacade, EntityFacadeRegistryService } from '@dflat-core/web/core-ux';\r\nimport { Guid } from 'guid-typescript';\r\n\r\n/** Registre de façades du socle (vraies façades), qui enregistre les façades demandées. */\r\nexport class EntityFacadeRegistrySpy extends EntityFacadeRegistryService {\r\n /** Façades demandées (getFacade), dans l'ordre : clé d'entité, identifiant et façade renvoyée. */\r\n readonly requests: { key: string; id: Guid; facade: EntityFacade<EntityViewModel> }[] = [];\r\n\r\n /** Enregistre la demande et renvoie la façade du registre. */\r\n override getFacade<TData extends EntityViewModel>(key: string, id: Guid): EntityFacade<TData> {\r\n const facade = super.getFacade<TData>(key, id);\r\n this.requests.push({ key, id, facade });\r\n return facade;\r\n }\r\n}\r\n","import { NavigationExtras, Router, UrlCreationOptions, UrlTree } from '@angular/router';\r\n\r\n/**\r\n * Doublure de Router : URL courante, construction d'URL (chemin et paramètres) et navigations\r\n * enregistrées. Une navigation réussit toujours et devient l'URL courante.\r\n */\r\nexport class RouterDouble {\r\n /** URL courante. */\r\n url: string;\r\n\r\n /** Navigations demandées (navigate), dans l'ordre. */\r\n readonly navigations: { commands: unknown[]; extras?: NavigationExtras }[] = [];\r\n\r\n constructor(url: string) {\r\n this.url = url;\r\n }\r\n\r\n /** URL formée des segments `commands` et des paramètres `extras.queryParams`. */\r\n createUrlTree(commands: unknown[], extras?: UrlCreationOptions): UrlTree {\r\n const url = RouterDouble.format(commands, extras?.queryParams);\r\n return { toString: () => url } as UrlTree;\r\n }\r\n\r\n /** Enregistre la navigation, qui devient l'URL courante. */\r\n navigate(commands: unknown[], extras?: NavigationExtras): Promise<boolean> {\r\n this.navigations.push({ commands, extras });\r\n this.url = RouterDouble.format(commands, extras?.queryParams);\r\n return Promise.resolve(true);\r\n }\r\n\r\n /** La doublure, vue comme le Router qu'elle remplace. */\r\n asRouter(): Router {\r\n return this as unknown as Router;\r\n }\r\n\r\n /** URL d'un chemin et de ses paramètres. */\r\n private static format(commands: unknown[], queryParams?: object | null): string {\r\n const path = commands.join('/');\r\n const query = Object.entries(queryParams ?? {})\r\n .map(([key, value]) => `${key}=${encodeURIComponent(String(value))}`)\r\n .join('&');\r\n return query ? `${path}?${query}` : path;\r\n }\r\n}\r\n","import { Title } from '@angular/platform-browser';\r\nimport { ActivatedRoute } from '@angular/router';\r\nimport { BreadcrumbService, NotificationMqService } from '@dflat-core/web/core-services';\r\nimport { ApplicationService, RootDeps } from '@dflat-core/web/core-ux';\r\nimport { of } from 'rxjs';\r\nimport { DataServiceDouble } from './doubles/data-service-double';\r\nimport { EntityFacadeRegistrySpy } from './doubles/entity-facade-registry-spy';\r\nimport { RouterDouble } from './doubles/router-double';\r\nimport { BreadcrumbData } from './interfaces/breadcrumb-data';\r\n\r\n/**\r\n * Services donnés à un composant dérivé de BaseComponent pendant un test : vraies classes du\r\n * socle quand elles s'instancient sans dépendance (ApplicationService, registre de façades et\r\n * façades, fil d'Ariane, file de messages), doublures pour le service de données, le routeur et\r\n * la route. Les services que BaseComponent n'utilise pas (authentification, fenêtres modales,\r\n * alertes, mise en page...) ne sont pas fournis.\r\n */\r\nexport class BaseComponentTestContext {\r\n /** Doublure du service de données. */\r\n readonly dataService = new DataServiceDouble();\r\n\r\n /** Doublure du routeur. */\r\n readonly router: RouterDouble;\r\n\r\n /** Route active, sans paramètre. */\r\n readonly route: ActivatedRoute;\r\n\r\n /** Fil d'Ariane du socle. */\r\n readonly breadcrumb: BreadcrumbService;\r\n\r\n /** File de messages (notifications d'entité) du socle. */\r\n readonly notifications = new NotificationMqService();\r\n\r\n /** Registre de façades du socle, qui enregistre les façades demandées. */\r\n readonly registry: EntityFacadeRegistrySpy;\r\n\r\n /** ApplicationService du socle, construit sur les services ci-dessus. */\r\n readonly app: ApplicationService;\r\n\r\n /**\r\n * @param url URL courante du routeur.\r\n * @param title Service du titre de la page (TestBed.inject(Title)), utilisé par le fil d'Ariane.\r\n */\r\n constructor(url: string, title: Title) {\r\n this.router = new RouterDouble(url);\r\n this.route = { snapshot: { params: {}, queryParams: {} }, params: of({}), queryParams: of({}) } as unknown as ActivatedRoute;\r\n this.breadcrumb = new BreadcrumbService(title);\r\n\r\n const deps = {\r\n nav: { route: this.route, router: this.router.asRouter(), location: undefined },\r\n ui: { layout: undefined, breadcrumb: this.breadcrumb, toastr: undefined, alert: undefined, modal: undefined },\r\n data: { data: this.dataService.asDataService(), filter: undefined, selector: undefined },\r\n security: { auth: undefined, user: undefined },\r\n selection: undefined,\r\n test: undefined\r\n } as unknown as RootDeps;\r\n\r\n this.registry = new EntityFacadeRegistrySpy(deps);\r\n this.app = new ApplicationService(deps, this.notifications, undefined, undefined, undefined, this.registry);\r\n }\r\n\r\n /** Fil d'Ariane courant (dernière valeur publiée). */\r\n get currentBreadcrumb(): BreadcrumbData {\r\n let current: BreadcrumbData;\r\n this.breadcrumb.getCurrent().subscribe((data: BreadcrumbData) => current = data).unsubscribe();\r\n return current;\r\n }\r\n}\r\n","import { CallCounter } from './interfaces/call-counter';\r\n\r\n/**\r\n * Compte les appels de la méthode `method` de `target`, qui reste exécutée : équivalent, sans\r\n * outil de test, d'un espion transparent.\r\n */\r\nexport function countCalls<T extends object, K extends keyof T>(target: T, method: K): CallCounter {\r\n const original = target[method] as unknown as (...args: unknown[]) => unknown;\r\n const counter: CallCounter = { count: 0 };\r\n target[method] = ((...args: unknown[]) => {\r\n counter.count++;\r\n return original.apply(target, args);\r\n }) as unknown as T[K];\r\n return counter;\r\n}\r\n","import { TestRunner } from './interfaces/test-runner';\r\n\r\n/**\r\n * Fonctions globales de l'outil de test (describe, it, beforeEach, expect), lues sur globalThis\r\n * pour ne dépendre d'aucun outil ni de ses types.\r\n */\r\nexport function getTestRunner(): TestRunner {\r\n const runner = globalThis as unknown as Partial<TestRunner>;\r\n if (typeof runner.describe !== 'function' || typeof runner.it !== 'function'\r\n || typeof runner.beforeEach !== 'function' || typeof runner.expect !== 'function')\r\n throw new Error('Suites de test Dflat : describe, it, beforeEach et expect doivent être des fonctions globales (Vitest : option globals).');\r\n return runner as TestRunner;\r\n}\r\n","import { TestBed } from '@angular/core/testing';\r\nimport { Title } from '@angular/platform-browser';\r\nimport { Utilities } from '@dflat-core/web/core';\r\nimport { EntityViewModel, FilterViewModel, Notification } from '@dflat-core/web/core-model';\r\nimport { BaseComponent } from '@dflat-core/web/core-ux';\r\nimport { BaseComponentTestContext } from './base-component-test-context';\r\nimport { countCalls } from './count-calls';\r\nimport { getTestRunner } from './get-test-runner';\r\nimport { BaseComponentExpectations } from './interfaces/base-component-expectations';\r\nimport { BaseComponentFactory } from './interfaces/base-component-factory';\r\n\r\n/**\r\n * Suite de test standard d'un composant dérivé de BaseComponent : vérifie, sans rendre son\r\n * gabarit, le contrat de BaseComponent (création, clé d'entité, lien, titre, permission, façade,\r\n * ngOnInit, Search / refresh, notifications, ngOnDestroy). À appeler au premier niveau d'un\r\n * fichier de test :\r\n * `describeBaseComponent('CountryComponent', (app, route) => new TestCountryComponent(app, route), { ... })`.\r\n * La suite n'utilise que les fonctions globales de l'outil de test (describe, it, beforeEach,\r\n * expect) et TestBed.\r\n *\r\n * @param name Nom du composant, repris dans le titre de la suite.\r\n * @param factory Fabrique du composant, appelée dans un contexte d'injection avant chaque test.\r\n * @param expectations Valeurs attendues du composant.\r\n */\r\nexport function describeBaseComponent<TFilter extends FilterViewModel, TData extends EntityViewModel>(\r\n name: string, factory: BaseComponentFactory<TFilter, TData>, expectations: BaseComponentExpectations): void {\r\n const { describe, it, beforeEach, expect } = getTestRunner();\r\n\r\n describe(`${name} (contrat de BaseComponent)`, () => {\r\n let context: BaseComponentTestContext;\r\n let component: BaseComponent<TFilter, TData>;\r\n\r\n /** Publie la notification de modification de l'entité `entityId` sur le canal de l'entité du composant. */\r\n const notify = (entityId: number): void =>\r\n context.notifications.publish(new Notification({ entityType: Utilities.GetChannel(expectations.entityKey), entityId }));\r\n\r\n beforeEach(() => {\r\n TestBed.configureTestingModule({ providers: expectations.providers ?? [] });\r\n context = new BaseComponentTestContext(expectations.path, TestBed.inject(Title));\r\n component = TestBed.runInInjectionContext(() => factory(context.app, context.route));\r\n });\r\n\r\n it('est créé dans un contexte d\\'injection', () => {\r\n expect(component).toBeTruthy();\r\n expect(component.destroy$.closed).toBe(false);\r\n });\r\n\r\n it('expose la clé d\\'entité, le lien, le titre et la permission attendus', () => {\r\n expect(component.getEntityKey()).toBe(expectations.entityKey);\r\n expect(component.getPath()).toBe(expectations.path);\r\n expect(component.getTitle()).toBe(expectations.title);\r\n expect(component.PermissionKey).toBe(expectations.permissionKey);\r\n expect(component.layoutKey).toBe(expectations.entityKey);\r\n });\r\n\r\n it('demande sa façade au registre avec la clé de l\\'entité', () => {\r\n expect(context.registry.requests.length).toBe(1);\r\n expect(context.registry.requests[0].key).toBe(expectations.entityKey);\r\n expect(context.registry.requests[0].facade).toBe(component.facade);\r\n expect(component.facade.actions.handler.path).toBe(expectations.path);\r\n });\r\n\r\n it('ngOnInit publie le fil d\\'Ariane du composant principal', () => {\r\n component.ngOnInit();\r\n\r\n expect(component.displayComponent).toBe(true);\r\n expect(component.actionTitle).toBe(expectations.title);\r\n expect(context.currentBreadcrumb).toEqual({\r\n description: expectations.title,\r\n display: true,\r\n header: expectations.title,\r\n levels: [{ icon: component.getIcon(), link: [expectations.path], title: expectations.title }]\r\n });\r\n });\r\n\r\n it('ngOnInit ne publie pas de fil d\\'Ariane pour un composant d\\'onglet', () => {\r\n component.IsTheMainComponent = false;\r\n component.ngOnInit();\r\n\r\n expect(context.currentBreadcrumb.display).toBe(false);\r\n });\r\n\r\n it('ngOnInit branche le chargement sur dataService.search avec la clé de l\\'entité', () => {\r\n context.dataService.searchResult = { Values: [{ Id: 7, Name: 'avant' }], TotalRowCount: 1 };\r\n component.ngOnInit();\r\n\r\n const filter: FilterViewModel = { Offset: 0, Take: 25 };\r\n let total: number | undefined;\r\n component.facade.data.loadData(filter).subscribe(result => total = result.TotalRowCount);\r\n\r\n expect(context.dataService.searchCalls).toEqual([{ entityKey: expectations.entityKey, filter }]);\r\n expect(total).toBe(1);\r\n expect(component.facade.data.getOriginal(7).Name).toBe('avant');\r\n });\r\n\r\n it('ngOnInit s\\'abonne une seule fois aux notifications de l\\'entité', () => {\r\n component.ngOnInit();\r\n const first = component.notif;\r\n component.ngOnInit();\r\n\r\n expect(first.closed).toBe(true);\r\n expect(component.notif.closed).toBe(false);\r\n });\r\n\r\n if (expectations.sections) {\r\n it('ngOnInit enregistre les onglets attendus', async () => {\r\n component.ngOnInit();\r\n // Les onglets générés sont enregistrés dans une microtâche, après ceux du composant spécifique.\r\n await Promise.resolve();\r\n await Promise.resolve();\r\n\r\n let sections: (string | undefined)[] = [];\r\n component.facade.sections.sections$.subscribe(list => sections = list.map(s => s.Name)).unsubscribe();\r\n expect(sections).toEqual(expectations.sections);\r\n });\r\n }\r\n\r\n it('Search copie le filtre, remet la page à zéro et navigue vers l\\'URL du filtre', () => {\r\n component.ngOnInit();\r\n const loads = countCalls(component.facade.filter, 'triggerSearch');\r\n\r\n component.Search({ Keywords: 'abc', Offset: 50 });\r\n\r\n expect(component.Filter.Keywords).toBe('abc');\r\n expect(component.Filter.Offset).toBe(0);\r\n expect(context.router.navigations.length).toBe(1);\r\n expect(context.router.navigations[0].commands).toEqual([expectations.path]);\r\n expect(context.router.navigations[0].extras?.queryParams).toEqual({ Keywords: 'abc', Offset: 0 });\r\n expect(loads.count).toBe(0);\r\n });\r\n\r\n it('Search charge les données quand l\\'URL porte déjà le filtre', () => {\r\n component.ngOnInit();\r\n const loads = countCalls(component.facade.filter, 'triggerSearch');\r\n\r\n component.Search({ Keywords: 'abc' });\r\n component.Search({ Keywords: 'abc' });\r\n\r\n expect(context.router.navigations.length).toBe(1);\r\n expect(loads.count).toBe(1);\r\n });\r\n\r\n it('Search garde la page demandée sans remise à zéro', () => {\r\n component.ngOnInit();\r\n\r\n component.Search({ Offset: 50 }, false);\r\n\r\n expect(component.Filter.Offset).toBe(50);\r\n });\r\n\r\n it('Search et refresh chargent les données sans naviguer pour un composant d\\'onglet', () => {\r\n component.IsTheMainComponent = false;\r\n component.ngOnInit();\r\n const loads = countCalls(component.facade.filter, 'triggerSearch');\r\n\r\n component.Search({ Keywords: 'abc', Offset: 50 });\r\n expect(loads.count).toBe(1);\r\n expect(component.Filter.Keywords).toBe('abc');\r\n\r\n component.Filter.Offset = 50;\r\n component.refresh();\r\n expect(loads.count).toBe(2);\r\n expect(component.Filter.Offset).toBe(0);\r\n expect(context.router.navigations.length).toBe(0);\r\n });\r\n\r\n it('met à jour la ligne d\\'une entité chargée à sa notification', () => {\r\n context.dataService.searchResult = { Values: [{ Id: 7, Name: 'avant' }], TotalRowCount: 1 };\r\n component.ngOnInit();\r\n component.facade.data.loadData({ Offset: 0, Take: 25 }).subscribe();\r\n context.dataService.details = { Id: 7, Name: 'après' };\r\n\r\n notify(7);\r\n\r\n expect(context.dataService.detailsCalls).toEqual([{ entityKey: expectations.entityKey, id: 7 }]);\r\n expect(component.facade.data.getOriginal(7).Name).toBe('après');\r\n });\r\n\r\n it('ignore la notification d\\'une entité non chargée, ou recharge si loadDataOnNotification', () => {\r\n component.ngOnInit();\r\n const loads = countCalls(component.facade.filter, 'triggerSearch');\r\n\r\n notify(8);\r\n expect(context.dataService.detailsCalls.length).toBe(0);\r\n expect(loads.count).toBe(0);\r\n\r\n component.loadDataOnNotification = true;\r\n notify(8);\r\n expect(context.dataService.detailsCalls.length).toBe(0);\r\n expect(loads.count).toBe(1);\r\n });\r\n\r\n it('ngOnDestroy termine destroy$, se désabonne des notifications et efface le fil d\\'Ariane', () => {\r\n component.ngOnInit();\r\n let emitted = 0;\r\n let completed = false;\r\n component.destroy$.subscribe({ next: () => emitted++, complete: () => completed = true });\r\n\r\n component.ngOnDestroy();\r\n\r\n expect(emitted).toBe(1);\r\n expect(completed).toBe(true);\r\n expect(component.notif.closed).toBe(true);\r\n expect(context.currentBreadcrumb.display).toBe(false);\r\n\r\n notify(7);\r\n expect(context.dataService.detailsCalls.length).toBe(0);\r\n });\r\n });\r\n}\r\n","import { getTestRunner } from './get-test-runner';\r\nimport { GeneratedModuleExpectations } from './interfaces/generated-module-expectations';\r\n\r\n/** Valeurs des propriétés statiques de type chaîne d'une classe de constantes (clés d'entité, permissions). */\r\nfunction getConstants(constants: object): string[] {\r\n return Object.values(constants).filter((value): value is string => typeof value === 'string');\r\n}\r\n\r\n/** Valeurs présentes plusieurs fois dans `values`. */\r\nfunction getDuplicates(values: string[]): string[] {\r\n return values.filter((value, index) => values.indexOf(value) !== index);\r\n}\r\n\r\n/**\r\n * Suite de test standard d'un module généré (dflat -g ts) : cohérence de ses composants, routes,\r\n * sélecteurs, clés d'entité, éléments de menu et permissions. Chaque test compare à une liste\r\n * vide la liste des éléments en défaut, que l'outil de test affiche en cas d'échec.\r\n *\r\n * @param name Nom du module (ex. System), repris dans le titre de la suite.\r\n * @param module Éléments générés du module.\r\n */\r\nexport function describeGeneratedModule(name: string, module: GeneratedModuleExpectations): void {\r\n const { describe, it, expect } = getTestRunner();\r\n\r\n describe(`Module ${name} (éléments générés)`, () => {\r\n const paths = module.routes.map(route => route.path ?? '');\r\n\r\n it('donne une route à chaque composant généré', () => {\r\n const missing = module.components\r\n .filter(component => !module.routes.some(route => route.component === component))\r\n .map(component => component.name);\r\n expect(missing).toEqual([]);\r\n });\r\n\r\n it('n\\'a que des routes de composants générés, de chemins distincts', () => {\r\n const unknown = module.routes\r\n .filter(route => !module.components.some(component => component === route.component))\r\n .map(route => route.path);\r\n expect(unknown).toEqual([]);\r\n expect(getDuplicates(paths)).toEqual([]);\r\n });\r\n\r\n it('a des clés d\\'entité uniques, dont celles des routes', () => {\r\n const keys = getConstants(module.entityKeys);\r\n expect(getDuplicates(keys)).toEqual([]);\r\n expect(paths.filter(path => !keys.includes(path))).toEqual([]);\r\n });\r\n\r\n it('donne à chaque sélecteur d\\'entité le composant de la route de sa clé', () => {\r\n const mismatched = module.selectors\r\n .filter(selector => !module.routes.some(route => route.path === selector.key && route.component === selector.component))\r\n .map(selector => selector.key);\r\n expect(mismatched).toEqual([]);\r\n });\r\n\r\n it('fait pointer chaque élément de menu généré vers une route existante', () => {\r\n const links = paths.map(path => module.getLink(path));\r\n const orphans = module.menuItems.filter(item => !links.includes(item.link ?? '')).map(item => item.link);\r\n expect(orphans).toEqual([]);\r\n });\r\n\r\n if (module.permissions) {\r\n const permissions = getConstants(module.permissions);\r\n\r\n it('donne à chaque élément de menu généré une permission du module', () => {\r\n const ungranted = module.menuItems.filter(item => !permissions.includes(item.permissionKey ?? '')).map(item => item.link);\r\n expect(ungranted).toEqual([]);\r\n });\r\n }\r\n });\r\n}\r\n"],"mappings":"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"}
1
+ {"version":3,"file":"dflat-core-web-core-ux-testing.mjs","names":[],"sources":["../../core-ux/testing/src/lib/doubles/data-service-double.ts","../../core-ux/testing/src/lib/doubles/entity-facade-registry-spy.ts","../../core-ux/testing/src/lib/doubles/router-double.ts","../../core-ux/testing/src/lib/base-component-test-context.ts","../../core-ux/testing/src/lib/count-calls.ts","../../core-ux/testing/src/lib/get-test-runner.ts","../../core-ux/testing/src/lib/describe-base-component.ts","../../core-ux/testing/src/lib/describe-generated-module.ts"],"sourcesContent":["import { DataService } from '@dflat-core/web/core-data';\r\nimport {\r\n ActionDescriptor,\r\n ColumnDescriptor,\r\n EntityViewModel,\r\n EntityViewModelWithFields,\r\n FilterDescriptor,\r\n FilterViewModel,\r\n PagedList\r\n} from '@dflat-core/web/core-model';\r\nimport { Observable, of } from 'rxjs';\r\n\r\n/**\r\n * Doublure de DataService : aucune requête HTTP. Fournit les données d'initialisation d'une\r\n * façade (colonnes, filtre vide, formulaire, actions) et enregistre les recherches et lectures\r\n * de détail demandées par le composant testé.\r\n */\r\nexport class DataServiceDouble {\r\n /** Recherches demandées (search), dans l'ordre. */\r\n readonly searchCalls: { entityKey: string; filter: FilterViewModel }[] = [];\r\n\r\n /** Lectures de détail demandées (getDetails), dans l'ordre. */\r\n readonly detailsCalls: { entityKey: string; id: number }[] = [];\r\n\r\n /** Résultat renvoyé par search. */\r\n searchResult: PagedList<EntityViewModel> = { Values: [], TotalRowCount: 0 };\r\n\r\n /** Entité renvoyée par getDetails. */\r\n details: EntityViewModel = {};\r\n\r\n /** Enregistre la recherche et renvoie searchResult. */\r\n search<TData>(entityKey: string, filter: FilterViewModel): Observable<PagedList<TData>> {\r\n this.searchCalls.push({ entityKey, filter });\r\n return of(this.searchResult as PagedList<TData>);\r\n }\r\n\r\n /** Enregistre la lecture et renvoie details. */\r\n getDetails<TData>(entityKey: string, id: number): Observable<TData> {\r\n this.detailsCalls.push({ entityKey, id });\r\n return of(this.details as TData);\r\n }\r\n\r\n /** Paramètres d'URL d'un filtre : copie du filtre, sans mise en forme. */\r\n getFormattedParams(params: object): object {\r\n return { ...params };\r\n }\r\n\r\n /** Colonnes de la grille : aucune. */\r\n getViewColumns(): Observable<ColumnDescriptor[]> {\r\n return of([]);\r\n }\r\n\r\n /** Critères du filtre : aucun. */\r\n getViewFilterDescriptors(): Observable<FilterDescriptor[]> {\r\n return of([]);\r\n }\r\n\r\n /** Filtre vide : première page de 25 lignes. */\r\n getEmptyFilter<TFilter>(): Observable<TFilter> {\r\n return of({ Offset: 0, Take: 25 } as TFilter);\r\n }\r\n\r\n /** Formulaire vide, sans champ. */\r\n getEntityViewModel<TData>(): Observable<EntityViewModelWithFields<TData>> {\r\n return of({} as EntityViewModelWithFields<TData>);\r\n }\r\n\r\n /** Actions étendues : aucune. */\r\n getExtendedActions(): Observable<ActionDescriptor[]> {\r\n return of([]);\r\n }\r\n\r\n /** Valeur d'initialisation calculée par le front : calculée à chaque appel, sans cache. */\r\n getCachedValue<T>(entityKey: string, name: string, factory: () => T): Observable<T> {\r\n return of(factory());\r\n }\r\n\r\n /** La doublure, vue comme le DataService qu'elle remplace. */\r\n asDataService(): DataService {\r\n return this as unknown as DataService;\r\n }\r\n}\r\n","import { EntityViewModel } from '@dflat-core/web/core-model';\r\nimport { EntityFacade, EntityFacadeRegistryService } from '@dflat-core/web/core-ux';\r\nimport { Guid } from 'guid-typescript';\r\n\r\n/** Registre de façades du socle (vraies façades), qui enregistre les façades demandées. */\r\nexport class EntityFacadeRegistrySpy extends EntityFacadeRegistryService {\r\n /** Façades demandées (getFacade), dans l'ordre : clé d'entité, identifiant et façade renvoyée. */\r\n readonly requests: { key: string; id: Guid; facade: EntityFacade<EntityViewModel> }[] = [];\r\n\r\n /** Enregistre la demande et renvoie la façade du registre. */\r\n override getFacade<TData extends EntityViewModel>(key: string, id: Guid): EntityFacade<TData> {\r\n const facade = super.getFacade<TData>(key, id);\r\n this.requests.push({ key, id, facade });\r\n return facade;\r\n }\r\n}\r\n","import { NavigationExtras, Router, UrlCreationOptions, UrlTree } from '@angular/router';\r\n\r\n/**\r\n * Doublure de Router : URL courante, construction d'URL (chemin et paramètres) et navigations\r\n * enregistrées. Une navigation réussit toujours et devient l'URL courante.\r\n */\r\nexport class RouterDouble {\r\n /** URL courante. */\r\n url: string;\r\n\r\n /** Navigations demandées (navigate), dans l'ordre. */\r\n readonly navigations: { commands: unknown[]; extras?: NavigationExtras }[] = [];\r\n\r\n constructor(url: string) {\r\n this.url = url;\r\n }\r\n\r\n /** URL formée des segments `commands` et des paramètres `extras.queryParams`. */\r\n createUrlTree(commands: unknown[], extras?: UrlCreationOptions): UrlTree {\r\n const url = RouterDouble.format(commands, extras?.queryParams);\r\n return { toString: () => url } as UrlTree;\r\n }\r\n\r\n /** Enregistre la navigation, qui devient l'URL courante. */\r\n navigate(commands: unknown[], extras?: NavigationExtras): Promise<boolean> {\r\n this.navigations.push({ commands, extras });\r\n this.url = RouterDouble.format(commands, extras?.queryParams);\r\n return Promise.resolve(true);\r\n }\r\n\r\n /** La doublure, vue comme le Router qu'elle remplace. */\r\n asRouter(): Router {\r\n return this as unknown as Router;\r\n }\r\n\r\n /** URL d'un chemin et de ses paramètres. */\r\n private static format(commands: unknown[], queryParams?: object | null): string {\r\n const path = commands.join('/');\r\n const query = Object.entries(queryParams ?? {})\r\n .map(([key, value]) => `${key}=${encodeURIComponent(String(value))}`)\r\n .join('&');\r\n return query ? `${path}?${query}` : path;\r\n }\r\n}\r\n","import { Title } from '@angular/platform-browser';\r\nimport { ActivatedRoute } from '@angular/router';\r\nimport { BreadcrumbService, NotificationMqService } from '@dflat-core/web/core-services';\r\nimport { ApplicationService, RootDeps } from '@dflat-core/web/core-ux';\r\nimport { of } from 'rxjs';\r\nimport { DataServiceDouble } from './doubles/data-service-double';\r\nimport { EntityFacadeRegistrySpy } from './doubles/entity-facade-registry-spy';\r\nimport { RouterDouble } from './doubles/router-double';\r\nimport { BreadcrumbData } from './interfaces/breadcrumb-data';\r\n\r\n/**\r\n * Services donnés à un composant dérivé de BaseComponent pendant un test : vraies classes du\r\n * socle quand elles s'instancient sans dépendance (ApplicationService, registre de façades et\r\n * façades, fil d'Ariane, file de messages), doublures pour le service de données, le routeur et\r\n * la route. Les services que BaseComponent n'utilise pas (authentification, fenêtres modales,\r\n * alertes, mise en page...) ne sont pas fournis.\r\n */\r\nexport class BaseComponentTestContext {\r\n /** Doublure du service de données. */\r\n readonly dataService = new DataServiceDouble();\r\n\r\n /** Doublure du routeur. */\r\n readonly router: RouterDouble;\r\n\r\n /** Route active, sans paramètre. */\r\n readonly route: ActivatedRoute;\r\n\r\n /** Fil d'Ariane du socle. */\r\n readonly breadcrumb: BreadcrumbService;\r\n\r\n /** File de messages (notifications d'entité) du socle. */\r\n readonly notifications = new NotificationMqService();\r\n\r\n /** Registre de façades du socle, qui enregistre les façades demandées. */\r\n readonly registry: EntityFacadeRegistrySpy;\r\n\r\n /** ApplicationService du socle, construit sur les services ci-dessus. */\r\n readonly app: ApplicationService;\r\n\r\n /**\r\n * @param url URL courante du routeur.\r\n * @param title Service du titre de la page (TestBed.inject(Title)), utilisé par le fil d'Ariane.\r\n */\r\n constructor(url: string, title: Title) {\r\n this.router = new RouterDouble(url);\r\n this.route = { snapshot: { params: {}, queryParams: {} }, params: of({}), queryParams: of({}) } as unknown as ActivatedRoute;\r\n this.breadcrumb = new BreadcrumbService(title);\r\n\r\n const deps = {\r\n nav: { route: this.route, router: this.router.asRouter(), location: undefined },\r\n ui: { layout: undefined, breadcrumb: this.breadcrumb, toastr: undefined, alert: undefined, modal: undefined },\r\n data: { data: this.dataService.asDataService(), filter: undefined, selector: undefined },\r\n security: { auth: undefined, user: undefined },\r\n selection: undefined,\r\n test: undefined\r\n } as unknown as RootDeps;\r\n\r\n this.registry = new EntityFacadeRegistrySpy(deps);\r\n this.app = new ApplicationService(deps, this.notifications, undefined, undefined, undefined, this.registry);\r\n }\r\n\r\n /** Fil d'Ariane courant (dernière valeur publiée). */\r\n get currentBreadcrumb(): BreadcrumbData {\r\n let current: BreadcrumbData;\r\n this.breadcrumb.getCurrent().subscribe((data: BreadcrumbData) => current = data).unsubscribe();\r\n return current;\r\n }\r\n}\r\n","import { CallCounter } from './interfaces/call-counter';\r\n\r\n/**\r\n * Compte les appels de la méthode `method` de `target`, qui reste exécutée : équivalent, sans\r\n * outil de test, d'un espion transparent.\r\n */\r\nexport function countCalls<T extends object, K extends keyof T>(target: T, method: K): CallCounter {\r\n const original = target[method] as unknown as (...args: unknown[]) => unknown;\r\n const counter: CallCounter = { count: 0 };\r\n target[method] = ((...args: unknown[]) => {\r\n counter.count++;\r\n return original.apply(target, args);\r\n }) as unknown as T[K];\r\n return counter;\r\n}\r\n","import { TestRunner } from './interfaces/test-runner';\r\n\r\n/**\r\n * Fonctions globales de l'outil de test (describe, it, beforeEach, expect), lues sur globalThis\r\n * pour ne dépendre d'aucun outil ni de ses types.\r\n */\r\nexport function getTestRunner(): TestRunner {\r\n const runner = globalThis as unknown as Partial<TestRunner>;\r\n if (typeof runner.describe !== 'function' || typeof runner.it !== 'function'\r\n || typeof runner.beforeEach !== 'function' || typeof runner.expect !== 'function')\r\n throw new Error('Suites de test Dflat : describe, it, beforeEach et expect doivent être des fonctions globales (Vitest : option globals).');\r\n return runner as TestRunner;\r\n}\r\n","import { TestBed } from '@angular/core/testing';\r\nimport { Title } from '@angular/platform-browser';\r\nimport { Utilities } from '@dflat-core/web/core';\r\nimport { EntityViewModel, FilterViewModel, Notification } from '@dflat-core/web/core-model';\r\nimport { BaseComponent, EntitySections } from '@dflat-core/web/core-ux';\r\nimport { BaseComponentTestContext } from './base-component-test-context';\r\nimport { countCalls } from './count-calls';\r\nimport { getTestRunner } from './get-test-runner';\r\nimport { BaseComponentExpectations } from './interfaces/base-component-expectations';\r\nimport { BaseComponentFactory } from './interfaces/base-component-factory';\r\n\r\n/**\r\n * Suite de test standard d'un composant dérivé de BaseComponent : vérifie, sans rendre son\r\n * gabarit, le contrat de BaseComponent (création, clé d'entité, lien, titre, permission, façade,\r\n * ngOnInit, demande des onglets, Search / refresh, notifications, ngOnDestroy). À appeler au premier niveau d'un\r\n * fichier de test :\r\n * `describeBaseComponent('CountryComponent', (app, route) => new TestCountryComponent(app, route), { ... })`.\r\n * La suite n'utilise que les fonctions globales de l'outil de test (describe, it, beforeEach,\r\n * expect) et TestBed.\r\n *\r\n * @param name Nom du composant, repris dans le titre de la suite.\r\n * @param factory Fabrique du composant, appelée dans un contexte d'injection avant chaque test.\r\n * @param expectations Valeurs attendues du composant.\r\n */\r\nexport function describeBaseComponent<TFilter extends FilterViewModel, TData extends EntityViewModel>(\r\n name: string, factory: BaseComponentFactory<TFilter, TData>, expectations: BaseComponentExpectations): void {\r\n const { describe, it, beforeEach, expect } = getTestRunner();\r\n\r\n describe(`${name} (contrat de BaseComponent)`, () => {\r\n let context: BaseComponentTestContext;\r\n let component: BaseComponent<TFilter, TData>;\r\n\r\n /** Publie la notification de modification de l'entité `entityId` sur le canal de l'entité du composant. */\r\n const notify = (entityId: number): void =>\r\n context.notifications.publish(new Notification({ entityType: Utilities.GetChannel(expectations.entityKey), entityId }));\r\n\r\n beforeEach(() => {\r\n TestBed.configureTestingModule({ providers: expectations.providers ?? [] });\r\n context = new BaseComponentTestContext(expectations.path, TestBed.inject(Title));\r\n component = TestBed.runInInjectionContext(() => factory(context.app, context.route));\r\n });\r\n\r\n it('est créé dans un contexte d\\'injection', () => {\r\n expect(component).toBeTruthy();\r\n expect(component.destroy$.closed).toBe(false);\r\n });\r\n\r\n it('expose la clé d\\'entité, le lien, le titre et la permission attendus', () => {\r\n expect(component.getEntityKey()).toBe(expectations.entityKey);\r\n expect(component.getPath()).toBe(expectations.path);\r\n expect(component.getTitle()).toBe(expectations.title);\r\n expect(component.PermissionKey).toBe(expectations.permissionKey);\r\n expect(component.layoutKey).toBe(expectations.entityKey);\r\n });\r\n\r\n it('demande sa façade au registre avec la clé de l\\'entité', () => {\r\n expect(context.registry.requests.length).toBe(1);\r\n expect(context.registry.requests[0].key).toBe(expectations.entityKey);\r\n expect(context.registry.requests[0].facade).toBe(component.facade);\r\n expect(component.facade.actions.handler.path).toBe(expectations.path);\r\n });\r\n\r\n it('ngOnInit publie le fil d\\'Ariane du composant principal', () => {\r\n component.ngOnInit();\r\n\r\n expect(component.displayComponent).toBe(true);\r\n expect(component.actionTitle).toBe(expectations.title);\r\n expect(context.currentBreadcrumb).toEqual({\r\n description: expectations.title,\r\n display: true,\r\n header: expectations.title,\r\n levels: [{ icon: component.getIcon(), link: [expectations.path], title: expectations.title }]\r\n });\r\n });\r\n\r\n it('ngOnInit ne publie pas de fil d\\'Ariane pour un composant d\\'onglet', () => {\r\n component.IsTheMainComponent = false;\r\n component.ngOnInit();\r\n\r\n expect(context.currentBreadcrumb.display).toBe(false);\r\n });\r\n\r\n it('ngOnInit branche le chargement sur dataService.search avec la clé de l\\'entité', () => {\r\n context.dataService.searchResult = { Values: [{ Id: 7, Name: 'avant' }], TotalRowCount: 1 };\r\n component.ngOnInit();\r\n\r\n const filter: FilterViewModel = { Offset: 0, Take: 25 };\r\n let total: number | undefined;\r\n component.facade.data.loadData(filter).subscribe(result => total = result.TotalRowCount);\r\n\r\n expect(context.dataService.searchCalls).toEqual([{ entityKey: expectations.entityKey, filter }]);\r\n expect(total).toBe(1);\r\n expect(component.facade.data.getOriginal(7).Name).toBe('avant');\r\n });\r\n\r\n it('ngOnInit s\\'abonne une seule fois aux notifications de l\\'entité', () => {\r\n component.ngOnInit();\r\n const first = component.notif;\r\n component.ngOnInit();\r\n\r\n expect(first.closed).toBe(true);\r\n expect(component.notif.closed).toBe(false);\r\n });\r\n\r\n if (expectations.sections) {\r\n it('ngOnInit enregistre les onglets attendus', async () => {\r\n component.ngOnInit();\r\n // Les onglets générés sont enregistrés dans une microtâche, après ceux du composant spécifique.\r\n await Promise.resolve();\r\n await Promise.resolve();\r\n\r\n let sections: (string | undefined)[] = [];\r\n component.facade.sections.sections$.subscribe(list => sections = list.map(s => s.Name)).unsubscribe();\r\n expect(sections).toEqual(expectations.sections);\r\n });\r\n }\r\n\r\n if (expectations.requestsSections !== undefined) {\r\n const title = expectations.requestsSections\r\n ? 'ngOnInit demande les onglets de l\\'entité au registre du socle (EntitySections)'\r\n : 'ngOnInit ne demande pas d\\'onglet au registre du socle (EntitySections)';\r\n\r\n it(title, async () => {\r\n const original = EntitySections.registerSections;\r\n const requests: { entityKey: string; facade: unknown }[] = [];\r\n EntitySections.registerSections = ((entityKey: string, facade: unknown) => {\r\n requests.push({ entityKey, facade });\r\n }) as typeof EntitySections.registerSections;\r\n try {\r\n component.ngOnInit();\r\n // Les onglets sont demandés dans une microtâche, après ceux du composant spécifique.\r\n await Promise.resolve();\r\n await Promise.resolve();\r\n }\r\n finally {\r\n EntitySections.registerSections = original;\r\n }\r\n\r\n expect(requests.length).toBe(expectations.requestsSections ? 1 : 0);\r\n if (expectations.requestsSections) {\r\n expect(requests[0].entityKey).toBe(expectations.entityKey);\r\n expect(requests[0].facade).toBe(component.facade);\r\n }\r\n });\r\n }\r\n\r\n it('Search copie le filtre, remet la page à zéro et navigue vers l\\'URL du filtre', () => {\r\n component.ngOnInit();\r\n const loads = countCalls(component.facade.filter, 'triggerSearch');\r\n\r\n component.Search({ Keywords: 'abc', Offset: 50 });\r\n\r\n expect(component.Filter.Keywords).toBe('abc');\r\n expect(component.Filter.Offset).toBe(0);\r\n expect(context.router.navigations.length).toBe(1);\r\n expect(context.router.navigations[0].commands).toEqual([expectations.path]);\r\n expect(context.router.navigations[0].extras?.queryParams).toEqual({ Keywords: 'abc', Offset: 0 });\r\n expect(loads.count).toBe(0);\r\n });\r\n\r\n it('Search charge les données quand l\\'URL porte déjà le filtre', () => {\r\n component.ngOnInit();\r\n const loads = countCalls(component.facade.filter, 'triggerSearch');\r\n\r\n component.Search({ Keywords: 'abc' });\r\n component.Search({ Keywords: 'abc' });\r\n\r\n expect(context.router.navigations.length).toBe(1);\r\n expect(loads.count).toBe(1);\r\n });\r\n\r\n it('Search garde la page demandée sans remise à zéro', () => {\r\n component.ngOnInit();\r\n\r\n component.Search({ Offset: 50 }, false);\r\n\r\n expect(component.Filter.Offset).toBe(50);\r\n });\r\n\r\n it('Search et refresh chargent les données sans naviguer pour un composant d\\'onglet', () => {\r\n component.IsTheMainComponent = false;\r\n component.ngOnInit();\r\n const loads = countCalls(component.facade.filter, 'triggerSearch');\r\n\r\n component.Search({ Keywords: 'abc', Offset: 50 });\r\n expect(loads.count).toBe(1);\r\n expect(component.Filter.Keywords).toBe('abc');\r\n\r\n component.Filter.Offset = 50;\r\n component.refresh();\r\n expect(loads.count).toBe(2);\r\n expect(component.Filter.Offset).toBe(0);\r\n expect(context.router.navigations.length).toBe(0);\r\n });\r\n\r\n it('met à jour la ligne d\\'une entité chargée à sa notification', () => {\r\n context.dataService.searchResult = { Values: [{ Id: 7, Name: 'avant' }], TotalRowCount: 1 };\r\n component.ngOnInit();\r\n component.facade.data.loadData({ Offset: 0, Take: 25 }).subscribe();\r\n context.dataService.details = { Id: 7, Name: 'après' };\r\n\r\n notify(7);\r\n\r\n expect(context.dataService.detailsCalls).toEqual([{ entityKey: expectations.entityKey, id: 7 }]);\r\n expect(component.facade.data.getOriginal(7).Name).toBe('après');\r\n });\r\n\r\n it('ignore la notification d\\'une entité non chargée, ou recharge si loadDataOnNotification', () => {\r\n component.ngOnInit();\r\n const loads = countCalls(component.facade.filter, 'triggerSearch');\r\n\r\n notify(8);\r\n expect(context.dataService.detailsCalls.length).toBe(0);\r\n expect(loads.count).toBe(0);\r\n\r\n component.loadDataOnNotification = true;\r\n notify(8);\r\n expect(context.dataService.detailsCalls.length).toBe(0);\r\n expect(loads.count).toBe(1);\r\n });\r\n\r\n it('ngOnDestroy termine destroy$, se désabonne des notifications et efface le fil d\\'Ariane', () => {\r\n component.ngOnInit();\r\n let emitted = 0;\r\n let completed = false;\r\n component.destroy$.subscribe({ next: () => emitted++, complete: () => completed = true });\r\n\r\n component.ngOnDestroy();\r\n\r\n expect(emitted).toBe(1);\r\n expect(completed).toBe(true);\r\n expect(component.notif.closed).toBe(true);\r\n expect(context.currentBreadcrumb.display).toBe(false);\r\n\r\n notify(7);\r\n expect(context.dataService.detailsCalls.length).toBe(0);\r\n });\r\n });\r\n}\r\n","import { getTestRunner } from './get-test-runner';\r\nimport { GeneratedModuleExpectations } from './interfaces/generated-module-expectations';\r\n\r\n/** Valeurs des propriétés statiques de type chaîne d'une classe de constantes (clés d'entité, permissions). */\r\nfunction getConstants(constants: object): string[] {\r\n return Object.values(constants).filter((value): value is string => typeof value === 'string');\r\n}\r\n\r\n/** Valeurs présentes plusieurs fois dans `values`. */\r\nfunction getDuplicates(values: string[]): string[] {\r\n return values.filter((value, index) => values.indexOf(value) !== index);\r\n}\r\n\r\n/**\r\n * Suite de test standard d'un module généré (dflat -g ts) : cohérence de ses composants, routes,\r\n * sélecteurs, clés d'entité, onglets, éléments de menu et permissions. Chaque test compare à une liste\r\n * vide la liste des éléments en défaut, que l'outil de test affiche en cas d'échec.\r\n *\r\n * @param name Nom du module (ex. System), repris dans le titre de la suite.\r\n * @param module Éléments générés du module.\r\n */\r\nexport function describeGeneratedModule(name: string, module: GeneratedModuleExpectations): void {\r\n const { describe, it, expect } = getTestRunner();\r\n\r\n describe(`Module ${name} (éléments générés)`, () => {\r\n const paths = module.routes.map(route => route.path ?? '');\r\n\r\n it('donne une route à chaque composant généré', () => {\r\n const missing = module.components\r\n .filter(component => !module.routes.some(route => route.component === component))\r\n .map(component => component.name);\r\n expect(missing).toEqual([]);\r\n });\r\n\r\n it('n\\'a que des routes de composants générés, de chemins distincts', () => {\r\n const unknown = module.routes\r\n .filter(route => !module.components.some(component => component === route.component))\r\n .map(route => route.path);\r\n expect(unknown).toEqual([]);\r\n expect(getDuplicates(paths)).toEqual([]);\r\n });\r\n\r\n it('a des clés d\\'entité uniques, dont celles des routes', () => {\r\n const keys = getConstants(module.entityKeys);\r\n expect(getDuplicates(keys)).toEqual([]);\r\n expect(paths.filter(path => !keys.includes(path))).toEqual([]);\r\n });\r\n\r\n it('donne à chaque sélecteur d\\'entité le composant de la route de sa clé', () => {\r\n const mismatched = module.selectors\r\n .filter(selector => !module.routes.some(route => route.path === selector.key && route.component === selector.component))\r\n .map(selector => selector.key);\r\n expect(mismatched).toEqual([]);\r\n });\r\n\r\n if (module.sectionProvider) {\r\n const provider = module.sectionProvider;\r\n\r\n if (module.sections) {\r\n const expected = module.sections;\r\n\r\n it('déclare les onglets attendus de chaque entité, dans l\\'ordre', () => {\r\n const declared: { [entityKey: string]: string[] } = {};\r\n provider.getEntityKeys().forEach(key => declared[key] = provider.getSections(key).map(section => section.title));\r\n expect(declared).toEqual(expected);\r\n });\r\n }\r\n\r\n it('ne déclare d\\'onglets que pour des entités du module, vers des composants générés du module', () => {\r\n const keys = getConstants(module.entityKeys);\r\n expect(provider.getEntityKeys().filter(key => !keys.includes(key))).toEqual([]);\r\n\r\n const unknown = provider.getEntityKeys()\r\n .flatMap(key => provider.getSections(key).map(section => ({ key, section })))\r\n .filter(({ section }) => !module.components.some(component => component === section.component))\r\n .map(({ key, section }) => `${key} : ${section.title}`);\r\n expect(unknown).toEqual([]);\r\n });\r\n\r\n it('ne déclare aucun onglet pour une entité sans onglet', () => {\r\n expect(provider.getSections('')).toEqual([]);\r\n });\r\n }\r\n\r\n it('fait pointer chaque élément de menu généré vers une route existante', () => {\r\n const links = paths.map(path => module.getLink(path));\r\n const orphans = module.menuItems.filter(item => !links.includes(item.link ?? '')).map(item => item.link);\r\n expect(orphans).toEqual([]);\r\n });\r\n\r\n if (module.permissions) {\r\n const permissions = getConstants(module.permissions);\r\n\r\n it('donne à chaque élément de menu généré une permission du module', () => {\r\n const ungranted = module.menuItems.filter(item => !permissions.includes(item.permissionKey ?? '')).map(item => item.link);\r\n expect(ungranted).toEqual([]);\r\n });\r\n }\r\n });\r\n}\r\n"],"mappings":"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"}
@@ -19313,6 +19313,35 @@ i0.ɵɵngDeclareClassMetadata({
19313
19313
  }]
19314
19314
  });
19315
19315
  const PP_SERVICE = new InjectionToken("PP_SERVICE");
19316
+ var EntitySections = class EntitySections {
19317
+ static {
19318
+ this.CACHE_NAME = "FormSections";
19319
+ }
19320
+ static {
19321
+ this.providers = /* @__PURE__ */ new Map();
19322
+ }
19323
+ static register(provider) {
19324
+ (provider?.getEntityKeys() ?? []).forEach((key) => EntitySections.providers.set(key.toLowerCase(), {
19325
+ provider,
19326
+ key
19327
+ }));
19328
+ }
19329
+ static find(entityKey) {
19330
+ return entityKey ? EntitySections.providers.get(entityKey.toLowerCase())?.provider : void 0;
19331
+ }
19332
+ static related(title, component, filterProperty) {
19333
+ return {
19334
+ title,
19335
+ component,
19336
+ filterProperty
19337
+ };
19338
+ }
19339
+ static registerSections(entityKey, facade) {
19340
+ const registered = entityKey ? EntitySections.providers.get(entityKey.toLowerCase()) : void 0;
19341
+ if (!registered) return;
19342
+ facade.facadeDeps.data.getCachedValue(entityKey, EntitySections.CACHE_NAME, () => registered.provider.getSections(registered.key)).subscribe((sections) => sections.forEach((section) => facade.sections.registerRelatedSection(section.title, section.component, section.filterProperty)));
19343
+ }
19344
+ };
19316
19345
  var BaseEditorComponent = class BaseEditorComponent extends DflatBaseComponent {
19317
19346
  getEntityKey() {
19318
19347
  return "";
@@ -21461,6 +21490,6 @@ var SortingState = class {
21461
21490
  this.direction$.next("asc");
21462
21491
  }
21463
21492
  };
21464
- export { Action, ActionContext, ActionDefinition, ActionHandlerContext, ActionPanelComponent, ActionService, ActionsComponent, ActionsContentComponent, AddclassDirective, AlertDialog, AlertMessage, AlertService, AppConfigService, ApplicationService, BaseArrayParameterTypeComponent, BaseComponent, BaseEditorComponent, BaseFormContext, BaseItemListComponent, BaseParameterTypeComponent, BaseShortcutsDirective, BreadcrumbComponent, CanDeactivateGuard, CellRendererComponent, ChangeDetectionContext, ChangeUtilities, CollapseDirective, ConfirmModalComponent, ConfirmOptions, ContextChoice, CoreUxModule, DataContext, DataGridComponent, DataGridStyleContext, DataListComponent, DatagridCellComponent, DatagridCustomComponent, DefaultDataGridStyleContext, DefaultFilterComponent, DefaultLayoutComponent, DflatBaseComponent, DflatBaseFilterComponent, DialogComponent, DialogComponentParameter, DialogType, DraggableDirective, DroppableDirective, DynamicFieldComponent, DynamicFilterComponent, DynamicFilterExtraComponent, DynamicFilterPanel, DynamicFormComponent, EXTENDED_ACTION_COMPONENT, EditModeDirective, EditorContext, EditorTabComponent, EditorTabSetComponent, EntityConfigContext, EntityFacade, EntityFacadeRegistryService, EntitySelectors, EntityUpdater, ExportDataModalComponent, ExtendedAction, ExtendedActionComponent, FacadeLoadGuard, FacadeModalService, FieldComponent, FilterContext, FilterFormModalComponent, FilterLifecycleGuard, FilterSummaryComponent, FilterSummaryPanelComponent, FireAuthService, FocusableDirective, FormPasswordComponent, FormWrapper, GridFieldComponent, GridRowComponent, GridStylePipe, HideOnThresholdDirective, IIsCancelWithWarning, ImageComponent, ImageService, InputSelectorComponent, InputSelectorOptions, InputTreeSelectorComponent, InsertionDirective, LayoutContext, LayoutHandlerDirective, LayoutService, LoginLayoutComponent, LogsComponent, MenuComponent, MenuDashboardComponent, MessageSeverity, ModalService, ModalStateService, NewEditorComponent, NewPaginatorComponent, OldDataPageComponent, OldLayoutHandlerDirective, OnEnterDirective, OrderableModalComponent, OrderableModalService, PP_SERVICE, PageLayoutComponent, PaginatorComponent, ParameterFieldComponent, ParameterTypeBoolArrayComponent, ParameterTypeBoolComponent, ParameterTypeDateArrayComponent, ParameterTypeDateComponent, ParameterTypeNumberArrayComponent, ParameterTypeNumberComponent, ParameterTypePasswordComponent, ParameterTypeTextArrayComponent, ParameterTypeTextComponent, PropertyChangeBus, PropertyEditorComponent, PropertyGridComponent, ROOT_DEPS, RowRefDirective, RowRefHost, RowRegistry, SearchResultsComponent, SectionContext, SelectionContext, SelectionService, SelectionStyler, SelectorComponent, SelectorDataSource, SelectorMapperServiceBase, SelectorRegistry, SortableColumnComponent, SortingState, StandardPageLayoutComponent, TRACK_RECORD_MODAL_COMPONENT, Tab, TabHeadingDirective, TabTranscludeDirective, Tabset, TestService, TextViewerComponent, TimePickerComponent, ToDefinePipe, ToFixedSizeIntegerPipe, TooltipDirective, TrackRecordModalComponent, TreeComponent, ValidateOnEnterDirective, ValidationErrorComponent, ValidationModalComponent, ValidationService, ViewModeDirective, ViewModelLoaderService, WizardComponent, WizardStepComponent, downloadFile, filterFromId, layoutKeyToString, normalisedOrder };
21493
+ export { Action, ActionContext, ActionDefinition, ActionHandlerContext, ActionPanelComponent, ActionService, ActionsComponent, ActionsContentComponent, AddclassDirective, AlertDialog, AlertMessage, AlertService, AppConfigService, ApplicationService, BaseArrayParameterTypeComponent, BaseComponent, BaseEditorComponent, BaseFormContext, BaseItemListComponent, BaseParameterTypeComponent, BaseShortcutsDirective, BreadcrumbComponent, CanDeactivateGuard, CellRendererComponent, ChangeDetectionContext, ChangeUtilities, CollapseDirective, ConfirmModalComponent, ConfirmOptions, ContextChoice, CoreUxModule, DataContext, DataGridComponent, DataGridStyleContext, DataListComponent, DatagridCellComponent, DatagridCustomComponent, DefaultDataGridStyleContext, DefaultFilterComponent, DefaultLayoutComponent, DflatBaseComponent, DflatBaseFilterComponent, DialogComponent, DialogComponentParameter, DialogType, DraggableDirective, DroppableDirective, DynamicFieldComponent, DynamicFilterComponent, DynamicFilterExtraComponent, DynamicFilterPanel, DynamicFormComponent, EXTENDED_ACTION_COMPONENT, EditModeDirective, EditorContext, EditorTabComponent, EditorTabSetComponent, EntityConfigContext, EntityFacade, EntityFacadeRegistryService, EntitySections, EntitySelectors, EntityUpdater, ExportDataModalComponent, ExtendedAction, ExtendedActionComponent, FacadeLoadGuard, FacadeModalService, FieldComponent, FilterContext, FilterFormModalComponent, FilterLifecycleGuard, FilterSummaryComponent, FilterSummaryPanelComponent, FireAuthService, FocusableDirective, FormPasswordComponent, FormWrapper, GridFieldComponent, GridRowComponent, GridStylePipe, HideOnThresholdDirective, IIsCancelWithWarning, ImageComponent, ImageService, InputSelectorComponent, InputSelectorOptions, InputTreeSelectorComponent, InsertionDirective, LayoutContext, LayoutHandlerDirective, LayoutService, LoginLayoutComponent, LogsComponent, MenuComponent, MenuDashboardComponent, MessageSeverity, ModalService, ModalStateService, NewEditorComponent, NewPaginatorComponent, OldDataPageComponent, OldLayoutHandlerDirective, OnEnterDirective, OrderableModalComponent, OrderableModalService, PP_SERVICE, PageLayoutComponent, PaginatorComponent, ParameterFieldComponent, ParameterTypeBoolArrayComponent, ParameterTypeBoolComponent, ParameterTypeDateArrayComponent, ParameterTypeDateComponent, ParameterTypeNumberArrayComponent, ParameterTypeNumberComponent, ParameterTypePasswordComponent, ParameterTypeTextArrayComponent, ParameterTypeTextComponent, PropertyChangeBus, PropertyEditorComponent, PropertyGridComponent, ROOT_DEPS, RowRefDirective, RowRefHost, RowRegistry, SearchResultsComponent, SectionContext, SelectionContext, SelectionService, SelectionStyler, SelectorComponent, SelectorDataSource, SelectorMapperServiceBase, SelectorRegistry, SortableColumnComponent, SortingState, StandardPageLayoutComponent, TRACK_RECORD_MODAL_COMPONENT, Tab, TabHeadingDirective, TabTranscludeDirective, Tabset, TestService, TextViewerComponent, TimePickerComponent, ToDefinePipe, ToFixedSizeIntegerPipe, TooltipDirective, TrackRecordModalComponent, TreeComponent, ValidateOnEnterDirective, ValidationErrorComponent, ValidationModalComponent, ValidationService, ViewModeDirective, ViewModelLoaderService, WizardComponent, WizardStepComponent, downloadFile, filterFromId, layoutKeyToString, normalisedOrder };
21465
21494
 
21466
21495
  //# sourceMappingURL=dflat-core-web-core-ux.mjs.map