Source: services/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 {Map} 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
	}
}