@foxy.io/sdk
Version:
Universal SDK for a full server-side and a limited in-browser access to Foxy hAPI.
124 lines (123 loc) • 6.59 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import * as Core from '../core/index.js';
import { Headers, Request, fetch } from 'cross-fetch';
import { storageV8N, v8n } from '../core/v8n.js';
import MemoryStorage from 'fake-storage';
/** JS SDK for building backends with [Foxy Hypermedia API](https://api.foxy.io/docs). Hypermedia API is designed to give you complete control over all aspects of your Foxy accounts, whether working with a single store or automating the provisioning of thousands. Anything you can do within the Foxy administration, you can also do through the API. This means that you can embed Foxy into any application (CMS, LMS, CRM, etc.) and expose as much or as little of Foxy's functionality as desired. */
export class API extends Core.API {
constructor(params) {
var _a, _b, _c, _d;
API.v8n.classConstructor.check(params);
super({
base: (_a = params.base) !== null && _a !== void 0 ? _a : API.BASE_URL,
cache: (_b = params.cache) !== null && _b !== void 0 ? _b : new MemoryStorage(),
fetch: (...args) => this.__fetch(...args),
level: params.level,
storage: (_c = params.storage) !== null && _c !== void 0 ? _c : new MemoryStorage(),
});
this.refreshToken = params.refreshToken;
this.clientSecret = params.clientSecret;
this.clientId = params.clientId;
this.version = (_d = params.version) !== null && _d !== void 0 ? _d : API.VERSION;
}
static getToken(opts, throwOnFailure = false) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
API.v8n.getAccessToken.check(opts);
const headers = new Headers();
const body = new URLSearchParams();
const url = new URL('token', (_a = opts.base) !== null && _a !== void 0 ? _a : API.BASE_URL).toString();
headers.set('FOXY-API-VERSION', (_b = opts.version) !== null && _b !== void 0 ? _b : API.VERSION);
headers.set('Content-Type', 'application/x-www-form-urlencoded');
body.set('client_id', opts.clientId);
body.set('client_secret', opts.clientSecret);
if ('code' in opts) {
body.set('code', opts.code);
body.set('grant_type', 'authorization_code');
}
else {
body.set('grant_type', 'refresh_token');
body.set('refresh_token', opts.refreshToken);
}
const response = yield fetch(url, { body, headers, method: 'POST' });
if (response.ok)
return response.json();
if (throwOnFailure)
throw new Error(yield response.text());
return null;
});
}
__fetch(input, init) {
var _a, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
let token = JSON.parse((_a = this.storage.getItem(API.ACCESS_TOKEN)) !== null && _a !== void 0 ? _a : 'null');
const request = new Request(input, init);
if (token !== null) {
const expiresAt = new Date(token.date_created).getTime() + token.expires_in * 1000;
const refreshAt = Date.now() + API.REFRESH_THRESHOLD;
if (expiresAt < refreshAt) {
this.storage.removeItem(API.ACCESS_TOKEN);
this.console.info('Removed old access token from the storage.');
token = null;
}
}
if (token === null) {
this.console.trace("Access token isn't present in the storage. Fetching a new one...");
const rawToken = yield API.getToken(this, true).catch(err => {
this.console.error(err.message);
return null;
});
if (rawToken) {
token = Object.assign(Object.assign({}, rawToken), { date_created: new Date().toISOString() });
this.storage.setItem(API.ACCESS_TOKEN, JSON.stringify(token));
this.console.info('Access token updated.');
}
else {
this.console.warn('Failed to fetch access token. Proceeding without authentication.');
}
}
const headers = request.headers;
const method = (_c = (_b = init === null || init === void 0 ? void 0 : init.method) === null || _b === void 0 ? void 0 : _b.toUpperCase()) !== null && _c !== void 0 ? _c : 'GET';
if (!headers.get('Authorization') && token)
headers.set('Authorization', `Bearer ${token.access_token}`);
if (!headers.get('Content-Type'))
headers.set('Content-Type', 'application/json');
if (!headers.get('FOXY-API-VERSION'))
headers.set('FOXY-API-VERSION', this.version);
this.console.trace(`${method} ${request.url}`);
return fetch(request);
});
}
}
API.REFRESH_THRESHOLD = 5 * 60 * 1000;
API.ACCESS_TOKEN = 'access_token';
API.BASE_URL = new URL('https://api.foxy.io/');
API.VERSION = '1';
API.v8n = {
classConstructor: v8n().schema({
base: v8n().optional(v8n().instanceOf(URL)),
cache: v8n().optional(storageV8N),
clientId: v8n().string(),
clientSecret: v8n().string(),
level: v8n().optional(v8n().integer()),
refreshToken: v8n().string(),
storage: v8n().optional(storageV8N),
version: v8n().optional(v8n().passesAnyOf(v8n().exact('1'))),
}),
getAccessToken: v8n()
.passesAnyOf(v8n().schema({ code: v8n().string() }), v8n().schema({ refreshToken: v8n().string() }))
.schema({
base: v8n().optional(v8n().instanceOf(URL)),
clientId: v8n().string(),
clientSecret: v8n().string(),
version: v8n().optional(v8n().passesAnyOf(v8n().exact('1'))),
}),
};