2
0
mirror of https://github.com/iconify/iconify.git synced 2024-09-19 16:59:02 +00:00

Split API queries into icons and custom queries

This commit is contained in:
Vjacheslav Trushkin 2021-08-30 10:22:53 +03:00
parent eebac83a65
commit fbb836d3c0
9 changed files with 195 additions and 65 deletions

View File

@ -4,20 +4,28 @@ import type { GetAPIConfig } from '../api/config';
/**
* Params for sendQuery()
*/
export interface APIQueryParams {
export interface APIIconsQueryParams {
type: 'icons';
provider: string;
prefix: string;
icons: string[];
}
export interface APICustomQueryParams {
type: 'custom';
provider: string;
uri: string;
}
export type APIQueryParams = APIIconsQueryParams | APICustomQueryParams;
/**
* Functions to implement in module
*/
export type IconifyAPIPrepareQuery = (
export type IconifyAPIPrepareIconsQuery = (
provider: string,
prefix: string,
icons: string[]
) => APIQueryParams[];
) => APIIconsQueryParams[];
export type IconifyAPISendQuery = (
host: string,
@ -29,7 +37,7 @@ export type IconifyAPISendQuery = (
* API modules
*/
export interface IconifyAPIModule {
prepare: IconifyAPIPrepareQuery;
prepare: IconifyAPIPrepareIconsQuery;
send: IconifyAPISendQuery;
}

View File

@ -1,10 +1,11 @@
import type { PendingQueryItem } from '@cyberalien/redundancy';
import type {
APIQueryParams,
IconifyAPIPrepareQuery,
IconifyAPIPrepareIconsQuery,
IconifyAPISendQuery,
IconifyAPIModule,
GetIconifyAPIModule,
APIIconsQueryParams,
} from '../modules';
import type { GetAPIConfig } from '../config';
@ -121,12 +122,12 @@ export const getAPIModule: GetIconifyAPIModule = (
/**
* Prepare params
*/
const prepare: IconifyAPIPrepareQuery = (
const prepare: IconifyAPIPrepareIconsQuery = (
provider: string,
prefix: string,
icons: string[]
): APIQueryParams[] => {
const results: APIQueryParams[] = [];
): APIIconsQueryParams[] => {
const results: APIIconsQueryParams[] = [];
// Get maximum icons list length
let maxLength = maxLengthCache[prefix];
@ -135,7 +136,9 @@ export const getAPIModule: GetIconifyAPIModule = (
}
// Split icons
let item: APIQueryParams = {
const type = 'icons';
let item: APIIconsQueryParams = {
type,
provider,
prefix,
icons: [],
@ -147,6 +150,7 @@ export const getAPIModule: GetIconifyAPIModule = (
// Next set
results.push(item);
item = {
type,
provider,
prefix,
icons: [],
@ -170,17 +174,35 @@ export const getAPIModule: GetIconifyAPIModule = (
status: PendingQueryItem
): void => {
const provider = params.provider;
const prefix = params.prefix;
const icons = params.icons;
const iconsList = icons.join(',');
let path: string;
const cacheKey = provider + ':' + prefix;
const path =
pathCache[cacheKey] +
endPoint
.replace('{provider}', provider)
.replace('{prefix}', prefix)
.replace('{icons}', iconsList);
switch (params.type) {
case 'icons': {
const prefix = params.prefix;
const icons = params.icons;
const iconsList = icons.join(',');
const cacheKey = provider + ':' + prefix;
path =
pathCache[cacheKey] +
endPoint
.replace('{provider}', provider)
.replace('{prefix}', prefix)
.replace('{icons}', iconsList);
break;
}
case 'custom': {
const uri = params.uri;
path = uri.slice(0, 1) === '/' ? uri.slice(1) : uri;
break;
}
default:
// Fail: return 400 Bad Request
status.done(void 0, 400);
return;
}
if (!fetchModule) {
// Fail: return 424 Failed Dependency (its not meant to be used like that, but it is the best match)

View File

@ -1,10 +1,11 @@
import type { PendingQueryItem } from '@cyberalien/redundancy';
import type {
APIQueryParams,
IconifyAPIPrepareQuery,
IconifyAPIPrepareIconsQuery,
IconifyAPISendQuery,
IconifyAPIModule,
GetIconifyAPIModule,
APIIconsQueryParams,
} from '../modules';
import type { GetAPIConfig } from '../config';
@ -50,7 +51,7 @@ function getGlobal(): JSONPRoot {
// Create root
if (rootVar === null) {
// window
const globalRoot = (self as unknown) as Record<string, unknown>;
const globalRoot = self as unknown as Record<string, unknown>;
// Test for window.Iconify. If missing, create 'IconifyJSONP'
let prefix = 'Iconify';
@ -138,12 +139,12 @@ export const getAPIModule: GetIconifyAPIModule = (
/**
* Prepare params
*/
const prepare: IconifyAPIPrepareQuery = (
const prepare: IconifyAPIPrepareIconsQuery = (
provider: string,
prefix: string,
icons: string[]
): APIQueryParams[] => {
const results: APIQueryParams[] = [];
): APIIconsQueryParams[] => {
const results: APIIconsQueryParams[] = [];
// Get maximum icons list length
const cacheKey = provider + ':' + prefix;
@ -153,7 +154,9 @@ export const getAPIModule: GetIconifyAPIModule = (
}
// Split icons
let item: APIQueryParams = {
const type = 'icons';
let item: APIIconsQueryParams = {
type,
provider,
prefix,
icons: [],
@ -165,6 +168,7 @@ export const getAPIModule: GetIconifyAPIModule = (
// Next set
results.push(item);
item = {
type,
provider,
prefix,
icons: [],
@ -187,6 +191,12 @@ export const getAPIModule: GetIconifyAPIModule = (
params: APIQueryParams,
status: PendingQueryItem
): void => {
if (params.type !== 'icons') {
// JSONP supports only icons
status.done(void 0, 400);
return;
}
const provider = params.provider;
const prefix = params.prefix;
const icons = params.icons;

View File

@ -1,7 +1,11 @@
/* eslint-disable @typescript-eslint/no-unused-vars-experimental */
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { PendingQueryItem } from '@cyberalien/redundancy';
import type { APIQueryParams, IconifyAPIModule } from '../modules';
import type {
APIIconsQueryParams,
APIQueryParams,
IconifyAPIModule,
} from '../modules';
import type { IconifyJSON } from '@iconify/types';
/**
@ -15,9 +19,21 @@ export type IconifyMockAPIDelayCallback = (
/**
* Fake API result
*/
export interface IconifyMockAPI {
// Request parameters
interface IconifyMockAPIBase {
provider: string;
// Response
// Number if error should be sent, JSON on success
response: number | IconifyJSON | Record<string, unknown>;
// Delay for response: in milliseconds or callback
delay?: number | IconifyMockAPIDelayCallback;
}
export interface IconifyMockIconsAPI extends IconifyMockAPIBase {
type: 'icons';
// Request parameters
prefix: string;
// If icons list is missing, applies to all requests
@ -27,11 +43,10 @@ export interface IconifyMockAPI {
// Response
// Number if error should be sent, JSON on success
response: number | IconifyJSON;
// Delay for response: in milliseconds or callback
delay?: number | IconifyMockAPIDelayCallback;
}
export type IconifyMockAPI = IconifyMockIconsAPI;
/**
* Fake API storage
*
@ -60,7 +75,7 @@ export function mockAPIData(data: IconifyMockAPI): void {
storage[provider][prefix].push(data);
}
interface MockAPIQueryParams extends APIQueryParams {
interface MockAPIIconsQueryParams extends APIIconsQueryParams {
index: number;
}
@ -71,7 +86,13 @@ export const mockAPIModule: IconifyAPIModule = {
/**
* Prepare params
*/
prepare: (provider: string, prefix: string, icons: string[]) => {
prepare: (
provider: string,
prefix: string,
icons: string[]
): APIIconsQueryParams[] => {
const type = 'icons';
if (
storage[provider] === void 0 ||
storage[provider][prefix] === void 0
@ -79,6 +100,7 @@ export const mockAPIModule: IconifyAPIModule = {
// No mock data: bundle all icons in one request that will return 404
return [
{
type,
provider,
prefix,
icons,
@ -135,9 +157,10 @@ export const mockAPIModule: IconifyAPIModule = {
});
// Sort results
const results: APIQueryParams[] = [];
const results: APIIconsQueryParams[] = [];
if (noMatch.length > 0) {
results.push({
type,
provider,
prefix,
icons: noMatch,
@ -146,11 +169,12 @@ export const mockAPIModule: IconifyAPIModule = {
Object.keys(matches).forEach((key) => {
const index = parseInt(key);
results.push({
type,
provider,
prefix,
icons: matches[index],
index,
} as APIQueryParams);
} as APIIconsQueryParams);
});
return results;
@ -161,22 +185,34 @@ export const mockAPIModule: IconifyAPIModule = {
*/
send: (host: string, params: APIQueryParams, status: PendingQueryItem) => {
const provider = params.provider;
const prefix = params.prefix;
const index = (params as MockAPIQueryParams).index;
let data: IconifyMockAPI;
// Get item
if (
storage[provider] === void 0 ||
storage[provider][prefix] === void 0 ||
storage[provider][prefix][index] === void 0
) {
// No entry
status.done(void 0, 404);
return;
switch (params.type) {
case 'icons': {
const prefix = params.prefix;
const index = (params as MockAPIIconsQueryParams).index;
// Get item
if (
storage[provider] === void 0 ||
storage[provider][prefix] === void 0 ||
storage[provider][prefix][index] === void 0
) {
// No entry
status.done(void 0, 404);
return;
}
data = storage[provider][prefix][index];
break;
}
default:
// Fail: return 400 Bad Request
status.done(void 0, 400);
return;
}
const data = storage[provider][prefix][index];
// Get delay
const delay = data.delay;
let callback: IconifyMockAPIDelayCallback;

View File

@ -8,7 +8,6 @@ import {
mockAPIData,
storage,
} from '../../lib/api/modules/mock';
import type { GetAPIConfig } from '../../lib/api/config';
describe('Testing mock API module prepare function', () => {
let prefixCounter = 0;
@ -28,6 +27,7 @@ describe('Testing mock API module prepare function', () => {
const prefix = nextPrefix();
const item: IconifyMockAPI = {
type: 'icons',
provider,
prefix,
response: 404,
@ -42,6 +42,7 @@ describe('Testing mock API module prepare function', () => {
const result = prepare(provider, prefix, ['foo', 'bar', 'baz']);
expect(result).to.be.eql([
{
type: 'icons',
provider,
prefix,
icons: ['foo', 'bar', 'baz'],
@ -55,18 +56,21 @@ describe('Testing mock API module prepare function', () => {
const prefix = nextPrefix();
const item1: IconifyMockAPI = {
type: 'icons',
provider,
prefix,
response: 404,
icons: ['foo', 'bar'],
};
const item2: IconifyMockAPI = {
type: 'icons',
provider,
prefix,
response: 404,
icons: 'baz',
};
const item3: IconifyMockAPI = {
type: 'icons',
provider,
prefix,
response: {
@ -98,23 +102,27 @@ describe('Testing mock API module prepare function', () => {
expect(result).to.be.eql([
// Unknown icons first
{
type: 'icons',
provider,
prefix,
icons: ['test1', 'test2'],
},
{
type: 'icons',
provider,
prefix,
icons: ['foo', 'bar'],
index: 0,
},
{
type: 'icons',
provider,
prefix,
icons: ['baz'],
index: 1,
},
{
type: 'icons',
provider,
prefix,
icons: ['test10'],
@ -128,6 +136,7 @@ describe('Testing mock API module prepare function', () => {
const prefix = nextPrefix();
const item: IconifyMockAPI = {
type: 'icons',
provider,
prefix,
response: 404,
@ -144,11 +153,13 @@ describe('Testing mock API module prepare function', () => {
expect(result).to.be.eql([
// Missing icons first
{
type: 'icons',
provider,
prefix,
icons: ['bar', 'baz'],
},
{
type: 'icons',
provider,
prefix,
icons: ['foo'],

View File

@ -5,7 +5,11 @@ import { expect } from 'chai';
import type { PendingQueryItem } from '@cyberalien/redundancy';
import type { IconifyAPIConfig } from '../../lib/api/config';
import { setAPIConfig, getAPIConfig } from '../../lib/api/config';
import type { APIQueryParams, IconifyAPIModule } from '../../lib/api/modules';
import type {
APIIconsQueryParams,
APIQueryParams,
IconifyAPIModule,
} from '../../lib/api/modules';
import { setAPIModule, getAPIModule } from '../../lib/api/modules';
describe('Testing API modules', () => {
@ -21,8 +25,9 @@ describe('Testing API modules', () => {
provider: string,
prefix: string,
icons: string[]
): APIQueryParams[] => {
const item: APIQueryParams = {
): APIIconsQueryParams[] => {
const item: APIIconsQueryParams = {
type: 'icons',
provider,
prefix,
icons,

View File

@ -4,7 +4,10 @@ import 'mocha';
import { expect } from 'chai';
import type { PendingQueryItem } from '@cyberalien/redundancy';
import { setAPIConfig } from '../../lib/api/config';
import type { APIQueryParams } from '../../lib/api/modules';
import type {
APIIconsQueryParams,
APIQueryParams,
} from '../../lib/api/modules';
import { setAPIModule } from '../../lib/api/modules';
import { API } from '../../lib/api/';
@ -32,8 +35,9 @@ describe('Testing API loadIcons', () => {
provider: string,
prefix: string,
icons: string[]
): APIQueryParams[] => {
const item: APIQueryParams = {
): APIIconsQueryParams[] => {
const item: APIIconsQueryParams = {
type: 'icons',
provider,
prefix,
icons,
@ -44,7 +48,8 @@ describe('Testing API loadIcons', () => {
asyncCounter++;
// Test input and return as one item
const expected: APIQueryParams = {
const expected: APIIconsQueryParams = {
type: 'icons',
provider,
prefix,
icons: ['icon1', 'icon2'],
@ -63,9 +68,12 @@ describe('Testing API loadIcons', () => {
expect(asyncCounter).to.be.equal(2);
asyncCounter++;
expect(params.type).to.be.equal('icons');
// Test input
expect(host).to.be.equal('https://api1.local');
const expected: APIQueryParams = {
type: 'icons',
provider,
prefix,
icons: ['icon1', 'icon2'],
@ -168,11 +176,12 @@ describe('Testing API loadIcons', () => {
provider: string,
prefix: string,
icons: string[]
): APIQueryParams[] => {
): APIIconsQueryParams[] => {
// Split all icons in multiple queries, one icon per query
const results: APIQueryParams[] = [];
const results: APIIconsQueryParams[] = [];
icons.forEach((icon) => {
const item: APIQueryParams = {
const item: APIIconsQueryParams = {
type: 'icons',
provider,
prefix,
icons: [icon],
@ -194,9 +203,15 @@ describe('Testing API loadIcons', () => {
// Test input
expect(host).to.be.equal('https://api1.local');
expect(params.type).to.be.equal('icons');
if (params.type !== 'icons') {
return;
}
// Icon names should match queryCounter: 'icon1' on first run, 'icon2' on second run
queryCounter++;
const expected: APIQueryParams = {
type: 'icons',
provider,
prefix,
icons: ['icon' + queryCounter],
@ -270,8 +285,9 @@ describe('Testing API loadIcons', () => {
provider: string,
prefix: string,
icons: string[]
): APIQueryParams[] => {
const item: APIQueryParams = {
): APIIconsQueryParams[] => {
const item: APIIconsQueryParams = {
type: 'icons',
provider,
prefix,
icons,
@ -372,8 +388,9 @@ describe('Testing API loadIcons', () => {
provider: string,
prefix: string,
icons: string[]
): APIQueryParams[] => {
const item: APIQueryParams = {
): APIIconsQueryParams[] => {
const item: APIIconsQueryParams = {
type: 'icons',
provider,
prefix,
icons,
@ -388,6 +405,12 @@ describe('Testing API loadIcons', () => {
item: PendingQueryItem
): void => {
queryCounter++;
expect(params.type).to.be.equal('icons');
if (params.type !== 'icons') {
return;
}
switch (queryCounter) {
case 1:
// First call on api1
@ -528,8 +551,9 @@ describe('Testing API loadIcons', () => {
provider: string,
prefix: string,
icons: string[]
): APIQueryParams[] => {
const item: APIQueryParams = {
): APIIconsQueryParams[] => {
const item: APIIconsQueryParams = {
type: 'icons',
provider,
prefix,
icons,
@ -544,6 +568,12 @@ describe('Testing API loadIcons', () => {
item: PendingQueryItem
): void => {
queryCounter++;
expect(params.type).to.be.equal('icons');
if (params.type !== 'icons') {
return;
}
switch (queryCounter) {
case 1:
// First call on api1

View File

@ -28,6 +28,7 @@ describe('Testing mock API module', () => {
const prefix = nextPrefix();
mockAPIData({
type: 'icons',
provider,
prefix,
icons: ['test1', 'test2'],
@ -66,6 +67,7 @@ describe('Testing mock API module', () => {
const prefix = nextPrefix();
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
@ -81,6 +83,7 @@ describe('Testing mock API module', () => {
},
});
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
@ -140,6 +143,7 @@ describe('Testing mock API module', () => {
let next: IconifyMockAPIDelayDoneCallback | undefined;
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
@ -155,6 +159,7 @@ describe('Testing mock API module', () => {
},
});
mockAPIData({
type: 'icons',
provider,
prefix,
response: {
@ -246,6 +251,7 @@ describe('Testing mock API module', () => {
// Mock data
mockAPIData({
type: 'icons',
provider,
prefix,
response: {

View File

@ -24,6 +24,7 @@ describe('Testing simple names with API module', () => {
it('Loading icons without prefix', (done) => {
mockAPIData({
type: 'icons',
provider: '',
prefix: '',
response: {
@ -39,6 +40,7 @@ describe('Testing simple names with API module', () => {
},
});
mockAPIData({
type: 'icons',
provider: '',
prefix: 'test200',
response: {