diff --git a/.github/.cache_version b/.github/.cache_version index 10e598c19c..8b22a322d0 100644 --- a/.github/.cache_version +++ b/.github/.cache_version @@ -1 +1 @@ -7.2.1.0.1 +8.0.2 diff --git a/.github/actions/cache/action.yml b/.github/actions/cache/action.yml index 348e879b8b..28ff64310c 100644 --- a/.github/actions/cache/action.yml +++ b/.github/actions/cache/action.yml @@ -132,6 +132,18 @@ runs: 'specs/common/**' )}} + - name: Restore built algoliasearch-lite spec + if: ${{ inputs.job == 'cts' || inputs.job == 'client' || inputs.job == 'codegen' }} + uses: actions/cache@v2 + with: + path: specs/bundled/algoliasearch-lite.yml + key: | + ${{ env.CACHE_VERSION }}-${{ + hashFiles( + 'specs/search/**', + 'specs/common/**' + )}} + - name: Restore built sources spec if: ${{ inputs.job == 'cts' || inputs.job == 'client' || inputs.job == 'codegen' }} uses: actions/cache@v2 @@ -193,6 +205,23 @@ runs: 'clients/algoliasearch-client-javascript/packages/client-personalization/**' )}} + - name: Restore built JavaScript algoliasearch-lite client + if: ${{ inputs.job == 'cts' || inputs.job == 'codegen' }} + uses: actions/cache@v2 + with: + path: clients/algoliasearch-client-javascript/packages/algoliasearch-lite + key: | + ${{ env.CACHE_VERSION }}-${{ + hashFiles( + 'clients/algoliasearch-client-javascript/packages/algoliasearch-lite/src/**', + 'clients/algoliasearch-client-javascript/packages/algoliasearch-lite/model/**', + 'clients/algoliasearch-client-javascript/packages/algoliasearch-lite/builds/**', + 'clients/algoliasearch-client-javascript/packages/algoliasearch-lite/package.json', + 'specs/bundled/algoliasearch-lite.yml', + 'templates/javascript/**', + 'generators/src/**' + )}} + - name: Restore built JavaScript search client if: ${{ inputs.job == 'cts' || inputs.job == 'codegen' }} uses: actions/cache@v2 diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 305e0e14c7..cbbe70450c 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -94,21 +94,21 @@ jobs: - name: Restore cache uses: ./.github/actions/cache - - name: Cache '${{ matrix.client }}' bundled specs + - name: Cache '${{ matrix.client.name }}' bundled specs id: cache uses: actions/cache@v2 with: - path: specs/bundled/${{ matrix.client }}.yml + path: ${{ matrix.client.bundledPath }} key: | ${{ env.CACHE_VERSION }}-${{ hashFiles( - format('specs/{0}/**', matrix.client), + format('{0}/**', matrix.client.path), 'specs/common/**' )}} - - name: Building '${{ matrix.client }}' specs + - name: Building '${{ matrix.client.name }}' specs if: steps.cache.outputs.cache-hit != 'true' - run: yarn cli build specs ${{ matrix.client }} + run: yarn cli build specs ${{ matrix.client.name }} client_javascript_common: timeout-minutes: 10 @@ -169,14 +169,14 @@ jobs: id: cache uses: actions/cache@v2 with: - path: ${{ matrix.client.folder }} + path: ${{ matrix.client.path }} key: | ${{ env.CACHE_VERSION }}-${{ hashFiles( - format('{0}/src/**', matrix.client.folder), - format('{0}/model/**', matrix.client.folder), - format('{0}/builds/**', matrix.client.folder), - format('{0}/package.json', matrix.client.folder), + format('{0}/src/**', matrix.client.path), + format('{0}/model/**', matrix.client.path), + format('{0}/builds/**', matrix.client.path), + format('{0}/package.json', matrix.client.path), format('specs/bundled/{0}.yml', matrix.client.name), 'templates/javascript/**', 'generators/src/**' @@ -220,12 +220,12 @@ jobs: id: cache uses: actions/cache@v2 with: - path: ${{ matrix.client.folder }} + path: ${{ matrix.client.path }} key: | ${{ env.CACHE_VERSION }}-${{ hashFiles( - format('{0}/{1}/**', matrix.client.folder, matrix.client.name), - format('{0}/model/{1}/**', matrix.client.folder, matrix.client.name), + format('{0}/{1}/**', matrix.client.path, matrix.client.name), + format('{0}/model/{1}/**', matrix.client.path, matrix.client.name), format('specs/bundled/{0}.yml', matrix.client.name), 'templates/java/**', 'generators/src/**' diff --git a/clients/algoliasearch-client-javascript/bundlesize.config.json b/clients/algoliasearch-client-javascript/bundlesize.config.json index af02c71de4..770a781f3f 100644 --- a/clients/algoliasearch-client-javascript/bundlesize.config.json +++ b/clients/algoliasearch-client-javascript/bundlesize.config.json @@ -4,6 +4,10 @@ "path": "packages/algoliasearch/dist/algoliasearch.umd.browser.js", "maxSize": "7.05KB" }, + { + "path": "packages/algoliasearch-lite/dist/algoliasearch-lite.umd.browser.js", + "maxSize": "3.80KB" + }, { "path": "packages/client-abtesting/dist/client-abtesting.umd.browser.js", "maxSize": "3.85KB" diff --git a/clients/algoliasearch-client-javascript/packages/algoliasearch-lite/.openapi-generator-ignore b/clients/algoliasearch-client-javascript/packages/algoliasearch-lite/.openapi-generator-ignore new file mode 100644 index 0000000000..a6fb9df9d1 --- /dev/null +++ b/clients/algoliasearch-client-javascript/packages/algoliasearch-lite/.openapi-generator-ignore @@ -0,0 +1,8 @@ +# OpenAPI Generator Ignore +# Generated by openapi-generator https://github.com/openapitools/openapi-generator + +# Use this file to prevent files from being overwritten by the generator. +# The patterns follow closely to .gitignore or .dockerignore. + +git_push.sh +.gitignore diff --git a/clients/algoliasearch-client-javascript/rollup.config.js b/clients/algoliasearch-client-javascript/rollup.config.js index a28d6657a9..5bea747fd5 100644 --- a/clients/algoliasearch-client-javascript/rollup.config.js +++ b/clients/algoliasearch-client-javascript/rollup.config.js @@ -112,7 +112,7 @@ function initPackagesConfig() { } return availableClients.flatMap((packageName) => { - const isAlgoliasearchClient = packageName.startsWith('algoliasearch'); + const isAlgoliasearchClient = packageName === 'algoliasearch'; const commonConfig = { package: packageName, name: isAlgoliasearchClient diff --git a/openapitools.json b/openapitools.json index 3e41d5e023..fddab6e0a3 100644 --- a/openapitools.json +++ b/openapitools.json @@ -26,6 +26,29 @@ "utilsPackageVersion": "0.0.5" } }, + "javascript-algoliasearch-lite": { + "generatorName": "algolia-javascript", + "templateDir": "#{cwd}/templates/javascript/", + "config": "#{cwd}/openapitools.json", + "apiPackage": "src", + "output": "#{cwd}/clients/algoliasearch-client-javascript/packages/algoliasearch-lite", + "glob": "specs/bundled/algoliasearch-lite.yml", + "gitHost": "algolia", + "gitUserId": "algolia", + "gitRepoId": "algoliasearch-client-javascript", + "reservedWordsMappings": "queryParameters=queryParameters,requestOptions=requestOptions", + "additionalProperties": { + "modelPropertyNaming": "original", + "supportsES6": true, + "npmName": "@experimental-api-clients-automation/algoliasearch-lite", + "buildFile": "algoliasearch-lite", + "apiName": "algoliasearchLite", + "capitalizedApiName": "AlgoliasearchLite", + "packageVersion": "0.0.5", + "packageName": "@experimental-api-clients-automation/algoliasearch-lite", + "utilsPackageVersion": "0.0.5" + } + }, "javascript-recommend": { "generatorName": "algolia-javascript", "templateDir": "#{cwd}/templates/javascript/", diff --git a/scripts/buildSpecs.ts b/scripts/buildSpecs.ts index aa916ded5c..32476279e6 100644 --- a/scripts/buildSpecs.ts +++ b/scripts/buildSpecs.ts @@ -6,8 +6,16 @@ import { checkForCache, exists, run, toAbsolutePath } from './common'; import { createSpinner } from './oraLog'; import type { Spec } from './pre-gen/setHostsOptions'; +const ALGOLIASEARCH_LITE_OPERATIONS = [ + 'search', + 'multipleQueries', + 'searchForFacetValues', + 'post', +]; + async function propagateTagsToOperations( - bundledPath: string + bundledPath: string, + client: string ): Promise { if (!(await exists(bundledPath))) { throw new Error(`Bundled file not found ${bundledPath}.`); @@ -17,17 +25,9 @@ async function propagateTagsToOperations( await fsp.readFile(bundledPath, 'utf8') ) as Spec; - if (bundledSpec.tags.length === 0) { - throw new Error( - `No tags defined for ${bundledPath}, tags are required to properly generate a client.` - ); - } - - const tagsName = bundledSpec.tags.map((tag) => tag.name); - for (const pathMethods of Object.values(bundledSpec.paths)) { for (const specMethod of Object.values(pathMethods)) { - specMethod.tags = tagsName; + specMethod.tags = [client]; } } @@ -74,22 +74,77 @@ async function lintCommon(verbose: boolean, useCache: boolean): Promise { spinner.succeed(); } +/** + * Creates a lite search spec with the `ALGOLIASEARCH_LITE_OPERATIONS` methods + * from the `search` spec. + */ +async function buildLiteSpec( + spec: string, + bundledPath: string, + outputFormat: string, + verbose: boolean +): Promise { + const searchSpec = yaml.load( + await fsp.readFile(toAbsolutePath(bundledPath), 'utf8') + ) as Spec; + + searchSpec.paths = Object.entries(searchSpec.paths).reduce( + (acc, [path, operations]) => { + for (const [method, operation] of Object.entries(operations)) { + if ( + method === 'post' && + ALGOLIASEARCH_LITE_OPERATIONS.includes(operation.operationId) + ) { + return { ...acc, [path]: { post: operation } }; + } + } + + return acc; + }, + {} as Spec['paths'] + ); + + const liteBundledPath = `specs/bundled/${spec}.${outputFormat}`; + await fsp.writeFile(toAbsolutePath(liteBundledPath), yaml.dump(searchSpec)); + + if ( + !(await propagateTagsToOperations(toAbsolutePath(liteBundledPath), spec)) + ) { + throw new Error( + `Unable to propage tags to operations for \`${spec}\` spec.` + ); + } + + await run(`yarn specs:fix bundled/${spec}.${outputFormat}`, { + verbose, + }); +} + async function buildSpec( - client: string, + spec: string, outputFormat: string, verbose: boolean, useCache: boolean ): Promise { - createSpinner(`'${client}' spec`, verbose).start().info(); + const shouldBundleLiteSpec = spec === 'algoliasearch-lite'; + const client = shouldBundleLiteSpec ? 'search' : spec; const cacheFile = toAbsolutePath(`specs/dist/${client}.cache`); let hash = ''; + createSpinner(`'${client}' spec`, verbose).start().info(); + if (useCache) { + const generatedFiles = [`bundled/${client}.yml`]; + + if (shouldBundleLiteSpec) { + generatedFiles.push(`bundled/${spec}.yml`); + } + const { cacheExists, hash: newCache } = await checkForCache( { job: `'${client}' specs`, folder: toAbsolutePath('specs/'), - generatedFiles: [`bundled/${client}.yml`], + generatedFiles, filesToCache: [client, 'common'], cacheFile, }, @@ -110,9 +165,7 @@ async function buildSpec( { verbose } ); - if ( - (await propagateTagsToOperations(toAbsolutePath(bundledPath))) === false - ) { + if (!(await propagateTagsToOperations(toAbsolutePath(bundledPath), client))) { spinner.fail(); throw new Error( `Unable to propage tags to operations for \`${client}\` spec.` @@ -130,6 +183,11 @@ async function buildSpec( spinner.text = `linting '${client}' bundled spec`; await run(`yarn specs:fix bundled/${client}.${outputFormat}`, { verbose }); + if (shouldBundleLiteSpec) { + spinner.text = `Building and linting '${spec}' spec`; + await buildLiteSpec(spec, bundledPath, outputFormat, verbose); + } + if (hash) { spinner.text = `storing ${client} spec cache`; await fsp.writeFile(cacheFile, hash); diff --git a/scripts/ci/createMatrix.ts b/scripts/ci/createMatrix.ts index 86c009690b..56a5815846 100644 --- a/scripts/ci/createMatrix.ts +++ b/scripts/ci/createMatrix.ts @@ -10,14 +10,21 @@ type CreateMatrix = { language?: Language; }; -type ClientMatrix = { +type BaseMatrix = { name: string; - folder: string; + path: string; +}; + +type ClientMatrix = BaseMatrix & { config?: string; api?: string; capitalizedName?: string; }; +type SpecMatrix = BaseMatrix & { + bundledPath: string; +}; + type Matrix = { client: TMatrix[]; }; @@ -61,7 +68,7 @@ async function getClientMatrix({ const matchedGenerator: ClientMatrix = { name: client, - folder: output, + path: output, }; // Extra informations for the PHP matrix in order to properly scope the @@ -84,8 +91,8 @@ async function getClientMatrix({ async function getSpecMatrix({ baseBranch, baseChanged, -}: CreateMatrix): Promise> { - const matrix: Matrix = { client: [] }; +}: CreateMatrix): Promise> { + const matrix: Matrix = { client: [] }; for (const client of CLIENTS) { const specChanges = await getNbGitDiff({ @@ -97,7 +104,23 @@ async function getSpecMatrix({ continue; } - matrix.client.push(client); + const spec = { + name: client, + path: `specs/${client}`, + bundledPath: `specs/bundled/${client}.yml`, + }; + + // The `algoliasearch-lite` spec is created by the `search` spec + if (client === 'algoliasearch-lite') { + matrix.client.push({ + ...spec, + path: 'specs/search', + }); + + continue; + } + + matrix.client.push(spec); } return matrix; diff --git a/scripts/generate.ts b/scripts/generate.ts index 226866ea00..88ad9a5bb6 100644 --- a/scripts/generate.ts +++ b/scripts/generate.ts @@ -102,6 +102,7 @@ export async function generate( await buildSpecs(clients, 'yml', verbose, true); } + const availableWorkspaces = await run('yarn workspaces list', { verbose }); const langs = [...new Set(generators.map((gen) => gen.language))]; const useCustomGenerator = langs .map((lang) => getCustomGenerator(lang)) @@ -117,6 +118,17 @@ export async function generate( spinner.text = `generating ${gen.key}`; await generateClient(gen, verbose); + // Prevents the CI/CLI to throw when a new JS client is generated + // by linking it if it's not the case + if ( + gen.language === 'javascript' && + !availableWorkspaces.includes(gen.output) + ) { + spinner.text = `First time generating ${gen.client}, linking to workspaces`; + + await run('YARN_ENABLE_IMMUTABLE_INSTALLS=false yarn', { verbose }); + } + spinner.text = `post-gen ${gen.key}`; await postGen(gen, verbose); diff --git a/scripts/index.ts b/scripts/index.ts index abdf7e5884..186f7a4484 100644 --- a/scripts/index.ts +++ b/scripts/index.ts @@ -150,15 +150,17 @@ buildCommand { verbose, interactive } ) => { language = await promptLanguage(language, interactive); - client = await promptClient(client, interactive, [ - ...CLIENTS_JS_UTILS, - ...CLIENTS_JS, - ]); + + const shouldBuildJs = language === 'javascript' || language === 'all'; + const clientList = shouldBuildJs + ? [...CLIENTS_JS_UTILS, ...CLIENTS_JS] + : CLIENTS; + client = await promptClient(client, interactive, clientList); // We build the JavaScript utils before generated clients as they // rely on them if ( - (language === 'javascript' || language === 'all') && + shouldBuildJs && (!client || client === 'all' || CLIENTS_JS_UTILS.includes(client)) ) { await buildJSClientUtils(Boolean(verbose), client); diff --git a/scripts/pre-gen/setHostsOptions.ts b/scripts/pre-gen/setHostsOptions.ts index 9182722459..6a27f7cdfe 100644 --- a/scripts/pre-gen/setHostsOptions.ts +++ b/scripts/pre-gen/setHostsOptions.ts @@ -21,12 +21,12 @@ type Tag = { description: string; }; -type Path = Record>; +type Paths = Record>; export type Spec = { servers: Server[]; tags: Tag[]; - paths: Path[]; + paths: Paths; }; type AdditionalProperties = Partial<{