浏览代码

services cleaning and refactoring

Olivier Massot 3 年之前
父节点
当前提交
692465c8f7

+ 3 - 3
composables/data/useAp2iRequestService.ts

@@ -1,6 +1,6 @@
 import {useProfileAccessStore} from "~/store/profile/access";
 import {FetchContext, FetchOptions} from "ohmyfetch";
-import Page from "~/services/store/page";
+import PageStore from "~/services/store/pageStoreHelper";
 import {TYPE_ALERT} from "~/types/enums";
 import {useAppConfig, useRuntimeConfig} from "#app";
 import OhMyFetchConnector from "~/services/data/connector/ohMyFetchConnector";
@@ -58,12 +58,12 @@ export const useAp2iRequestService = () => {
             console.error('Unauthorized')
         }
         if (response && response.status === 403) {
-            new Page().addAlerts(TYPE_ALERT.ALERT, ['forbidden'])
+            new PageStore().addAlerts(TYPE_ALERT.ALERT, ['forbidden'])
             console.error('forbidden')
         }
 
         if (response && response.status === 500) {
-            new Page().addAlerts(TYPE_ALERT.ALERT, [error ? error.message : response.statusText])
+            new PageStore().addAlerts(TYPE_ALERT.ALERT, [error ? error.message : response.statusText])
             console.error(error ? error.message : response.statusText)
         }
     }

+ 0 - 18
pages/form.vue

@@ -1,18 +0,0 @@
-<template>
-  <main>
-    <p>Test : {{ organization.name }}</p>
-
-    <nuxt-link to="/">Goto main</nuxt-link>
-  </main>
-</template>
-
-<script setup lang="ts">
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import {Organization} from "~/models/Organization/Organization";
-
-const em = useEntityManager()
-
-const organization = ref(null)
-organization.value = await em.fetch(Organization, 32366)
-
-</script>

+ 4 - 2
pages/poc/index.vue

@@ -5,7 +5,7 @@
         <nuxt-link :to="'/poc/' + file.id" class="mr-3">{{ file.name }}</nuxt-link>
       </li>
     </ul>
-
+    <div class="ma-3">{{ totalItems }} results</div>
     <div class="ma-3">
       <button @click="goToPreviousPage" class="mr-3">Previous page ({{ previousPage }})</button>
       <span class="mx-3">Page : {{ page }}</span>
@@ -13,7 +13,7 @@
       <span class="mx-2"> (Last page : {{ lastPage }})</span>
     </div>
     <div class="ma-3">
-      <nuxt-link to="/poc/new">New</nuxt-link>
+      <nuxt-link to="/poc/new">Create</nuxt-link>
     </div>
   </main>
 </template>
@@ -29,6 +29,7 @@
   let files: Array<ApiResource> = reactive([])
   const page: Ref<number> = ref(1)
 
+  const totalItems: Ref<number> = ref(0)
   const firstPage: Ref<number> = ref(1)
   const lastPage: Ref<number> = ref(1)
   const previousPage: Ref<number | null> = ref(null)
@@ -40,6 +41,7 @@
     // On met à jour l'array sans la remplacer pour ne pas perdre la réactivité
     em.reactiveUpdate(files, collection.items)
 
+    totalItems.value = collection.totalItems || 0
     firstPage.value = collection.firstPage || 1
     lastPage.value = collection.lastPage || 1
     previousPage.value = collection.previousPage || null

+ 5 - 5
services/data/serializer/denormalizer/hydraDenormalizer.ts

@@ -108,10 +108,10 @@ class HydraDenormalizer {
     }
 
     if(data['hydra:view']){
-      metadata.firstPage = HydraDenormalizer.extractPageFromUri(data['hydra:view']['hydra:first'], 1) as number
-      metadata.lastPage = HydraDenormalizer.extractPageFromUri(data['hydra:view']['hydra:last'], 1) as number
-      metadata.nextPage = HydraDenormalizer.extractPageFromUri(data['hydra:view']['hydra:next']) ?? undefined
-      metadata.previousPage = HydraDenormalizer.extractPageFromUri(data['hydra:view']['hydra:previous']) ?? undefined
+      metadata.firstPage = HydraDenormalizer.getPageFromUri(data['hydra:view']['hydra:first'], 1) as number
+      metadata.lastPage = HydraDenormalizer.getPageFromUri(data['hydra:view']['hydra:last'], 1) as number
+      metadata.nextPage = HydraDenormalizer.getPageFromUri(data['hydra:view']['hydra:next']) ?? undefined
+      metadata.previousPage = HydraDenormalizer.getPageFromUri(data['hydra:view']['hydra:previous']) ?? undefined
     }
 
     metadata.type = METADATA_TYPE.COLLECTION
@@ -133,7 +133,7 @@ class HydraDenormalizer {
     }
   }
 
-  private static extractPageFromUri(uri: string, default_: number | null = null): number | null {
+  private static getPageFromUri(uri: string, default_: number | null = null): number | null {
     const url = 'https://foo' + uri // Pour que l'uri partielle soit parsée, on doit y ajouter une url de base bidon
     const page = Url.getParameter(url, 'page')
     return page ? parseInt(page) : default_

+ 5 - 5
services/profile/accessProfile.ts

@@ -1,7 +1,7 @@
-import {AbilitiesType, accessState, AnyJson} from "~/types/interfaces";
-import {Ability} from "@casl/ability";
-import {Pinia} from "pinia";
+import {AbilitiesType, accessState} from "~/types/interfaces";
 import {useProfileAccessStore} from "~/store/profile/access";
+import {MongoAbility} from "@casl/ability/dist/types/Ability";
+import {AnyJson} from "~/types/data";
 
 /**
  * L'AccessProfile permet de manipuler l'AccessState l'Access qui peuvent
@@ -11,7 +11,7 @@ import {useProfileAccessStore} from "~/store/profile/access";
 export class AccessProfile {
   private accessProfile!: accessState
 
-  private $ability:Ability = {} as Ability
+  private $ability: MongoAbility = {} as MongoAbility
 
   /**
    * Set le store
@@ -24,7 +24,7 @@ export class AccessProfile {
    * Permet de setter le service d'abilités
    * @param ability
    */
-  public setAbility(ability: Ability){
+  public setAbility(ability: MongoAbility){
     this.$ability = ability
   }
 

+ 2 - 1
services/profile/organizationProfile.ts

@@ -1,5 +1,6 @@
-import { AnyJson, organizationState } from '~/types/interfaces'
+import { organizationState } from '~/types/interfaces'
 import {useProfileOrganizationStore} from "~/store/profile/organization";
+import {AnyJson} from "~/types/data";
 /**
  * L'OrganizationProfile permet de manipuler l'OrganizationState auquel
  * l'access courant est connecté et qui peuvent  être nécessaires pour l'affichage

+ 8 - 8
services/rights/abilitiesUtils.ts

@@ -1,24 +1,24 @@
 import {$accessProfile} from '~/services/profile/accessProfile'
 import {$organizationProfile} from '~/services/profile/organizationProfile'
-import {Ability} from '@casl/ability'
 import {$roleUtils} from '~/services/rights/roleUtils'
-import {AbilitiesType, AnyJson} from '~/types/interfaces'
-import {DENORMALIZER_TYPE} from '~/types/enums'
+import {AbilitiesType} from '~/types/interfaces'
 import {useProfileAccessStore} from "~/store/profile/access";
 import {useProfileOrganizationStore} from "~/store/profile/organization";
 import YamlDenormalizer from "~/services/data/serializer/denormalizer/yamlDenormalizer";
+import {MongoAbility} from "@casl/ability/dist/types/Ability";
+import {AnyJson} from "~/types/data";
 
 /**
  * Classe permettant de mener des opérations sur les habilités
  */
 class AbilitiesUtils {
-    private readonly $ability: Ability = {} as Ability
+    private readonly $ability: MongoAbility = {} as MongoAbility
     private factory: AnyJson = {}
 
     /**
      * @constructor
      */
-    constructor(ability: Ability) {
+    constructor(ability: MongoAbility) {
         this.$ability = ability
     }
 
@@ -122,7 +122,7 @@ class AbilitiesUtils {
             const abilitiesAvailable = doc.abilities
             const abilitiesFiltered = this.abilitiesAvailableFilter(abilitiesAvailable)
             abilitiesByConfig = this.transformAbilitiesConfigToAbility(abilitiesFiltered)
-        } catch (e) {
+        } catch (e: any) {
             throw new Error(e.message)
         }
         return abilitiesByConfig
@@ -162,8 +162,8 @@ class AbilitiesUtils {
     /**
      * Parcourt les fonctions par services et établit si oui ou non l'habilité est autorisée
      *
-     * @param {AnyJson} functionByServices
      * @return {boolean}
+     * @param functionByservices
      */
     canHaveTheAbility(functionByservices: AnyJson) {
         let hasAbility: boolean = true;
@@ -186,4 +186,4 @@ class AbilitiesUtils {
     }
 }
 
-export const $abilitiesUtils = (ability: Ability) => new AbilitiesUtils(ability)
+export const $abilitiesUtils = (ability: MongoAbility) => new AbilitiesUtils(ability)

+ 3 - 1
services/rights/roleUtils.ts

@@ -1,5 +1,7 @@
-import { AbilitiesType, AnyJson } from '~/types/interfaces'
+import { AbilitiesType } from '~/types/interfaces'
+import {AnyJson} from "~/types/data";
 
+// TODO: peut-être passer ces constantes dans la config?
 const rolesByFunction: Array<string> = [
   'ROLE_SUPER_ADMIN',
   'ROLE_ADMIN',

+ 2 - 3
services/store/form.ts → services/store/formStoreHelper.ts

@@ -1,15 +1,14 @@
 import {FORM_STATUS} from "~/types/enums";
-// import {Route} from "vue-router";
 import {useFormStore} from "~/store/form";
 
-export default class FormStorage {
+export default class FormStoreHelper {
 
   /**
    * Actions devant être gérées si on souhaite quitter une page
    * @param to
    */
-  // handleActionsAfterLeavePage(to: Route){
   handleActionsAfterLeavePage(to: any){
+    // TODO: pourquoi ces méthodes sont ici et pas dans les actions du store lui même?
     const formStore = useFormStore()
     if (formStore.dirty) {
       formStore.showConfirmToLeave = true

+ 1 - 1
services/store/page.ts → services/store/pageStoreHelper.ts

@@ -2,7 +2,7 @@ import {TYPE_ALERT} from "~/types/enums";
 import {Alert} from "~/types/interfaces";
 import {usePageStore} from "~/store/page";
 
-export default class Page {
+export default class PageStoreHelper {
   /**
    * Ajout des alerts dans le store
    * @param type

+ 0 - 76
services/store/query.ts

@@ -1,76 +0,0 @@
-import { Query as PiniaQuery, Collection, Item } from 'pinia-orm'
-import { $objectProperties } from '~/services/utils/objectProperties'
-import { AnyJson } from '~/types/interfaces'
-import {OrderByVuexOrm} from "~/types/types";
-
-/**
- * Classe Wrapper pour assurer les opérations les plus communes des Query de VuexORM
- */
-class Query {
-  /**
-   * Récupération de l'Item souhaité
-   *
-   * @param {VuexQuery} query
-   * @param {number|string} id
-   * @return {Item} l'Item
-   */
-  public getItem (query: PiniaQuery, id: number|string): Item {
-    const item = query.find(id)
-    if (!item || typeof item === 'undefined') { throw new Error('item not found') }
-    return item
-  }
-
-  /**
-   * Récupération du premier item
-   *
-   * @param {VuexQuery} query
-   * @return {Item} l'Item
-   */
-  public getFirstItem (query: PiniaQuery): Item {
-    const item = query.first()
-    if (!item || typeof item === 'undefined') { throw new Error('item not found') }
-    return item
-  }
-
-  /**
-   * Récupération la collection de la Query
-   *
-   * @param {VuexQuery} query
-   * @return {Collection} Array d'Item
-   */
-  public getCollection (query: PiniaQuery, orderBy?: OrderByVuexOrm): Collection {
-    if(orderBy){
-      for(const orderKey in orderBy){
-        query.orderBy(orderKey, orderBy[orderKey])
-      }
-    }
-
-    return query.get()
-  }
-
-  /**
-   * Récupération de l'Item souhaité puis transformation en JSON aplati
-   *
-   * @param {VuexQuery} query
-   * @param {number|string} id
-   * @return {AnyJson} réponse
-   */
-  public getFlattenEntry (query: PiniaQuery, id: number|string): AnyJson {
-    return $objectProperties.cloneAndFlatten(this.getItem(query, id) as AnyJson)
-  }
-
-  /**
-   * Récupération la collection de la Query puis transformation en JSON aplati
-   *
-   * @param {VuexQuery} query
-   * @return {Array<AnyJson>} réponse
-   */
-  public getFlattenEntries (query: PiniaQuery): Array<AnyJson> {
-    const entries = this.getCollection(query)
-    return entries.map((entry: AnyJson) => {
-      return $objectProperties.cloneAndFlatten(entry)
-    })
-  }
-}
-
-export const queryHelper = new Query()

+ 0 - 124
services/store/repository.ts

@@ -1,124 +0,0 @@
-import { Collection, Item, Model, Repository as PiniaRepository, useRepo } from 'pinia-orm'
-import { $objectProperties } from '~/services/utils/objectProperties'
-import { AnyJson } from '~/types/interfaces'
-import {OrderByVuexOrm} from "~/types/types";
-
-/**
- * Classe Wrapper pour assurer les opérations les plus communes des Repository de VuexORM
- */
-class Repository {
-
-  /**
-   * Récupération du repository du Model
-   *
-   * @param {Model} model
-   * @return {VuexRepository<Model>} le repository
-   */
-  public getRepository (model: typeof Model): PiniaRepository<Model> {
-    return useRepo(model)
-  }
-
-  public createNewModelInstance (model: typeof Model): Model {
-    return this.getRepository(model).make()
-  }
-
-  /**x
-   * Récupération du nom de l'entité du model
-   *
-   * @param {Model} model
-   * @return {string} l'entité
-   */
-  public getEntity (model: typeof Model): string {
-    return this.getRepository(model).getModel().$entity()
-  }
-
-  /**
-   * Créer une entry dans le repository
-   *
-   * @param {Model} model
-   * @param {AnyJson} entry
-   */
-  public make (model: typeof Model, entry?: AnyJson): Model {
-    return this.getRepository(model).make(entry)
-  }
-
-  /**
-   * Persist l'entry dans le repository
-   *
-   * @param {Model} model
-   * @param {AnyJson} entry
-   */
-  public persist (model: typeof Model, entry: AnyJson): Model {
-    return this.getRepository(model).save(entry)
-  }
-
-  /**
-   * Effectue une mise à jour du store après la modification d'un champ de l'entry
-   *
-   * @param {Model} model
-   * @param {AnyJson} entry
-   * @param {any} value
-   * @param {string} field
-   */
-  public updateStoreFromField (model: typeof Model, entry: AnyJson, value: any, field: string): void {
-    if (!useHas(entry, field)) { throw new Error('field not found') }
-
-    entry[field] = value
-    this.persist(model, $objectProperties.cloneAndNest(entry))
-  }
-
-  /**
-   * Récupération de l'Item du Model souhaité
-   *
-   * @param {Model} model
-   * @param {number|string} id
-   * @return {Item} l'Item
-   */
-  public findItemFromModel (model: typeof Model, id: number|string): Item {
-    const repository = this.getRepository(model)
-    const item = repository.find(id)
-    if (!item || typeof item === 'undefined') { throw new Error('Item not found') }
-
-    return item
-  }
-
-  /**
-   * Récupération de la Collection du Model souhaité
-   *
-   * @param {Model} model
-   * @param {OrderByVuexOrm} orderBy
-   * @return {Collection} la collection
-   */
-  public findCollectionFromModel (model: typeof Model, orderBy?: OrderByVuexOrm): Collection {
-    const repository = this.getRepository(model)
-    if(orderBy){
-      for(const orderKey in orderBy){
-        repository.orderBy(orderKey, orderBy[orderKey])
-      }
-    }
-    return repository.all()
-  }
-
-  /**
-   * Supprime l'Item du repository
-   *
-   * @param {Model} model
-   * @param {number} id
-   */
-  public deleteItem (model: typeof Model, id: number|string) {
-    const repository = this.getRepository(model)
-    repository.destroy(id)
-  }
-
-  /**
-   * Supprime tous les Items du repository
-   *
-   * @param {Model} model
-   */
-  public cleanRepository (model: typeof Model) {
-    const repository = this.getRepository(model)
-    repository.flush()
-  }
-}
-
-export const repositoryHelper = new Repository()

+ 44 - 6
services/utils/i18n.ts

@@ -1,8 +1,46 @@
-import { parsePhoneNumber } from 'libphonenumber-js'
+import {VueI18n} from "vue-i18n";
+import {EnumChoice, EnumChoices} from "~/types/interfaces";
+import {parsePhoneNumber} from "libphonenumber-js";
 
-export default class UseI18N {
-  static formatPhoneNumber (number: string): string {
-    const parsed = parsePhoneNumber(number)
-    return parsed ? parsed.formatNational() : ''
-  }
+
+class I18n {
+    private i18n!: VueI18n
+
+    public constructor(i18n: VueI18n) {
+        this.i18n = i18n
+    }
+
+    /**
+     * Return the given enum with its labels translated
+     *
+     * If sort is true, the enum is also sorted by its newly translated labels
+     *
+     * @param enum_
+     * @param sort
+     */
+    public translateEnum(enum_: EnumChoices, sort: boolean = true): EnumChoices {
+        enum_ = enum_.map(
+            (item: EnumChoice) => {
+                return {value: item.value, label: this.i18n.t(item.value) as string}
+            }
+        )
+
+        if (sort) {
+            enum_ = enum_.sort((a, b) => {
+                if (a.label > b.label) {
+                    return 1
+                }
+                if (a.label < b.label) {
+                    return -1
+                }
+                return 0
+            })
+        }
+        return enum_
+    }
+
+    static formatPhoneNumber (number: string): string {
+        const parsed = parsePhoneNumber(number)
+        return parsed ? parsed.formatNational() : ''
+    }
 }

+ 0 - 40
services/utils/i18nUtils.ts

@@ -1,40 +0,0 @@
-import {VueI18n} from "vue-i18n";
-import {EnumChoice, EnumChoices} from "~/types/interfaces";
-
-
-class I18nUtils {
-    private i18n!: VueI18n
-
-    public constructor(i18n: VueI18n) {
-        this.i18n = i18n
-    }
-
-    /**
-     * Return the given enum with its labels translated
-     *
-     * If sort is true, the enum is also sorted by its newly translated labels
-     *
-     * @param enum_
-     * @param sort
-     */
-    public translateEnum(enum_: EnumChoices, sort: boolean = true): EnumChoices {
-        enum_ = enum_.map(
-            (item: EnumChoice) => {
-                return {value: item.value, label: this.i18n.t(item.value) as string}
-            }
-        )
-
-        if (sort) {
-            enum_ = enum_.sort((a, b) => {
-                if (a.label > b.label) {
-                    return 1
-                }
-                if (a.label < b.label) {
-                    return -1
-                }
-                return 0
-            })
-        }
-        return enum_
-    }
-}

+ 0 - 38
services/utils/modelsUtils.ts

@@ -1,38 +0,0 @@
-import {models} from "~/models/_import";
-
-export default class ModelsUtils {
-  /**
-   * Extrait l'ID de l'URI passée en paramètre
-   * @param uri
-   */
-  static extractIdFromUri (uri: string): number|null {
-    if(!uri) return null
-
-    const partUri: Array<string> = uri.split('/')
-    const id:any = partUri.pop()
-
-    if(isNaN(id))
-      throw new Error('id is not a number')
-
-    return parseInt(id)
-  }
-  /**
-   * Extrait l'ID de l'URI passée en paramètre
-   * @param iri
-   */
-  static getModelFromIri (iri: string): any {
-    const matches = iri.match(/^\/api\/(\w+)\/.*/)
-    if (!matches || !matches[1]) {
-      throw new Error('cannot parse the IRI')
-    }
-    const entityName = matches[1]
-
-    let model = models.find(candidate => { return candidate.entity === entityName })
-    if (!model) {
-      throw new Error('no model found')
-    }
-    return model
-  }
-
-
-}

+ 2 - 2
services/utils/objectProperties.ts

@@ -1,10 +1,10 @@
-import { AnyJson } from '~/types/interfaces'
-
 /**
  * @category Services/utils
  * @class ObjectProperties
  * Classe aidant à manipuler des Objets
  */
+import {AnyJson} from "~/types/data";
+
 class ObjectProperties {
   /**
    * Flatten un objet nested en un objet avec un seul niveau avec des noms de propriétés transformées comme cela 'foo.bar'

+ 0 - 21
services/utils/typesTesting.ts

@@ -1,21 +0,0 @@
-import {DataPersisterArgs, DataProviderArgs} from "~/types/interfaces";
-
-export default class TypesTesting {
-  /**
-   * Test si l'argument est bien de type DataProviderArgs
-   * @param args
-   */
-  public static isDataProviderArgs (args: DataProviderArgs|DataPersisterArgs): args is DataProviderArgs {
-    return (args as DataProviderArgs).imgArgs !== undefined
-        || (args as DataProviderArgs).listArgs !== undefined
-        || (args as DataProviderArgs).fileArgs !== undefined
-  }
-
-  /**
-   * Test si l'argument est bien de type DataPersister
-   * @param args
-   */
-  public static isDataPersisterArgs (args: DataProviderArgs|DataPersisterArgs): args is DataPersisterArgs {
-    return (args as DataPersisterArgs).data !== undefined
-  }
-}

+ 15 - 4
services/utils/url.ts

@@ -1,7 +1,3 @@
-import {FileArgs, ImageArgs, ListArgs, UrlArgs} from '~/types/interfaces'
-import {QUERY_TYPE} from '~/types/enums'
-import TypesTesting from "~/services/utils/typesTesting";
-
 /**
  * Classe permettant de construire une URL pour l'interrogation d'une API externe
  */
@@ -52,6 +48,21 @@ class Url {
     return res ?? default_
   }
 
+  /**
+   * Extrait l'ID de l'URI passée en paramètre
+   * L'uri est supposée être de la forme `.../foo/bar/{id}`, où l'id est un identifiant numérique
+   *
+   * @param uri
+   */
+  public static extractIdFromUri (uri: string): number|null {
+    const partUri: Array<string> = uri.split('/')
+    const id:any = partUri.pop()
+
+    if(isNaN(id))
+      throw new Error('id is not a number')
+
+    return parseInt(id)
+  }
 }
 
 export default Url

+ 1 - 1
store/form.ts

@@ -22,4 +22,4 @@ export const state = () => ({
   dirty: false,
   showConfirmToLeave: false,
   goAfterLeave: null
-})
+})