@publidata/utils-fontawesome
Version:
Collection of methods to handle opening hours
262 lines (237 loc) • 6.71 kB
JavaScript
const axios = require("axios");
const MemoryCache = require("../MemoryCache");
const { GraphQLClient, gql } = require("graphql-request");
const { FA_API_BASE_URL } = require("../constants");
/**
* Class used to interact with the FontAwesome API
* @class
* @see https://fontawesome.com/docs/apis/graphql/get-started
* @constructor {string} _faToken Token used to generate the access token
*/
class FontAwesomeApi {
/**
* Token used to generate the access token
* Should never be modified after the constructor
* @type {string}
* @private
*/
#faToken;
constructor(_faToken) {
if (typeof _faToken !== "string") {
throw new Error("Invalid token provided");
}
this.#faToken = _faToken;
// From here _faToken should never be use again,
// this.#faToken should be used instead
}
/**
* Instantiate a new GraphQLClient with the endpoint and the headers
* Usage:
* const query = gql`...`;
* const data = await this._client.request(query);
* @type {GraphQLClient}
* @private
*/
_client = new GraphQLClient(FA_API_BASE_URL);
/**
* Cache used to store the results of the queries
* @type {MemoryCache}
* @private
* @see MemoryCache
*/
_cache = new MemoryCache(500);
/**
* Allowing accessing the cache from outside the class
* @returns {MemoryCache}
* @see MemoryCache
*/
get cache() {
return { get: this._cache.get };
}
/**
* Retrieve the access token from the FA API and set the headers of the client
* @returns {Promise<void>}
* @throws {Error} if no token is provided
* @throws {Error} if the token is invalid
* @private
*/
_connect() {
return new Promise((resolve, reject) => {
if (this.#faToken) {
axios
.post(
`${FA_API_BASE_URL}/token`,
{},
{
headers: {
Authorization: `Bearer ${this.#faToken}`,
},
}
)
.then((res) => {
if (res.status === 200) {
this._client.setHeaders({
Authorization: `Bearer ${res.data?.access_token}`,
});
resolve();
} else {
reject(Error("Invalid token provided"));
}
})
.catch((err) => {
reject(err);
});
} else {
reject(Error("No token provided"));
}
});
}
/**
* Execute a query and retry if the token is expired
* @param {String} query - GraphQL query
* @returns {Promise<*>}
* @throws {Error} if the second attempt fails
* @private
*/
_queryWithToken(query) {
// 1 - First attempt
return new Promise((resolve, reject) => {
this._client
.request(query)
.then(resolve)
.catch(() => {
// 2 - Second attempt
this._connect()
.then(() => {
this._client.request(query).then(resolve).catch(reject);
})
.catch(reject);
});
});
}
/**
* Récupère une icône standard de FontAwesome via l'API GraphQL
* @param {String} iconName - Nom de l'icône (ex: "wine-bottle")
* @param {String} prefix - Préfixe de l'icône (ex: "fas", "far", "fab")
* @returns {Promise<Object>} - Objet contenant les infos et le SVG de l'icône
*/
getIcon(iconName, prefix = "fas") {
const styles = {
fas: "SOLID",
far: "REGULAR",
fab: "BRAND",
};
const query = gql`
{
release(version: "6.x") {
icon(name: "${iconName}") {
id
svgs(
filter: {
familyStyles: [
{ family: CLASSIC, style: ${styles[prefix]} }
]
}
) {
html
}
}
}
}
`;
return this._queryWithToken(query).then((res) => {
return res.release?.icon?.svgs?.[0];
});
}
/**
* Retrieve all the kits related to the account
* @returns {Promise<*>}
* @throws {Error} if no token is provided
*/
kits() {
const query = gql`
{
me {
kits {
name
token
version
licenseSelected
technologySelected
}
}
}
`;
return this._queryWithToken(query);
}
/**
* Retrieve a kit from the account including all the icons
* @param {String} token - Token of the kit to retrieve
* @returns {Promise<Object>} { me: { kit: { name, token, version, licenseSelected, technologySelected, iconUploads: [ { name, path, width, height, version, unicode } ] } }
* @throws {Error} if no token is provided
*/
kit(token) {
if (!token) throw new Error("No token provided to .kit($token)");
const query = gql`
{
me {
kit(token: "${token}") {
name
token
version
licenseSelected
technologySelected
iconUploads {
name
path
width
height
version
unicode
}
}
}
}`;
return this._queryWithToken(query);
}
/**
* Retrieve all the icons of a kit and filter them by name
* @param {String} token - Token of the kit to retrieve
* @param {String} icons - Name of the icon to retrieve
* @returns {Promise<Object>} [ { name, path, width, height, version, unicode } ]
* @throws {Error} if no token is provided
*/
kitIcons(token, icons) {
if (!token)
throw new Error("No token provided to .kitIcons($token, $[icons])");
const keyCache = `kitIcons-${token}-${icons?.join("-")}`;
return new Promise((resolve, reject) => {
// Check if this query is already in the cache
const cached = this._cache.get(keyCache);
if (cached) return resolve(cached);
// If not check if the requested icons are in the cache
if (icons) {
const cachedIcons = icons.map((icon) => this._cache.get(icon));
if (cachedIcons.every(Boolean)) {
return resolve(cachedIcons);
}
}
this.kit(token)
.then((res) => {
if (icons)
res.me.kit.iconUploads = res.me.kit.iconUploads.filter((icon) =>
icons.includes(icon.name)
);
const toCache = res.me.kit.iconUploads.map((icon) => ({
key: `${icon.name}`,
value: icon,
}));
this._cache.put(keyCache, res.me.kit.iconUploads);
this._cache.putAll(toCache);
resolve(res.me.kit.iconUploads);
})
.catch(reject);
});
}
}
module.exports = FontAwesomeApi;