Source: services/identity-manager-pool.js

import IdentityManager from './identity-manager.js'

/** Gère l'assocation des identity-manager au resource (layer, control, service...)
 * Permet de mutualiser les identity-manager entre plusieurs resources
*/
export default class IdentityManagerPool {
  /**
  * @ type {Map<String, {identityManager: IdentityManager, resources: Array<{resourceType: String, resourceId: String}>}>}
   */
  #identityManager = new Map()

  /**
   *
   * @param {String} resourceType type de resource (layer, control, service...)
   * @param {String} resourceId identifiant de la resource
   * @param {import("./identity-manager.js").identityOptions | String} identityOptions options de gestion d'identité ou token en clair (historique)
   * @returns {IdentityManager}
  */
  async addIdentitySource (resourceType, resourceId, identityOptions) {
    const identityManagerKey = this.#getIdentityManagerKey(identityOptions)
    if (!this.#identityManager.has(identityManagerKey)) {
      // si le identity manager n'existe pas, on le crée
      const identityManager = new IdentityManager(identityOptions)
      this.#identityManager.set(identityManagerKey, {
        identityManager,
        resources: [{ resourceType, resourceId }],
      })
    } else {
      // on ajoute la resource à la liste des resources utilisant ce identity manager
      const identityManagerEntry = this.#identityManager.get(identityManagerKey)
      identityManagerEntry.resources.push({ resourceType, resourceId })
    }
    return this.#identityManager.get(identityManagerKey).identityManager
  }

  /**
   * supprime un identity manager et toutes les resources qui l'utilisent
  * @param {import("./identity-manager.js").identityOptions | String} identityOptions options de gestion du token ou token en clair (historique)
  * @returns {Boolean}
  */
  removeIdentitySource (identityOptions) {
    const identityManagerKey = this.#getIdentityManagerKey(identityOptions)
    return this.#identityManager.delete(identityManagerKey)
  }

  /**
   * Retire une source de la liste des resources utilisant un identity manager, si plus aucune resource n'utilise ce identity manager, on le supprime
   * @param {String} resourceType type de resource (layer, control, service...)
   * @param {String} resourceId identifiant de la resource
   * @param {Boolean} [removeIdentityManagerIfNoResource=false] indique si l'identity manager doit être supprimé si aucune ressource ne l'utilise
   */
  removeResource (resourceType, resourceId, removeIdentityManagerIfNoResource = false) {
    for (const [key, entry] of this.#identityManager.entries()) {
      const index = entry.resources.findIndex(r => r.resourceType === resourceType && r.resourceId === resourceId)
      if (index !== -1) {
        entry.resources.splice(index, 1)
        // si plus aucune resource n'utilise ce identity manager, on le supprime
        if (entry.resources.length === 0 && removeIdentityManagerIfNoResource) {
          this.#identityManager.get(key).identityManager.destroy()
          this.#identityManager.delete(key)
        }
        break
      }
    }
  }

  /**
   * Est-ce que la clé de identity manager existe déjà?
  * @param {import("./identity-manager.js").identityOptions | String} identityOptions options de gestion du d'identité ou token en clair (historique)
  * @returns {Boolean}
  */
  identitySourceExists (identityOptions) {
    const identityManagerKey = this.#getIdentityManagerKey(identityOptions)
    return this.#identityManager.has(identityManagerKey)
  }

  getIdentityManager (identityOptions) {
    const identityManagerKey = this.#getIdentityManagerKey(identityOptions)
    return this.#identityManager.has(identityManagerKey) ? this.#identityManager.get(identityManagerKey).identityManager : null
  }

  /**
   * Stringify stable qui garantit le même résultat indépendamment de l'ordre des propriétés
   * @param {*} obj
   * @returns {String}
   */
  #stableStringify (obj) {
    if (obj === null || typeof obj !== 'object') {
      return JSON.stringify(obj)
    }

    if (Array.isArray(obj)) {
      return '[' + obj.map(item => this.#stableStringify(item)).join(',') + ']'
    }

    const keys = Object.keys(obj).sort()
    const pairs = keys.map(key => {
      return JSON.stringify(key) + ':' + this.#stableStringify(obj[key])
    })

    return '{' + pairs.join(',') + '}'
  }

  /**
  * @param {import("./identity-manager.js").identityOptions | String} identityOptions options de gestion d'identité ou token en clair (historique)
  * @returns {String}
  */
  #getIdentityManagerKey (identityOptions) {
    // génère une clé de gestionnaire d'identity, si c'est une chaine de caractère, on la prend telle quelle
    // Sinon prend la propriété id
    // sinon calcule une clé en fonction de la chaine
    return typeof identityOptions === 'string'
      ? identityOptions
      : identityOptions?.id
        ? identityOptions.id
        : this.#stableStringify(identityOptions)
  }
}