Преглед изворни кода

restore tests and minor fixes

Olivier Massot пре 2 година
родитељ
комит
404c68484e

+ 6 - 0
models/Access/Access.ts

@@ -2,6 +2,8 @@ import { Historical } from '~/types/interfaces'
 import Person from "~/models/Person/Person";
 import ApiModel from "~/models/ApiModel";
 import {HasOne, Num, Uid, Attr} from "pinia-orm/dist/decorators";
+import {IriEncoded} from "~/models/decorators";
+import Organization from "~/models/Organization/Organization";
 
 /**
  * AP2i Model : Access
@@ -22,4 +24,8 @@ export default class Access extends ApiModel {
 
   @Attr({})
   declare historical: Historical
+
+  @Attr(null)
+  @IriEncoded(Organization)
+  declare organization: number | null
 }

+ 0 - 9
models/ApiResource.ts

@@ -5,17 +5,8 @@ import {Model} from "pinia-orm";
  */
 export class ApiResource extends Model {
 
-    private _model: typeof ApiResource | undefined = undefined;
     public static relations: Record<string, ApiResource>
 
-    public getModel() {
-        return this._model
-    }
-
-    public setModel(model: typeof ApiResource) {
-        this._model = model
-    }
-
     /**
      * Fix the 'Cannot stringify arbitrary non-POJOs' warning, meaning server can not parse the store
      *

+ 1 - 1
services/data/enumManager.ts

@@ -18,7 +18,7 @@ class EnumManager {
 
         const response = await this.apiRequestService.get(url)
 
-        const { data } = await HydraNormalizer.denormalize(response)
+        const { data } = HydraNormalizer.denormalize(response)
 
         const enum_: Enum = []
         for (const key in data.items) {

+ 75 - 50
services/data/normalizer/hydraNormalizer.ts

@@ -11,7 +11,7 @@ import {isArray} from "lodash";
 class HydraNormalizer {
 
   /**
-   * Normalize the given entity into an Hydra formatted content.
+   * Normalize the given entity into a Hydra formatted content.
    * @param entity
    */
   public static normalizeEntity(entity: ApiResource): AnyJson {
@@ -49,56 +49,13 @@ class HydraNormalizer {
 
   protected static getData(hydraData: AnyJson, model?: typeof ApiResource): AnyJson {
     if (hydraData['@type'] === 'hydra:Collection') {
-      let members = hydraData['hydra:member']
-      let results = []
-
-      for (let item of members) {
-        results.push(HydraNormalizer.denormalizeItem(item, model))
-      }
-
-      return results
+      const members = hydraData['hydra:member']
+      return members.map((item: AnyJson) => HydraNormalizer.denormalizeItem(item, model))
     } else {
       return HydraNormalizer.denormalizeItem(hydraData, model)
     }
   }
 
-  /**
-   * Dénormalise un item d'une réponse hydra
-   *
-   * @param item
-   * @param model
-   * @protected
-   */
-  protected static denormalizeItem(item: AnyJson, model?: typeof ApiResource): AnyJson {
-
-    if (!model) {
-      const {entity, id} = HydraNormalizer.parseIRI(item['@id'])
-      if (!id) {
-        throw Error('De-normalization error : Could not determine the model of the entity')
-      }
-      model = models[entity]
-    }
-
-    const instance = new model(item)
-
-    const iriEncodedFields = HydraNormalizer.getIriEncodedFields(instance)
-
-    for (const field in iriEncodedFields) {
-      const value = instance[field]
-      const targetEntity = iriEncodedFields[field].entity
-
-      if (isArray(value)) {
-        instance[field] = value.map((iri: string) => {
-          return HydraNormalizer.getIdFromIri(iri, targetEntity)
-        })
-      } else {
-        instance[field] = HydraNormalizer.getIdFromIri(value, targetEntity)
-      }
-    }
-
-    return instance
-  }
-
   /**
    * Génère les métadonnées d'un item ou d'une collection
    *
@@ -143,16 +100,84 @@ class HydraNormalizer {
     return metadata
   }
 
+  /**
+   * Dénormalise un item d'une réponse hydra
+   *
+   * @param item
+   * @param model
+   * @protected
+   */
+  protected static denormalizeItem(item: AnyJson, model?: typeof ApiResource): AnyJson {
+
+    if (model) {
+      return HydraNormalizer.denormalizeEntity(model, item)
+    }
+
+    if (!item.hasOwnProperty('@id')) {
+      // Not hydra formatted
+      console.error('De-normalization error : the item is not hydra formatted', item)
+      return item
+    }
+
+    if (item['@id'].match(/\/api\/enum\/\w+/)) {
+      return HydraNormalizer.denormalizeEnum(item)
+    }
+
+    let entity = null
+
+    // On essaie de déterminer la nature de l'objet à partir de son id
+    try {
+      const iri = HydraNormalizer.parseEntityIRI(item['@id'])
+      entity = iri.entity
+    } catch (e) {
+      console.error('De-normalization error : could not parse the IRI', item)
+      return item
+    }
+
+    if (entity && models.hasOwnProperty(entity)) {
+      model = models[entity]
+      return HydraNormalizer.denormalizeEntity(model, item)
+    }
+
+    throw Error('De-normalization error : Could not determine the type of the entity '
+        + item['@id'] + ' (found: ' + entity + ')')
+  }
+
+  protected static denormalizeEntity(model: typeof ApiResource, item: AnyJson) {
+    const instance = new model(item)
+
+    const iriEncodedFields = HydraNormalizer.getIriEncodedFields(instance)
+
+    for (const field in iriEncodedFields) {
+      const value = instance[field]
+      const targetEntity = iriEncodedFields[field].entity
+
+      if (isArray(value)) {
+        instance[field] = value.map((iri: string) => {
+          return HydraNormalizer.getIdFromEntityIri(iri, targetEntity)
+        })
+      } else {
+        instance[field] = HydraNormalizer.getIdFromEntityIri(value, targetEntity)
+      }
+    }
+
+    return instance
+  }
+
+  protected static denormalizeEnum(item: AnyJson): AnyJson {
+    return item
+  }
+
   /**
    * Parse the given IRI
    * @param iri
    * @protected
    */
-  protected static parseIRI(iri: string) {
+  protected static parseEntityIRI(iri: string) {
     const rx = /\/api\/(\w+)\/(\d+)/
     const match = rx.exec(iri)
     if (!match) {
-      throw Error('could not parse the IRI : ' + iri)
+      throw Error('could not parse the IRI : ' + JSON.stringify(iri))
     }
 
     return {
@@ -181,8 +206,8 @@ class HydraNormalizer {
    * @param expectedEntity
    * @protected
    */
-  protected static getIdFromIri(iri: string, expectedEntity: string): number {
-    const { entity, id } = HydraNormalizer.parseIRI(iri)
+  protected static getIdFromEntityIri(iri: string, expectedEntity: string): number | string {
+    const { entity, id } = HydraNormalizer.parseEntityIRI(iri)
     if (entity !== expectedEntity) {
       throw Error("IRI entity does not match the field's target entity (" + entity + ' != ' + expectedEntity + ")")
     }

+ 0 - 32
services/data/normalizer/hydraNormalizer__.ts

@@ -1,32 +0,0 @@
-import {AnyJson} from "~/types/data";
-import ApiResource from "~/models/ApiResource";
-import {isArray, isNumber} from "lodash";
-import UrlUtils from "~/services/utils/urlUtils";
-
-/**
- * Normalisation du format de données Hydra
- */
-class HydraNormalizer__ {
-    public static normalizeEntity(entity: ApiResource): AnyJson {
-
-        const prototype = Object.getPrototypeOf(entity)
-        const relations = prototype.constructor.relations
-
-        for (const field in relations) {
-            const value = entity[field]
-            const targetEntity = relations[field].entity
-
-            if (isArray(value)) {
-                entity[field] = value.map((id: number) => {
-                    return UrlUtils.makeIRI(targetEntity, id)
-                })
-            } else {
-                entity[field] = UrlUtils.makeIRI(targetEntity, value)
-            }
-        }
-
-        return entity.$toJson()
-    }
-}
-
-export default HydraNormalizer

+ 20 - 9
tests/units/services/data/entityManager.test.ts

@@ -1,19 +1,31 @@
-import { describe, test, it, expect } from 'vitest'
+import { describe, test, vi, expect, beforeEach, afterEach } from 'vitest'
 import EntityManager from "~/services/data/entityManager";
 import ApiResource from "~/models/ApiResource";
 import ApiModel from "~/models/ApiModel";
 import ApiRequestService from "~/services/data/apiRequestService";
 import {Element, Repository} from "pinia-orm";
+import {Str, Uid} from "pinia-orm/dist/decorators";
 
 
 
 class DummyApiResource extends ApiResource {
     static entity = 'dummyResource'
 
+    @Uid()
+    declare id: number | string
+
+    @Str(null)
+    declare name: string
 }
 
 class DummyApiModel extends ApiModel {
     static entity = 'dummyModel'
+
+    @Uid()
+    declare id: number | string
+
+    @Str(null)
+    declare name: string
 }
 
 let _console: any = {
@@ -142,7 +154,6 @@ describe('newInstance', () => {
 
         // @ts-ignore
         const entity = new DummyApiResource(properties)
-        entity.setModel = vi.fn((model: typeof ApiResource) => null)
 
         // @ts-ignore
         repo.make = vi.fn((properties: object) => {
@@ -225,7 +236,7 @@ describe('fetch', () => {
 
         expect(entityManager.find).toHaveBeenCalledWith(DummyApiResource, 1)
         expect(apiRequestService.get).toHaveBeenCalledWith('api/dummyResource/1')
-        expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, properties)
+        expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, { id: 1, name: null, _model: undefined})
 
         expect(result).toEqual(entity)
     })
@@ -264,7 +275,7 @@ describe('fetch', () => {
 
         expect(entityManager.find).toHaveBeenCalledTimes(0)
         expect(apiRequestService.get).toHaveBeenCalledWith('api/dummyResource/1')
-        expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, properties)
+        expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, { id: 1, name: null, _model: undefined})
 
         expect(result).toEqual(entity)
     })
@@ -295,9 +306,9 @@ describe('fetchCollection', () => {
 
         expect(apiRequestService.get).toHaveBeenCalledWith('api/dummyResource', null)
         expect(entityManager.newInstance).toHaveBeenCalledTimes(3)
-        expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {id: 1})
-        expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {id: 2})
-        expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {id: 3})
+        expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, { id: 1, name: null, _model: undefined })
+        expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, { id: 2, name: null, _model: undefined })
+        expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, { id: 3, name: null, _model: undefined })
 
         expect(result.items).toEqual([
             new DummyApiResource({id: 1}),
@@ -405,8 +416,8 @@ describe('persist', () => {
             return {id: 'tmp1', name: 'bob'}
         })
 
-        // TODO: attendre de voir si cet appel est nécessaire dans l'entity manager
-        // entityManager.cast = vi.fn((model: typeof ApiResource, entity: ApiResource): ApiResource => entity)
+        // @ts-ignore
+        entityManager.cast = vi.fn((model: typeof ApiResource, entity: ApiResource): ApiResource => entity)
 
         const response = { id: 1, name: 'bob' }
         // @ts-ignore

+ 1 - 1
tests/units/services/data/enumManager.test.ts

@@ -1,4 +1,4 @@
-import { describe, test, it, expect } from 'vitest'
+import { describe, test, it, expect, beforeEach } from 'vitest'
 import ApiRequestService from "~/services/data/apiRequestService";
 import EnumManager from "~/services/data/enumManager";
 import {VueI18n} from "vue-i18n";

+ 49 - 44
tests/units/services/data/normalizer/hydraDenormalizer.test.ts → tests/units/services/data/normalizer/hydraNormalizer.test.ts

@@ -2,6 +2,7 @@ import { describe, test, it, expect } from 'vitest'
 import {AnyJson} from "~/types/data";
 import HydraNormalizer from "~/services/data/normalizer/hydraNormalizer";
 import {METADATA_TYPE} from "~/types/enum/data";
+import Access from "~/models/Access/Access";
 
 describe('denormalize', () => {
 
@@ -15,12 +16,13 @@ describe('denormalize', () => {
             person: {
                 '@type': 'Person',
                 id: 11344,
-                name: 'BRUEL',
-                givenName: 'Patrick'
+                name: 'Trennet',
+                givenName: 'Charles',
+                username: "ctrennet"
             }
         }
 
-        const result = HydraNormalizer.denormalize(data)
+        const result = HydraNormalizer.denormalize(data, Access)
 
         // @ts-ignore
         expect(result.data).toEqual(HydraNormalizer.getData(data))
@@ -29,24 +31,24 @@ describe('denormalize', () => {
 
         const expected = {
             "data": {
-                "@context": "/api/contexts/Access",
-                "@id": "/api/accesses/7351",
-                "@type": "Access",
                 "id": 7351,
-                "organization": "/api/organizations/37306",
+                "organization": 37306,
                 "person": {
-                    "@type": "Person",
-                    "givenName": "Patrick",
+                    "accessId": null,
+                    "givenName": "Charles",
                     "id": 11344,
-                    "name": "BRUEL"
-                }
+                    "name": "Trennet",
+                    "username": "ctrennet"
+                },
+                "activityYear": 0,
+                "historical": {}
             },
             "metadata": {
                 "type": METADATA_TYPE.ITEM
             }
         }
 
-        expect(result).toStrictEqual<AnyJson>(expected)
+        expect(result).toEqual(expected)
     })
 
     it('should parse a API Collection response and return a JSON Object', () => {
@@ -59,17 +61,16 @@ describe('denormalize', () => {
                 organization: '/api/organizations/37306',
                 id: 7351,
                 person: {
-                    '@type': 'Person',
                     id: 11344,
-                    name: 'BRUEL',
-                    givenName: 'Patrick'
+                    name: 'Trennet',
+                    givenName: 'Charles',
+                    username: "ctrennet"
                 }
             }, {
                 '@id': '/api/accesses/7352',
                 organization: '/api/organizations/37306',
                 id: 7352,
                 person: {
-                    '@type': 'Person',
                     id: 11345,
                     name: 'BRASSENS',
                     givenName: 'George'
@@ -86,50 +87,54 @@ describe('denormalize', () => {
             }
         }
 
-        const result = HydraNormalizer.denormalize(data)
+        const result = HydraNormalizer.denormalize(data, Access)
 
         // @ts-ignore
         expect(result.data).toEqual(HydraNormalizer.getData(data))
         // @ts-ignore
         expect(result.metadata).toEqual(HydraNormalizer.getMetadata(data))
 
-        const expected = JSON.stringify(
-            {"data":[
+        const expected =
+        {
+            "data": [
                 {
-                    '@id': '/api/accesses/7351',
-                    organization: '/api/organizations/37306',
                     id: 7351,
-                    person:
-                        {
-                            '@type': 'Person',
-                            id: 11344,
-                            name: 'BRUEL',
-                            givenName: 'Patrick'
-                        }
+                    organization: 37306,
+                    person: {
+                        id: 11344,
+                        name: 'Trennet',
+                        givenName: 'Charles',
+                        accessId: null,
+                        username: "ctrennet"
+                    },
+                    activityYear: 0,
+                    historical: {}
                 },
                 {
-                    '@id': '/api/accesses/7352',
-                    organization: '/api/organizations/37306',
                     id: 7352,
-                    person:
-                        {
-                            '@type': 'Person',
-                            id: 11345,
-                            name: 'BRASSENS',
-                            givenName: 'George'
-                        }
+                    organization: 37306,
+                    person: {
+                        id: 11345,
+                        name: 'BRASSENS',
+                        givenName: 'George',
+                        accessId: null,
+                        username: null
+                    },
+                    activityYear: 0,
+                    historical: {}
                 }
             ],
             'metadata': {
-                'firstPage': 1,
-                'lastPage': 5,
-                'nextPage': 4,
-                'previousPage': 2,
-                'type': 1
+                firstPage: 1,
+                lastPage: 5,
+                nextPage: 4,
+                previousPage: 2,
+                totalItems: undefined,
+                type: 1
             }
-        })
+        }
 
-        expect(JSON.stringify(result)).toEqual(expected)
+        expect(result).toEqual(expected)
     })
 })