entityManager.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. import ApiRequestService from "./apiRequestService";
  2. import {AssociativeArray} from "./data";
  3. import {Repository, useRepo} from "pinia-orm";
  4. import UrlBuilder from "~/services/utils/urlBuilder";
  5. import ModelNormalizer from "./serializer/normalizer/modelNormalizer";
  6. import HydraDenormalizer from "./serializer/denormalizer/hydraDenormalizer";
  7. import ApiModel from "~/models/ApiModel";
  8. import {useProfileAccessStore} from "~/store/profile/access";
  9. import ApiResource from "~/models/ApiResource";
  10. import {MyProfile} from "~/models/Access/MyProfile";
  11. import { v4 as uuid4 } from 'uuid';
  12. import {useOrmStore} from "~/store/orm";
  13. /**
  14. * Entity manager: make operations on the models defined with the Pinia-Orm library
  15. *
  16. * @see https://pinia-orm.codedredd.de/
  17. */
  18. class EntityManager {
  19. private apiRequestService: ApiRequestService;
  20. public constructor(apiRequestService: ApiRequestService) {
  21. this.apiRequestService = apiRequestService
  22. }
  23. /**
  24. * Return the repository for the model
  25. *
  26. * @param model
  27. */
  28. public getRepository(model: typeof ApiResource): Repository<ApiResource> {
  29. return useRepo(model)
  30. }
  31. /**
  32. * Create a new instance of the given model
  33. *
  34. * @param model
  35. * @param properties
  36. */
  37. public new(model: typeof ApiResource, properties: object = {}) {
  38. const repository = this.getRepository(model)
  39. const entity = repository.make(properties)
  40. // @ts-ignore
  41. if (!properties.hasOwnProperty('id') || !properties.id) {
  42. // Object has no id yet, we give him a temporary one
  43. entity.id = 'tmp' + uuid4()
  44. }
  45. repository.save(entity)
  46. EntityManager.saveInitialState(model, entity)
  47. return entity
  48. }
  49. private update(entity: ApiResource, newEntity: ApiResource) {
  50. // On met à jour l'entité par référence, pour maintenir la réactivité lorsque l'entité est réactive
  51. // @see http://underscorejs.org/#extend
  52. useExtend(entity, newEntity)
  53. }
  54. /**
  55. * Fetch one Entity / ApiResource by its id, save it to the store and returns it
  56. *
  57. * @param model Model of the object to fetch
  58. * @param id Id of the object to fetch
  59. * @param forceRefresh Force a new get request to the api ;
  60. * current object in store will be overwritten if it exists
  61. */
  62. public async fetch(model: typeof ApiResource, id: number, forceRefresh: boolean = false): Promise<ApiResource> {
  63. const repository = this.getRepository(model)
  64. // If the entity is already in the store and forceRefresh is false, return the object in store
  65. if (!forceRefresh) {
  66. const item = repository.find(id)
  67. if (item && typeof item !== 'undefined') {
  68. return item
  69. }
  70. }
  71. // Else, get the object from the API
  72. const url = UrlBuilder.join('api', model.entity, String(id))
  73. const response = await this.apiRequestService.get(url)
  74. // deserialize the response
  75. const attributes = HydraDenormalizer.denormalize(response).data as object
  76. return this.new(model, attributes)
  77. }
  78. public findBy(model: typeof ApiResource, query: AssociativeArray) {
  79. // TODO: implement
  80. }
  81. public fetchAll(model: typeof ApiResource) {
  82. // TODO: implement
  83. }
  84. /**
  85. * Persist the entity as it is in the store into the data source via the API
  86. *
  87. * @param model
  88. * @param entity
  89. */
  90. public async persist(model: typeof ApiModel, entity: ApiModel) {
  91. const repository = this.getRepository(model)
  92. let url = UrlBuilder.join('api', model.entity)
  93. let response
  94. const data = ModelNormalizer.normalize(entity)
  95. if (!entity.isNew()) {
  96. url = UrlBuilder.join(url, String(entity.id))
  97. response = await this.apiRequestService.put(url, data)
  98. } else {
  99. delete data.id
  100. response = await this.apiRequestService.post(url, data)
  101. }
  102. const hydraResponse = await HydraDenormalizer.denormalize(response)
  103. const returnedEntity = this.new(model, hydraResponse.data)
  104. EntityManager.saveInitialState(model, returnedEntity)
  105. // Save data into the store
  106. repository.save(returnedEntity)
  107. if (['accesses', 'organizations', 'parameters', 'subdomains'].includes(model.entity)) {
  108. await this.refreshProfile()
  109. }
  110. this.update(entity, returnedEntity)
  111. }
  112. /**
  113. * Delete the entity from the datasource via the API
  114. *
  115. * @param model
  116. * @param entity
  117. */
  118. public async delete(model: typeof ApiModel, entity: ApiResource) {
  119. const repository = this.getRepository(model)
  120. // If object has been persisted to the datasource, send a delete request
  121. if (!entity.isNew()) {
  122. const url = UrlBuilder.join('api', model.entity, String(entity.id))
  123. await this.apiRequestService.delete(url)
  124. }
  125. // update the store
  126. repository.destroy(entity.id)
  127. }
  128. /**
  129. * Reset the entity to its initial state (i.e. the state it had when it was fetched from the API)
  130. *
  131. * @param model
  132. * @param entity
  133. */
  134. public reset(model: typeof ApiResource, entity: ApiResource) {
  135. const initialEntity = EntityManager.getInitialStateOf(model, entity.id)
  136. if (initialEntity === null) {
  137. throw new Error('no initial state recorded for this object - abort [' + model.entity + '/' + entity.id + ']')
  138. }
  139. EntityManager.saveInitialState(model, initialEntity)
  140. const repository = this.getRepository(model)
  141. repository.save(initialEntity)
  142. this.update(entity, initialEntity)
  143. }
  144. /**
  145. * Re-fetch the user profile and update the store
  146. */
  147. public async refreshProfile() {
  148. const response = await this.apiRequestService.get('api/my_profile')
  149. // deserialize the response
  150. const hydraResponse = await HydraDenormalizer.denormalize(response)
  151. const profile = this.new(MyProfile, hydraResponse.data)
  152. const profileAccessStore = useProfileAccessStore()
  153. profileAccessStore.setProfile(profile)
  154. }
  155. /**
  156. * Delete all records in the repository of the model
  157. *
  158. * @param model
  159. */
  160. public async flush(model: typeof ApiModel) {
  161. const repository = this.getRepository(model)
  162. repository.flush()
  163. }
  164. /**
  165. * Is the entity a new one, or does it already exist in the data source (=API)
  166. *
  167. * @param model
  168. * @param id
  169. */
  170. public isNewEntity(model: typeof ApiModel, id: number | string): boolean {
  171. const repository = this.getRepository(model)
  172. const item = repository.find(id)
  173. if (!item || typeof item === 'undefined') {
  174. console.error(model.entity + '/' + id, ' does not exist!')
  175. return false
  176. }
  177. return item.isNew()
  178. }
  179. /**
  180. * Save the state of the entity in the store, so this state could be be restored later
  181. *
  182. * @param model
  183. * @param entity
  184. * @private
  185. */
  186. private static saveInitialState(model: typeof ApiResource, entity: ApiResource) {
  187. useOrmStore().storeInitialValue(model, entity)
  188. }
  189. /**
  190. * Return the saved state of the entity from the store
  191. *
  192. * @param model
  193. * @param id
  194. * @private
  195. */
  196. private static getInitialStateOf(model: typeof ApiResource, id: number): ApiResource | null {
  197. const ormStore = useOrmStore()
  198. //@ts-ignore
  199. if (!ormStore.initialValues.has(model.entity) || !ormStore.initialValues.get(model.entity).has(id)) {
  200. return null
  201. }
  202. //@ts-ignore
  203. return ormStore.initialValues.get(model.entity).get(id)
  204. }
  205. }
  206. export default EntityManager