Source: servicios/rest.js

/**
  Servicio REST.
  Servicio para interfaz RESTful.
  Ref: https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
  Ref: https://restfulapi.net/
  Ref: https://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api
  O callback o devolvemos una promesa (mejor, así se puede controlar el error en local)
**/

export class Rest {
  static #URL = 'php/api/index.php'
  static #autorizacion = null

  /**
    Establece la autorización para las llamadas al servidor.
    La autorización se envía en la cabecera HTTP Authorization.
  **/
  static setAutorizacion (autorizacion) {
    Rest.#autorizacion = autorizacion
  }

  /**
    Realiza una llamada AJAX por GET
    @param path {String} Path del recurso solicitado.
    @param pathParams {Array} Parámetros de path que se añadirán a la llamada.
    @param queryParams {{h}} Mapa de parámetros que se añadirán después del path.
    @return {Promise} Devuelve una promesa.
  **/
  static get (path, pathParams = [], queryParams) {
    const opciones = {
      method: 'GET',
      headers: Rest._getHeaders()
    }

    return fetch(Rest._construirURL(path, pathParams, queryParams), opciones) // Hacemos la petición
      .then(respuesta => {
        // Control de Errores
        if (!respuesta.ok) throw Error(`${respuesta.status} - ${respuesta.statusText}`)
        // Comprobamos si es JSON válido
        const tipo = respuesta.headers.get('content-type')
        if (tipo && tipo.indexOf('application/json') !== -1) { return respuesta.json() }
        // No es json
        return respuesta.text()
      })
  }

  /**
    Realiza una llamada AJAX por POST
    @param path {String} Path del recurso solicitado.
    @param pathParams {Array} Parámetros de path que se añadirán a la llamada.
    @param requestBody {Object} Objeto que se pasa como parámetro en el body de la llamada.
    @param json {Boolean} Normalmente, la llamada POST devolverá un texto con la URL del nuevo recurso creado. Opcionalmente, especificando el parámetro json a true se obtiene el resultado en formato JSON.
    @return {Promise} Devuelve una promesa.
  **/
  static post (path, pathParams = [], requestBody = null, json = false) {
    const opciones = {
      method: 'POST',
      headers: Rest._getHeaders(),
      body: JSON.stringify(requestBody)
    }
    // Construimos la petición
    return fetch(Rest._construirURL(path, pathParams), opciones) // Hacemos la petición
      .then(respuesta => {
        // Control de Errores
        if (!respuesta.ok) { throw Error(`${respuesta.status} - ${respuesta.statusText}`) }
        if (json) return respuesta.json() // Si fuera json.
        // La respuesta es un texto con la URL del recurso creado.
        else return respuesta.text()
      })
  }

  /**
    Realiza una llamada AJAX por DELETE
    @param path {String} Path del recurso solicitado.
    @param pathParams {Array} Parámetros de path que se añadirán a la llamada.
    @return {Promise} Devuelve una promesa.
  **/
  static delete (path, pathParams) {
    const opciones = {
      method: 'DELETE',
      headers: Rest._getHeaders()
    }
    // Construimos la petición
    return fetch(Rest._construirURL(path, pathParams), opciones) // Hacemos la petición
      .then(respuesta => {
        // Control de Errores
        if (!respuesta.ok) throw Error(`${respuesta.status} - ${respuesta.statusText}`)

        return true
      })
  }

  /**
    Realiza una llamada AJAX por PUT
    @param path {String} Nombre del recurso solicitado.
    @param pathParams {Array} Parámetros de path que se añadirán a la llamada.
    @param requestBody {Object} Objeto que se pasa como parámetro en la llamada.
    @param json {Boolean} Normalmente, la llamada POST devolverá un texto con la URL del nuevo recurso creado. Opcionalmente, especificando el parámetro json a true se obtiene el resultado en formato JSON.
    @return {Promise} Devuelve una promesa.
  **/
  static put (path, pathParams = [], requestBody = null, json = false) {
    const opciones = {
      method: 'PUT',
      headers: Rest._getHeaders(),
      body: JSON.stringify(requestBody)
    }
    // Construimos la petición
    return fetch(Rest._construirURL(path, pathParams), opciones) // Hacemos la petición
      .then(respuesta => {
        // Control de Errores
        if (!respuesta.ok) { throw Error(`${respuesta.status} - ${respuesta.statusText}`) }

        if (json) return respuesta.json() // Si fuera json.
        // La respuesta es un texto con la URL del recurso creado.
        else return respuesta.text()
      })
  }

  // Métodos internos no documentado.
  static _getHeaders () {
    return {
      Accept: 'application/json',
      'Content-Type': 'application/json',
      Authorization2: Rest.#autorizacion
    }
  }

  static _construirURL (path, pathParams = [], queryParams) {
    let url = `${Rest.#URL}/${path}/${pathParams.join('/')}`
    // TODO: Procesar el mapa de queryParams para generar el query string ?nombre1=valor1&nombre2=valor2...
    if (queryParams) {
      url += '?'
      queryParams.forEach((valor, clave, mapa) => {
        url += `${clave}=${valor}&`
      })
      url = url.substring(0, url.length - 1)
    }
    url = encodeURI(url.replace('//', '/null/')) // aseguramos los parámetros nulos.
    return url
  }
}