diff --git a/.circleci/config.yml b/.circleci/config.yml index 5e7ae6e..8f2d5ea 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,5 +28,7 @@ jobs: steps: - checkout - install_deps - - run: yarn test + - run: yarn build - run: yarn lint:ci + - run: yarn type-check + - run: yarn test diff --git a/.eslintignore b/.eslintignore index be059c4..125543a 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,3 +2,4 @@ lib/ node_modules/ coverage/ renovate.json +tsconfig.json diff --git a/.eslintrc b/.eslintrc index 2e5076f..731c6be 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,7 +1,9 @@ { "root": true, - "extends": ["@shelf/eslint-config/backend"], + "extends": [ + "@shelf/eslint-config/typescript" + ], "rules": { - "no-console": 0 + "@typescript-eslint/no-var-requires": "off" } } diff --git a/.gitignore b/.gitignore index caecdfa..496d430 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .idea/ coverage/ node_modules/ -dist/ +lib/ yarn.lock *.log .DS_Store diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..ec6d3cd --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +package.json diff --git a/jest-preset.js b/jest-preset.js index 3dfc416..5102ee9 100644 --- a/jest-preset.js +++ b/jest-preset.js @@ -1,6 +1,3 @@ -const {resolve} = require('path'); +const preset = require('./lib'); -module.exports = { - globalSetup: resolve(__dirname, './setup.js'), - globalTeardown: resolve(__dirname, './teardown.js') -}; +module.exports = preset; diff --git a/license b/license new file mode 100644 index 0000000..98fd94c --- /dev/null +++ b/license @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) Gemshelf Inc. (shelf.io) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/package.json b/package.json index 4b33f32..ce13413 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@shelf/jest-elasticsearch", - "version": "4.0.0", + "version": "4.1.0", "description": "Run your tests using jest & elasticsearch-local", "keywords": [ "jest", @@ -20,25 +20,30 @@ "files": [ "jest-es-config.js", "jest-preset.js", - "setup.js", - "teardown.js" + "lib/" ], "scripts": { + "build": "rm -rf lib/ && yarn build:types && babel src --out-dir lib --ignore '**/*.test.ts' --extensions '.ts'", + "build:types": "tsc --emitDeclarationOnly --declaration --isolatedModules false --declarationDir lib", "coverage": "jest --coverage", - "lint": "eslint . --ext .js,.json --fix", - "lint:ci": "eslint . --ext .js,.json --quiet", - "test": "jest src" + "lint": "eslint . --ext .js,.ts,.json --fix", + "lint:ci": "eslint . --ext .js,.ts,.json", + "prepack": "yarn build", + "test": "export ENVIRONMENT=local && jest tests", + "type-check": "tsc --noEmit", + "type-check:watch": "npm run type-check -- --watch" }, "lint-staged": { - "*.js": [ - "eslint --fix", - "git add" + "*.{ts,js,}": [ + "eslint --fix" ], "*.{html,json,md,yml}": [ - "prettier --write", - "git add" + "prettier --write" ] }, + "babel": { + "extends": "@shelf/babel-config/backend" + }, "prettier": "@shelf/prettier-config", "jest": { "preset": "./jest-preset.js" @@ -48,14 +53,22 @@ "cwd": "0.10.0" }, "devDependencies": { + "@babel/cli": "7.18.9", + "@babel/core": "7.18.9", "@elastic/elasticsearch": "7.15.0", + "@shelf/babel-config": "1.2.0", "@shelf/eslint-config": "0.19.0", "@shelf/prettier-config": "0.0.7", + "@shelf/tsconfig": "0.0.8", + "@types/cwd": "^0.10.0", + "@types/jest": "28.1.3", + "@types/node": "16", "eslint": "7.26.0", "husky": "7.0.4", "jest": "28.1.3", "lint-staged": "12.1.2", - "prettier": "2.3.0" + "prettier": "2.3.0", + "typescript": "4.7.4" }, "engines": { "node": ">=16" diff --git a/renovate.json b/renovate.json index d834e6a..760913a 100644 --- a/renovate.json +++ b/renovate.json @@ -1,3 +1,13 @@ { - "extends": ["github>shelfio/renovate-config"] + "extends": [ + "config:base" + ], + "labels": [ + "dependencies", + "backend" + ], + "ignoreDeps": [ + "cimg/node", + "@types/node" + ] } diff --git a/src/elasticsearch.js b/src/elasticsearch.js deleted file mode 100644 index cb013fe..0000000 --- a/src/elasticsearch.js +++ /dev/null @@ -1,29 +0,0 @@ -const {Client} = require('@elastic/elasticsearch'); - -let client; - -function search(options) { - const es = getClient(); - - return es.search(options); -} - -async function refreshAllIndexes() { - const es = getClient(); - - return es.indices.refresh({index: '_all'}); -} - -function getClient() { - if (!client) { - if (!process.env.ES_URL) { - throw new Error('Please provide ES_URL env var first'); - } - - client = new Client({node: process.env.ES_URL, requestTimeout: 14 * 1000}); - } - - return client; -} - -module.exports = {search, refreshAllIndexes, getClient}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..632e721 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,6 @@ +import {resolve} from 'path'; + +module.exports = { + globalSetup: resolve(__dirname, './setup.js'), + globalTeardown: resolve(__dirname, './teardown.js') +}; diff --git a/src/insert-documents.js b/src/insert-documents.js deleted file mode 100644 index 222a5ae..0000000 --- a/src/insert-documents.js +++ /dev/null @@ -1,39 +0,0 @@ -const {refreshAllIndexes, getClient} = require('./elasticsearch'); - -module.exports = async function insertDocuments(documents) { - const body = generateTargetBody(documents); - await bulk(body, {index: 'documents'}); - await refreshAllIndexes(); -}; - -function generateTargetBody(documents) { - const targets = []; - documents.map(document => { - const body = [ - {update: {_index: 'documents', _id: document.id, routing: document.id}}, - { - doc: document, - doc_as_upsert: true - } - ]; - - return targets.push(...body); - }); - - return targets; -} - -async function bulk(body, {index} = {}) { - const client = getClient(); - const params = { - body - }; - - if (index) { - params.index = index; - } - - const {body: response} = await client.bulk(params); - - return response; -} diff --git a/setup.js b/src/setup.ts similarity index 64% rename from setup.js rename to src/setup.ts index f024d8b..43f8bf4 100644 --- a/setup.js +++ b/src/setup.ts @@ -1,6 +1,7 @@ -const {resolve} = require('path'); +import {resolve} from 'path'; +import {start} from '@shelf/elasticsearch-local'; + const cwd = require('cwd'); -const {start} = require('@shelf/elasticsearch-local'); module.exports = async function startES() { const config = require(resolve(cwd(), 'jest-es-config.js'))(); diff --git a/teardown.js b/src/teardown.ts similarity index 51% rename from teardown.js rename to src/teardown.ts index d7ad661..709a0be 100644 --- a/teardown.js +++ b/src/teardown.ts @@ -1,4 +1,4 @@ -const {stop} = require('@shelf/elasticsearch-local'); +import {stop} from '@shelf/elasticsearch-local'; module.exports = async function stopES() { stop(); diff --git a/src/elasticsearch.test.js b/tests/elasticsearch.test.ts similarity index 68% rename from src/elasticsearch.test.js rename to tests/elasticsearch.test.ts index 471c526..b4a0f46 100644 --- a/src/elasticsearch.test.js +++ b/tests/elasticsearch.test.ts @@ -1,4 +1,4 @@ -const {getClient} = require('./elasticsearch'); +import {getClient} from './elasticsearch'; it('should export getClient function', function () { expect(getClient).toBeInstanceOf(Function); diff --git a/tests/elasticsearch.ts b/tests/elasticsearch.ts new file mode 100644 index 0000000..0e8416f --- /dev/null +++ b/tests/elasticsearch.ts @@ -0,0 +1,34 @@ +import {Client} from '@elastic/elasticsearch'; +import { + ApiResponse, + TransportRequestOptions, + TransportRequestPromise +} from '@elastic/elasticsearch/lib/Transport'; + +let client: undefined | Client; + +export function search(options: TransportRequestOptions): TransportRequestPromise { + const es = getClient(); + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return es.search(options); +} + +export async function refreshAllIndexes(): Promise> { + const es = getClient(); + + return es.indices.refresh({index: '_all'}); +} + +export function getClient(): Client { + if (!client) { + if (!process.env.ES_URL) { + throw new Error('Please provide ES_URL env var first'); + } + + client = new Client({node: process.env.ES_URL, requestTimeout: 14 * 1000}); + } + + return client; +} diff --git a/tests/insert-helper.ts b/tests/insert-helper.ts new file mode 100644 index 0000000..c07a4f6 --- /dev/null +++ b/tests/insert-helper.ts @@ -0,0 +1,48 @@ +import {getClient, refreshAllIndexes} from './elasticsearch'; + +type Document = {id: string}; + +type Target = ( + | {update: {_index: string; _id: string; routing: string}} + | {doc: Document; doc_as_upsert: boolean} +)[]; + +export default async function insertDocuments(documents: Document[]): Promise { + const body = generateTargetBody(documents); + await bulk(body, {index: 'documents'}); + await refreshAllIndexes(); +} + +function generateTargetBody(documents: Document[]): Target { + const targets = [] as Target; + documents.map(document => { + const body = [ + {update: {_index: 'documents', _id: document.id, routing: document.id}}, + { + doc: document, + doc_as_upsert: true + } + ]; + + return targets.push(...body); + }); + + return targets; +} + +async function bulk(body: Target, {index}: {index?: string} = {}) { + const client = getClient(); + const params: {body: Target; index?: string} = { + body + }; + + if (index) { + params.index = index; + } + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + const {body: response} = await client.bulk(params); + + return response; +} diff --git a/src/documents.mock.js b/tests/mocks.ts similarity index 90% rename from src/documents.mock.js rename to tests/mocks.ts index a651907..f17eee3 100644 --- a/src/documents.mock.js +++ b/tests/mocks.ts @@ -1,4 +1,4 @@ -module.exports = [ +export default [ { id: 'some-doc-id-1', name: 'some-name-1' diff --git a/src/search-by-term.test.js b/tests/search-by-term.test.ts similarity index 81% rename from src/search-by-term.test.js rename to tests/search-by-term.test.ts index 578fc44..9995cbf 100644 --- a/src/search-by-term.test.js +++ b/tests/search-by-term.test.ts @@ -1,6 +1,6 @@ -const documents = require('./documents.mock'); -const getDocuments = require('./search-by-term'); -const insertDocuments = require('./insert-documents'); +import documents from './mocks'; +import getDocuments from './search-by-term'; +import insertDocuments from './insert-helper'; describe('getDocuments', () => { beforeAll(async () => { diff --git a/src/search-by-term.js b/tests/search-by-term.ts similarity index 50% rename from src/search-by-term.js rename to tests/search-by-term.ts index 1b0599c..4930b65 100644 --- a/src/search-by-term.js +++ b/tests/search-by-term.ts @@ -1,6 +1,19 @@ -const {search} = require('./elasticsearch'); +import {search} from './elasticsearch'; -module.exports = async function getDocuments({index, id, startFrom = 0}) { +type GetDocumentsResponse = { + items: Array<{[key: string]: string}>; + totalCount: number; +}; + +export default async function getDocuments({ + index, + id, + startFrom = 0 +}: { + index: string; + id: string; + startFrom: number; +}): Promise { const body = { _source: { includes: ['_id', 'name', 'id'] @@ -21,4 +34,4 @@ module.exports = async function getDocuments({index, id, startFrom = 0}) { } = await search({index, body}); return {items: hits, totalCount: total}; -}; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5bbaf96 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@shelf/tsconfig/backend", + "compilerOptions": { + "strict": true + }, + "exclude": ["node_modules"], + "include": ["src"] +}