From c230b0e5de5b9c108e5761e187b6c87cf8c87b8b Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 27 Jul 2020 16:43:27 +0200 Subject: [PATCH 1/3] Algolia theme option validation --- .../package.json | 1 + .../src/__tests__/validateOptions.test.js | 78 +++++++++++++++++++ .../src/index.js | 9 ++- .../src/validateOptions.js | 30 +++++++ 4 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 packages/docusaurus-theme-search-algolia/src/__tests__/validateOptions.test.js create mode 100644 packages/docusaurus-theme-search-algolia/src/validateOptions.js diff --git a/packages/docusaurus-theme-search-algolia/package.json b/packages/docusaurus-theme-search-algolia/package.json index 2ae636ca554a..ba0db4ccd276 100644 --- a/packages/docusaurus-theme-search-algolia/package.json +++ b/packages/docusaurus-theme-search-algolia/package.json @@ -9,6 +9,7 @@ "license": "MIT", "dependencies": { "@docsearch/react": "^1.0.0-alpha.24", + "@hapi/joi": "^17.1.1", "algoliasearch": "^4.0.0", "algoliasearch-helper": "^3.1.1", "clsx": "^1.1.1", diff --git a/packages/docusaurus-theme-search-algolia/src/__tests__/validateOptions.test.js b/packages/docusaurus-theme-search-algolia/src/__tests__/validateOptions.test.js new file mode 100644 index 000000000000..5809de9e4bec --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/src/__tests__/validateOptions.test.js @@ -0,0 +1,78 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const {validateOptions, DEFAULT_OPTIONS} = require('../validateOptions'); + +function testValidateOptions(options) { + function validate(schema, opt) { + const {value, error} = schema.validate(opt, { + convert: false, + }); + if (error) { + throw error; + } else { + return value; + } + } + + return validateOptions({options, validate}); +} + +describe('validateOptions', () => { + test('minimal config', () => { + const options = { + indexName: 'index', + apiKey: 'apiKey', + }; + expect(testValidateOptions(options)).toEqual({ + ...DEFAULT_OPTIONS, + ...options, + }); + }); + + test('unknown attributes', () => { + const options = { + indexName: 'index', + apiKey: 'apiKey', + unknownKey: 'unknownKey', + }; + expect(testValidateOptions(options)).toEqual({ + ...DEFAULT_OPTIONS, + ...options, + }); + }); + + test('undefined config', () => { + const options = undefined; + expect(() => + testValidateOptions(options), + ).toThrowErrorMatchingInlineSnapshot( + `"\\"Algolia theme config\\" is required"`, + ); + }); + + test('empty config', () => { + const options = {}; + expect(() => + testValidateOptions(options), + ).toThrowErrorMatchingInlineSnapshot(`"\\"apiKey\\" is required"`); + }); + + test('missing indexName config', () => { + const options = {apiKey: 'apiKey'}; + expect(() => + testValidateOptions(options), + ).toThrowErrorMatchingInlineSnapshot(`"\\"indexName\\" is required"`); + }); + + test('missing apiKey config', () => { + const options = {indexName: 'indexName'}; + expect(() => + testValidateOptions(options), + ).toThrowErrorMatchingInlineSnapshot(`"\\"apiKey\\" is required"`); + }); +}); diff --git a/packages/docusaurus-theme-search-algolia/src/index.js b/packages/docusaurus-theme-search-algolia/src/index.js index 634108bded7b..228bbd19ea2f 100644 --- a/packages/docusaurus-theme-search-algolia/src/index.js +++ b/packages/docusaurus-theme-search-algolia/src/index.js @@ -10,10 +10,11 @@ const fs = require('fs'); const eta = require('eta'); const {normalizeUrl} = require('@docusaurus/utils'); const openSearchTemplate = require('./templates/opensearch'); +const {validateOptions} = require('./validateOptions'); const OPEN_SEARCH_FILENAME = 'opensearch.xml'; -module.exports = function (context) { +function plugin(context) { const { baseUrl, siteConfig: {title, url, favicon}, @@ -70,4 +71,8 @@ module.exports = function (context) { }; }, }; -}; +} + +module.exports = plugin; + +plugin.validateOptions = validateOptions; diff --git a/packages/docusaurus-theme-search-algolia/src/validateOptions.js b/packages/docusaurus-theme-search-algolia/src/validateOptions.js new file mode 100644 index 000000000000..e2757cc7510c --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/src/validateOptions.js @@ -0,0 +1,30 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const Joi = require('@hapi/joi'); + +const DEFAULT_OPTIONS = { + // By default, all Docusaurus sites are using the same AppId + // This has been designed on purpose with Algolia. + appId: 'BH4D9OD16A', +}; +exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS; + +const Schema = Joi.object({ + appId: Joi.string().default(DEFAULT_OPTIONS.appId), + apiKey: Joi.string().required(), + indexName: Joi.string().required(), +}) + .label('Algolia theme config') + .required() + .unknown(); // DocSearch 3 is still alpha: don't validate the rest for now +exports.Schema = Schema; + +exports.validateOptions = function validateOptions({validate, options}) { + const validatedOptions = validate(Schema, options); + return validatedOptions; +}; From 7bc88c8e493b192f93044522c1c7686369452e63 Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 27 Jul 2020 17:08:33 +0200 Subject: [PATCH 2/3] validateThemeConfig --- .../src/__tests__/validateOptions.test.js | 78 ---------------- .../src/__tests__/validateThemeConfig.test.js | 91 +++++++++++++++++++ .../src/index.js | 8 +- .../src/validateOptions.js | 30 ------ .../src/validateThemeConfig.js | 34 +++++++ 5 files changed, 129 insertions(+), 112 deletions(-) delete mode 100644 packages/docusaurus-theme-search-algolia/src/__tests__/validateOptions.test.js create mode 100644 packages/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.js delete mode 100644 packages/docusaurus-theme-search-algolia/src/validateOptions.js create mode 100644 packages/docusaurus-theme-search-algolia/src/validateThemeConfig.js diff --git a/packages/docusaurus-theme-search-algolia/src/__tests__/validateOptions.test.js b/packages/docusaurus-theme-search-algolia/src/__tests__/validateOptions.test.js deleted file mode 100644 index 5809de9e4bec..000000000000 --- a/packages/docusaurus-theme-search-algolia/src/__tests__/validateOptions.test.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -const {validateOptions, DEFAULT_OPTIONS} = require('../validateOptions'); - -function testValidateOptions(options) { - function validate(schema, opt) { - const {value, error} = schema.validate(opt, { - convert: false, - }); - if (error) { - throw error; - } else { - return value; - } - } - - return validateOptions({options, validate}); -} - -describe('validateOptions', () => { - test('minimal config', () => { - const options = { - indexName: 'index', - apiKey: 'apiKey', - }; - expect(testValidateOptions(options)).toEqual({ - ...DEFAULT_OPTIONS, - ...options, - }); - }); - - test('unknown attributes', () => { - const options = { - indexName: 'index', - apiKey: 'apiKey', - unknownKey: 'unknownKey', - }; - expect(testValidateOptions(options)).toEqual({ - ...DEFAULT_OPTIONS, - ...options, - }); - }); - - test('undefined config', () => { - const options = undefined; - expect(() => - testValidateOptions(options), - ).toThrowErrorMatchingInlineSnapshot( - `"\\"Algolia theme config\\" is required"`, - ); - }); - - test('empty config', () => { - const options = {}; - expect(() => - testValidateOptions(options), - ).toThrowErrorMatchingInlineSnapshot(`"\\"apiKey\\" is required"`); - }); - - test('missing indexName config', () => { - const options = {apiKey: 'apiKey'}; - expect(() => - testValidateOptions(options), - ).toThrowErrorMatchingInlineSnapshot(`"\\"indexName\\" is required"`); - }); - - test('missing apiKey config', () => { - const options = {indexName: 'indexName'}; - expect(() => - testValidateOptions(options), - ).toThrowErrorMatchingInlineSnapshot(`"\\"apiKey\\" is required"`); - }); -}); diff --git a/packages/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.js b/packages/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.js new file mode 100644 index 000000000000..cb842e823352 --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/src/__tests__/validateThemeConfig.test.js @@ -0,0 +1,91 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const {validateThemeConfig, DEFAULT_CONFIG} = require('../validateThemeConfig'); + +function testValidateThemeConfig(themeConfig) { + function validate(schema, cfg) { + const {value, error} = schema.validate(cfg, { + convert: false, + }); + if (error) { + throw error; + } + return value; + } + + return validateThemeConfig({themeConfig, validate}); +} + +describe('validateThemeConfig', () => { + test('minimal config', () => { + const algolia = { + indexName: 'index', + apiKey: 'apiKey', + }; + expect(testValidateThemeConfig({algolia})).toEqual({ + algolia: { + ...DEFAULT_CONFIG, + ...algolia, + }, + }); + }); + + test('unknown attributes', () => { + const algolia = { + indexName: 'index', + apiKey: 'apiKey', + unknownKey: 'unknownKey', + }; + expect(testValidateThemeConfig({algolia})).toEqual({ + algolia: { + ...DEFAULT_CONFIG, + ...algolia, + }, + }); + }); + + test('undefined config', () => { + const algolia = undefined; + expect(() => + testValidateThemeConfig({algolia}), + ).toThrowErrorMatchingInlineSnapshot( + `"\\"themeConfig.algolia\\" is required"`, + ); + }); + + test('undefined config 2', () => { + expect(() => + testValidateThemeConfig({}), + ).toThrowErrorMatchingInlineSnapshot( + `"\\"themeConfig.algolia\\" is required"`, + ); + }); + + test('empty config', () => { + const algolia = {}; + expect(() => + testValidateThemeConfig({algolia}), + ).toThrowErrorMatchingInlineSnapshot(`"\\"algolia.apiKey\\" is required"`); + }); + + test('missing indexName config', () => { + const algolia = {apiKey: 'apiKey'}; + expect(() => + testValidateThemeConfig({algolia}), + ).toThrowErrorMatchingInlineSnapshot( + `"\\"algolia.indexName\\" is required"`, + ); + }); + + test('missing apiKey config', () => { + const algolia = {indexName: 'indexName'}; + expect(() => + testValidateThemeConfig({algolia}), + ).toThrowErrorMatchingInlineSnapshot(`"\\"algolia.apiKey\\" is required"`); + }); +}); diff --git a/packages/docusaurus-theme-search-algolia/src/index.js b/packages/docusaurus-theme-search-algolia/src/index.js index 228bbd19ea2f..958c0d6f81ce 100644 --- a/packages/docusaurus-theme-search-algolia/src/index.js +++ b/packages/docusaurus-theme-search-algolia/src/index.js @@ -10,11 +10,11 @@ const fs = require('fs'); const eta = require('eta'); const {normalizeUrl} = require('@docusaurus/utils'); const openSearchTemplate = require('./templates/opensearch'); -const {validateOptions} = require('./validateOptions'); +const {validateThemeConfig} = require('./validateThemeConfig'); const OPEN_SEARCH_FILENAME = 'opensearch.xml'; -function plugin(context) { +function theme(context) { const { baseUrl, siteConfig: {title, url, favicon}, @@ -73,6 +73,6 @@ function plugin(context) { }; } -module.exports = plugin; +module.exports = theme; -plugin.validateOptions = validateOptions; +theme.validateThemeConfig = validateThemeConfig; diff --git a/packages/docusaurus-theme-search-algolia/src/validateOptions.js b/packages/docusaurus-theme-search-algolia/src/validateOptions.js deleted file mode 100644 index e2757cc7510c..000000000000 --- a/packages/docusaurus-theme-search-algolia/src/validateOptions.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -const Joi = require('@hapi/joi'); - -const DEFAULT_OPTIONS = { - // By default, all Docusaurus sites are using the same AppId - // This has been designed on purpose with Algolia. - appId: 'BH4D9OD16A', -}; -exports.DEFAULT_OPTIONS = DEFAULT_OPTIONS; - -const Schema = Joi.object({ - appId: Joi.string().default(DEFAULT_OPTIONS.appId), - apiKey: Joi.string().required(), - indexName: Joi.string().required(), -}) - .label('Algolia theme config') - .required() - .unknown(); // DocSearch 3 is still alpha: don't validate the rest for now -exports.Schema = Schema; - -exports.validateOptions = function validateOptions({validate, options}) { - const validatedOptions = validate(Schema, options); - return validatedOptions; -}; diff --git a/packages/docusaurus-theme-search-algolia/src/validateThemeConfig.js b/packages/docusaurus-theme-search-algolia/src/validateThemeConfig.js new file mode 100644 index 000000000000..adda5956c5f8 --- /dev/null +++ b/packages/docusaurus-theme-search-algolia/src/validateThemeConfig.js @@ -0,0 +1,34 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const Joi = require('@hapi/joi'); + +const DEFAULT_CONFIG = { + // By default, all Docusaurus sites are using the same AppId + // This has been designed on purpose with Algolia. + appId: 'BH4D9OD16A', +}; +exports.DEFAULT_CONFIG = DEFAULT_CONFIG; + +const Schema = Joi.object({ + algolia: Joi.object({ + appId: Joi.string().default(DEFAULT_CONFIG.appId), + apiKey: Joi.string().required(), + indexName: Joi.string().required(), + }) + .label('themeConfig.algolia') + .required() + .unknown(), // DocSearch 3 is still alpha: don't validate the rest for now +}); +exports.Schema = Schema; + +exports.validateThemeConfig = function validateThemeConfig({ + validate, + themeConfig, +}) { + return validate(Schema, themeConfig); +}; From 79353fdcc1ca1dec6efb98c03111ed49bb213fb4 Mon Sep 17 00:00:00 2001 From: slorber Date: Mon, 27 Jul 2020 17:12:02 +0200 Subject: [PATCH 3/3] remove useless runtime check --- .../src/theme/SearchBar/index.js | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js index 7596f5ce13d6..3c0e3c8c1d85 100644 --- a/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js +++ b/packages/docusaurus-theme-search-algolia/src/theme/SearchBar/index.js @@ -128,17 +128,7 @@ function DocSearch(props) { } function SearchBar() { - const {siteConfig = {}} = useDocusaurusContext(); - - if (!siteConfig.themeConfig.algolia) { - // eslint-disable-next-line no-console - console.warn(`DocSearch requires an \`algolia\` field in your \`themeConfig\`. - -See: https://v2.docusaurus.io/docs/search/#using-algolia-docsearch`); - - return null; - } - + const {siteConfig} = useDocusaurusContext(); return ; }