| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- import * as _ from 'lodash-es'
- import type { AnyJson, ApiResponse, HydraMetadata } from '~/types/data'
- import UrlUtils from '~/services/utils/urlUtils'
- import { METADATA_TYPE } from '~/types/enum/data'
- import models from '~/models/models'
- import type ApiResource from '~/models/ApiResource'
- /**
- * Normalisation et dé-normalisation du format de données Hydra
- */
- class HydraNormalizer {
- static models = models
- /**
- * Normalize the given entity into a Hydra formatted content.
- * @param entity
- */
- public static normalizeEntity(entity: ApiResource): AnyJson {
- const iriEncodedFields =
- Object.getPrototypeOf(entity).constructor.getIriEncodedFields()
- for (const field in iriEncodedFields) {
- const value = entity[field]
- const targetEntity = iriEncodedFields[field].entity
- if (_.isArray(value)) {
- entity[field] = value.map((id: number) => {
- return UrlUtils.makeIRI(targetEntity, id)
- })
- } else {
- entity[field] =
- value !== null ? UrlUtils.makeIRI(targetEntity, value) : null
- }
- }
- return entity.$toJson()
- }
- /**
- * Parse une réponse Hydra et retourne un objet ApiResponse
- *
- * @param {AnyJson} data
- * @param model
- * @return {AnyJson} réponse parsée
- */
- public static denormalize(
- data: AnyJson,
- model?: typeof ApiResource,
- ): ApiResponse {
- return {
- data: HydraNormalizer.getData(data, model),
- metadata: HydraNormalizer.getMetadata(data),
- }
- }
- protected static getData(
- hydraData: AnyJson,
- model?: typeof ApiResource,
- ): AnyJson {
- if (hydraData['@type'] === 'Collection') {
- const members = hydraData.member
- return members.map((item: AnyJson) =>
- HydraNormalizer.denormalizeItem(item, model),
- )
- } else {
- return HydraNormalizer.denormalizeItem(hydraData, model)
- }
- }
- /**
- * Génère les métadonnées d'un item ou d'une collection
- *
- * @param data
- * @protected
- */
- protected static getMetadata(data: AnyJson): HydraMetadata {
- if (data['@type'] !== 'hydra:Collection') {
- // A single item, no metadata
- return { type: METADATA_TYPE.ITEM }
- }
- const metadata: HydraMetadata = {
- totalItems: data['hydra:totalItems'],
- }
- if (data['hydra:view']) {
- /**
- * Extract the page number from the IRIs in the hydra:view section
- */
- const extractPageNumber = (
- pos: string,
- default_: number | undefined = undefined,
- ): number | undefined => {
- const iri = data['hydra:view']['hydra:' + pos]
- if (!iri) {
- return default_
- }
- return UrlUtils.getParameter(
- data['hydra:view']['hydra:' + pos],
- 'page',
- default_,
- ) as number | undefined
- }
- // TODO: utile d'ajouter la page en cours?
- metadata.firstPage = extractPageNumber('first', 1)
- metadata.lastPage = extractPageNumber('last', 1)
- metadata.nextPage = extractPageNumber('next')
- metadata.previousPage = extractPageNumber('previous')
- }
- metadata.type = METADATA_TYPE.COLLECTION
- 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 (!Object.prototype.hasOwnProperty.call(item, '@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 &&
- Object.prototype.hasOwnProperty.call(HydraNormalizer.models, entity)
- ) {
- model = HydraNormalizer.models[entity]
- return HydraNormalizer.denormalizeEntity(model, item)
- }
- throw new Error(
- 'De-normalization error : Could not determine the type of the entity ' +
- item['@id'] +
- ' (found: ' +
- entity +
- ')',
- )
- }
- protected static denormalizeEntity(model: typeof ApiResource, item: AnyJson) {
- item.id = this.getItemIdValue(model, item)
-
- const instance = new model(item)
- const iriEncodedFields = model.getIriEncodedFields()
- for (const field in iriEncodedFields) {
- const value = instance[field]
- if (_.isEmpty(value)) {
- continue
- }
- 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 parseEntityIRI(iri: string) {
- const rx = /\/api\/(\w+)\/(\d+)/
- const match = rx.exec(iri)
- if (!match) {
- throw new Error('could not parse the IRI : ' + JSON.stringify(iri))
- }
- return {
- entity: match[1],
- id: parseInt(match[2]),
- }
- }
- /**
- * Retrieve the entitie's id from the given IRI
- * Throws an error if the IRI does not match the expected entity
- *
- * @param iri
- * @param expectedEntity
- * @protected
- */
- protected static getIdFromEntityIri(
- iri: string,
- expectedEntity: string,
- ): number | string {
- const { entity, id } = HydraNormalizer.parseEntityIRI(iri)
- if (entity !== expectedEntity) {
- throw new Error(
- "IRI entity does not match the field's target entity (" +
- entity +
- ' != ' +
- expectedEntity +
- ')',
- )
- }
- return id
- }
- /**
- * Get the id value of an item
- * @param model
- * @param item
- * @protected
- */
- protected static getItemIdValue(model: typeof ApiResource, item: AnyJson) {
- if (item.id !== undefined) {
- return item.id
- }
- if (
- model.getIdField() !== undefined &&
- item[model.getIdField()] !== undefined
- ) {
- return item[model.getIdField()]
- }
- throw new Error('Missing id field or @IdField decorator for ' + model)
- }
- }
- export default HydraNormalizer
|