From fbb836d3c0810d88935d060a8abe5c13682e4639 Mon Sep 17 00:00:00 2001 From: Vjacheslav Trushkin Date: Mon, 30 Aug 2021 10:22:53 +0300 Subject: [PATCH] Split API queries into icons and custom queries --- packages/core/src/api/modules.ts | 16 +++- packages/core/src/api/modules/fetch.ts | 52 ++++++++---- packages/core/src/api/modules/jsonp.ts | 22 +++-- packages/core/src/api/modules/mock.ts | 82 +++++++++++++------ .../core/tests/30-api/10-mock-prepare-test.ts | 13 ++- packages/core/tests/30-api/10-modules-test.ts | 11 ++- packages/core/tests/30-api/20-loading-test.ts | 56 ++++++++++--- packages/core/tests/30-api/30-mock-test.ts | 6 ++ .../core/tests/30-api/40-simple-names-test.ts | 2 + 9 files changed, 195 insertions(+), 65 deletions(-) diff --git a/packages/core/src/api/modules.ts b/packages/core/src/api/modules.ts index c75412c..0b4cdc9 100644 --- a/packages/core/src/api/modules.ts +++ b/packages/core/src/api/modules.ts @@ -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; } diff --git a/packages/core/src/api/modules/fetch.ts b/packages/core/src/api/modules/fetch.ts index 26671b6..487dfd5 100644 --- a/packages/core/src/api/modules/fetch.ts +++ b/packages/core/src/api/modules/fetch.ts @@ -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) diff --git a/packages/core/src/api/modules/jsonp.ts b/packages/core/src/api/modules/jsonp.ts index e8cd11a..89a5904 100644 --- a/packages/core/src/api/modules/jsonp.ts +++ b/packages/core/src/api/modules/jsonp.ts @@ -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; + const globalRoot = self as unknown as Record; // 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; diff --git a/packages/core/src/api/modules/mock.ts b/packages/core/src/api/modules/mock.ts index fe8e0f7..b1f18ed 100644 --- a/packages/core/src/api/modules/mock.ts +++ b/packages/core/src/api/modules/mock.ts @@ -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; + + // 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; diff --git a/packages/core/tests/30-api/10-mock-prepare-test.ts b/packages/core/tests/30-api/10-mock-prepare-test.ts index 69fca11..ebdd42c 100644 --- a/packages/core/tests/30-api/10-mock-prepare-test.ts +++ b/packages/core/tests/30-api/10-mock-prepare-test.ts @@ -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'], diff --git a/packages/core/tests/30-api/10-modules-test.ts b/packages/core/tests/30-api/10-modules-test.ts index 2b505d3..4542e76 100644 --- a/packages/core/tests/30-api/10-modules-test.ts +++ b/packages/core/tests/30-api/10-modules-test.ts @@ -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, diff --git a/packages/core/tests/30-api/20-loading-test.ts b/packages/core/tests/30-api/20-loading-test.ts index 39f1960..d6aad25 100644 --- a/packages/core/tests/30-api/20-loading-test.ts +++ b/packages/core/tests/30-api/20-loading-test.ts @@ -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 diff --git a/packages/core/tests/30-api/30-mock-test.ts b/packages/core/tests/30-api/30-mock-test.ts index b23c154..94b5320 100644 --- a/packages/core/tests/30-api/30-mock-test.ts +++ b/packages/core/tests/30-api/30-mock-test.ts @@ -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: { diff --git a/packages/core/tests/30-api/40-simple-names-test.ts b/packages/core/tests/30-api/40-simple-names-test.ts index fe9dff9..92140a0 100644 --- a/packages/core/tests/30-api/40-simple-names-test.ts +++ b/packages/core/tests/30-api/40-simple-names-test.ts @@ -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: {