From 1bcc0d331d749fe3f6920a7c0cd70b3d4175f553 Mon Sep 17 00:00:00 2001 From: Aschen Date: Tue, 14 Jul 2020 09:32:24 +0200 Subject: [PATCH 01/15] Prepare ts --- .eslintc-ts.json | 16 ++++ .eslintignore | 5 ++ index.js => index.ts | 0 package-lock.json | 178 ++++++++++++++++++++++++++++++++++++++++++- package.json | 18 ++++- 5 files changed, 210 insertions(+), 7 deletions(-) create mode 100644 .eslintc-ts.json create mode 100644 .eslintignore rename index.js => index.ts (100%) diff --git a/.eslintc-ts.json b/.eslintc-ts.json new file mode 100644 index 000000000..d1ec2e84a --- /dev/null +++ b/.eslintc-ts.json @@ -0,0 +1,16 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "@typescript-eslint" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "@typescript-eslint/no-explicit-any": 0, + "@typescript-eslint/explicit-module-boundary-types": 0 + } +} \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..70ad65b5c --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +# JS generated files from TS +src/**/*.d.ts +src/**/*.js.map + +src/controllers/*.js \ No newline at end of file diff --git a/index.js b/index.ts similarity index 100% rename from index.js rename to index.ts diff --git a/package-lock.json b/package-lock.json index b91454ce3..ac10372c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1339,6 +1339,18 @@ "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", "dev": true }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", + "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", + "dev": true + }, "@types/listr": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/@types/listr/-/listr-0.14.2.tgz", @@ -1350,11 +1362,125 @@ } }, "@types/node": { - "version": "13.11.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.11.1.tgz", - "integrity": "sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g==", + "version": "14.0.23", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.23.tgz", + "integrity": "sha512-Z4U8yDAl5TFkmYsZdFPdjeMa57NOvnaf1tljHzhouaPEp7LCj2JKkejpI1ODviIAQuW4CcQmxkQ77rnLsOOoKw==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.6.1.tgz", + "integrity": "sha512-06lfjo76naNeOMDl+mWG9Fh/a0UHKLGhin+mGaIw72FUMbMGBkdi/FEJmgEDzh4eE73KIYzHWvOCYJ0ak7nrJQ==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "3.6.1", + "debug": "^4.1.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.6.1.tgz", + "integrity": "sha512-oS+hihzQE5M84ewXrTlVx7eTgc52eu+sVmG7ayLfOhyZmJ8Unvf3osyFQNADHP26yoThFfbxcibbO0d2FjnYhg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/types": "3.6.1", + "@typescript-eslint/typescript-estree": "3.6.1", + "eslint-scope": "^5.0.0", + "eslint-utils": "^2.0.0" + }, + "dependencies": { + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.6.1.tgz", + "integrity": "sha512-SLihQU8RMe77YJ/jGTqOt0lMq7k3hlPVfp7v/cxMnXA9T0bQYoMDfTsNgHXpwSJM1Iq2aAJ8WqekxUwGv5F67Q==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "3.6.1", + "@typescript-eslint/types": "3.6.1", + "@typescript-eslint/typescript-estree": "3.6.1", + "eslint-visitor-keys": "^1.1.0" + } + }, + "@typescript-eslint/types": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-3.6.1.tgz", + "integrity": "sha512-NPxd5yXG63gx57WDTW1rp0cF3XlNuuFFB5G+Kc48zZ+51ZnQn9yjDEsjTPQ+aWM+V+Z0I4kuTFKjKvgcT1F7xQ==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.6.1.tgz", + "integrity": "sha512-G4XRe/ZbCZkL1fy09DPN3U0mR6SayIv1zSeBNquRFRk7CnVLgkC2ZPj8llEMJg5Y8dJ3T76SvTGtceytniaztQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "3.6.1", + "@typescript-eslint/visitor-keys": "3.6.1", + "debug": "^4.1.1", + "glob": "^7.1.6", + "is-glob": "^4.0.1", + "lodash": "^4.17.15", + "semver": "^7.3.2", + "tsutils": "^3.17.1" + }, + "dependencies": { + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-3.6.1.tgz", + "integrity": "sha512-qC8Olwz5ZyMTZrh4Wl3K4U6tfms0R/mzU4/5W3XeUZptVraGVmbptJbn6h2Ey6Rb3hOs3zWoAUebZk8t47KGiQ==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, "@webassemblyjs/ast": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", @@ -1697,6 +1823,12 @@ "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA=", "dev": true }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -6307,6 +6439,12 @@ "semver": "^5.6.0" } }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, "map-cache": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", @@ -9206,12 +9344,34 @@ "integrity": "sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A==", "dev": true }, + "ts-node": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.10.2.tgz", + "integrity": "sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA==", + "dev": true, + "requires": { + "arg": "^4.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, "tslib": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "integrity": "sha1-w8GflZc/sKYpc/sJ2Q2WHuQ+XIo=", "dev": true }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", @@ -9270,6 +9430,12 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.6.tgz", + "integrity": "sha512-Pspx3oKAPJtjNwE92YS05HQoY7z2SFyOpHo9MqJor3BXAGNaPUs83CuVp9VISFkSjyRfiTpmKuAYGJB7S7hOxw==", + "dev": true + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -9786,6 +9952,12 @@ "lodash": "^4.17.15", "yargs": "^13.3.0" } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true } } } diff --git a/package.json b/package.json index 654954987..3ceeae0e5 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,11 @@ "test": "npm run --silent lint && npm run unit-testing && npm run functional-testing", "test:unit": "nyc --reporter=text-summary --reporter=lcov mocha", "test:functional": "cucumber-js --exit --fail-fast", - "test:lint": "eslint --max-warnings=0 ./src ./test ./features", - "build": "node build.js", + "test:lint": "npm run test:lint:js && npm run test:lint:ts", + "test:lint:js": "eslint --max-warnings=0 index.js ./src ./test ./features", + "test:lint:ts": "eslint ./src --ext .ts --config .eslintc-ts.json", + "build": "npm run build-ts && node build.js", + "build-ts": "tsc --build tsconfig.json", "doc": "docker-compose -f doc/docker-compose.yml up", "doc-testing": "bash .ci/test-docs.sh", "doc-prepare": "kuzdoc framework:install", @@ -44,6 +47,9 @@ "devDependencies": { "@babel/core": "^7.9.0", "@babel/preset-env": "^7.9.5", + "@types/node": "^14.0.23", + "@typescript-eslint/eslint-plugin": "^3.6.1", + "@typescript-eslint/parser": "^3.6.1", "babel-loader": "^8.1.0", "codecov": "^3.7.0", "cucumber": "^6.0.5", @@ -61,14 +67,18 @@ "should": "13.2.3", "should-sinon": "0.0.6", "sinon": "^9.0.2", + "ts-node": "^8.10.2", + "typescript": "^3.9.6", "webpack": "^4.43.0" }, "engines": { "node": ">= 10.13.0" }, "files": [ - "src/**/*", + "dist/**/*", "index.js", - "dist/**/*" + "index.d.ts", + "src/**/*.js", + "src/**/*.d.ts" ] } From d9763caebb728dc1b66a63fba038c1c12c3bf671 Mon Sep 17 00:00:00 2001 From: Aschen Date: Tue, 14 Jul 2020 11:19:48 +0200 Subject: [PATCH 02/15] TS ok + Auth controller --- .eslintignore | 6 +- .gitignore | 11 +- doc/7/controllers/auth/check-token/index.md | 2 +- .../controllers/auth/create-api-key/index.md | 2 +- doc/7/controllers/auth/get-my-rights/index.md | 4 +- index.ts | 34 +- src/{Kuzzle.js => Kuzzle.ts} | 126 +++-- src/controllers/Auth.js | 358 ------------- src/controllers/Auth.ts | 502 ++++++++++++++++++ src/controllers/Base.js | 35 -- src/controllers/Base.ts | 44 ++ src/controllers/Bulk.js | 4 +- src/controllers/Collection.js | 6 +- src/controllers/Document.js | 4 +- src/controllers/Index.js | 4 +- src/controllers/MemoryStorage.js | 2 +- src/controllers/Realtime.js | 6 +- src/controllers/Security.js | 14 +- src/controllers/Server.js | 2 +- src/core/Jwt.js | 2 +- src/core/KuzzleEventEmitter.js | 2 +- src/core/Room.js | 2 +- src/core/searchResult/Document.js | 4 +- src/core/searchResult/Profile.js | 6 +- src/core/searchResult/Role.js | 4 +- src/core/searchResult/SearchResultBase.js | 2 +- src/core/searchResult/Specifications.js | 4 +- src/core/searchResult/User.js | 6 +- src/core/security/Profile.js | 4 +- src/core/security/{User.js => User.ts} | 30 +- src/protocols/abstract/Base.js | 6 +- src/protocols/abstract/Realtime.js | 2 +- src/utils/interfaces.ts | 20 + src/utils/proxify.js | 2 +- src/utils/uuidv4.js | 4 +- test/controllers/auth.test.js | 8 +- test/controllers/bulk.test.js | 2 +- test/controllers/collection.test.js | 4 +- test/controllers/document.test.js | 2 +- test/controllers/index.test.js | 2 +- test/controllers/memoryStorage.test.js | 2 +- test/controllers/realtime.test.js | 6 +- test/controllers/security.test.js | 12 +- test/controllers/server.test.js | 2 +- test/core/Jwt.test.js | 2 +- test/core/room.test.js | 2 +- test/core/searchResult/profile.test.js | 4 +- test/core/searchResult/role.test.js | 2 +- test/core/searchResult/specifications.test.js | 2 +- test/core/searchResult/user.test.js | 4 +- test/core/security/profile.test.js | 2 +- test/core/security/user.test.js | 2 +- test/kuzzle/connect.test.js | 6 +- test/kuzzle/constructor.test.js | 24 +- test/kuzzle/eventEmitter.test.js | 2 +- test/kuzzle/getters.test.js | 4 +- test/kuzzle/protocol.test.js | 2 +- test/kuzzle/proxy.test.js | 4 +- test/kuzzle/query.test.js | 2 +- test/kuzzle/queue.test.js | 2 +- test/kuzzle/setters.test.js | 4 +- test/kuzzle/useController.test.js | 4 +- test/mocks/protocol.mock.js | 2 +- test/mocks/window.mock.js | 2 +- test/utils/proxify.test.js | 2 +- 65 files changed, 826 insertions(+), 560 deletions(-) rename src/{Kuzzle.js => Kuzzle.ts} (86%) delete mode 100644 src/controllers/Auth.js create mode 100644 src/controllers/Auth.ts delete mode 100644 src/controllers/Base.js create mode 100644 src/controllers/Base.ts rename src/core/security/{User.js => User.ts} (52%) create mode 100644 src/utils/interfaces.ts diff --git a/.eslintignore b/.eslintignore index 70ad65b5c..e8697a479 100644 --- a/.eslintignore +++ b/.eslintignore @@ -2,4 +2,8 @@ src/**/*.d.ts src/**/*.js.map -src/controllers/*.js \ No newline at end of file +src/Kuzzle.js +src/controllers/Auth.js +src/controllers/Base.js +src/core/security/User.js +src/utils/interfaces.js diff --git a/.gitignore b/.gitignore index 3c3324c0e..3e9605a45 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,13 @@ doc/7/getting-started/.vuejs/cypress/screenshots doc/7/getting-started/.vuejs/cypress/videos # Debug snippets -test-*.js \ No newline at end of file +test-*.js + +# Typescript related files +*.d.ts +*.js.map +src/Kuzzle.js +src/controllers/Auth.js +src/controllers/Base.js +src/core/security/User.js +src/utils/interfaces.js diff --git a/doc/7/controllers/auth/check-token/index.md b/doc/7/controllers/auth/check-token/index.md index 896cf22f4..36c214a00 100644 --- a/doc/7/controllers/auth/check-token/index.md +++ b/doc/7/controllers/auth/check-token/index.md @@ -31,7 +31,7 @@ An `object` representing the token validity status | ------------- | ------------------ | --------------------------------- | | `valid` |
boolean
| Tell if the token is valid or not | | `state` |
string
| Explain why the token is invalid | -|  `expires_at` |
number
| Token expiration timestamp | +|  `expiresAt` |
number
| Token expiration timestamp | ## Usage diff --git a/doc/7/controllers/auth/create-api-key/index.md b/doc/7/controllers/auth/create-api-key/index.md index 3f21770ef..d7a2ca918 100644 --- a/doc/7/controllers/auth/create-api-key/index.md +++ b/doc/7/controllers/auth/create-api-key/index.md @@ -56,7 +56,7 @@ The API key content has the following properties: | Name | Type | Description | | --------- | ----------------- | ---------------- | | `userId` |
string
| User kuid | -| `expiresAt` |
number
| Aexpiration date in UNIX micro-timestamp format (`-1` if the token never expires) | +| `expiresAt` |
number
| Expiration date in UNIX micro-timestamp format (`-1` if the token never expires) | | `ttl` |
number
| Original TTL | | `description` |
string
| API key description | | `token` |
string
| Authentication token associated with this API key | diff --git a/doc/7/controllers/auth/get-my-rights/index.md b/doc/7/controllers/auth/get-my-rights/index.md index f8fa73bdb..6e338c44f 100644 --- a/doc/7/controllers/auth/get-my-rights/index.md +++ b/doc/7/controllers/auth/get-my-rights/index.md @@ -31,7 +31,9 @@ Additional query options ## Resolves -An `object[]` containing: +An array containing user rights objects. + +Each user right object has the following properties: | Property | Type | Description | | ------------- | ----------------- | ------------------------------------------- | diff --git a/index.ts b/index.ts index 123891a4e..26f7dd211 100644 --- a/index.ts +++ b/index.ts @@ -1,15 +1,5 @@ -const Kuzzle = require('./src/Kuzzle'); -const { Http, WebSocket } = require('./src/protocols'); -const BaseController = require('./src/controllers/Base'); -const KuzzleAbstractProtocol = require('./src/protocols/abstract/Base'); -const KuzzleEventEmitter = require('./src/core/KuzzleEventEmitter'); - -const SearchResultBase = require('./src/core/searchResult/SearchResultBase'); -const DocumentSearchResult = require('./src/core/searchResult/Document'); -const ProfileSearchResult = require('./src/core/searchResult/Profile'); -const RoleSearchResult = require('./src/core/searchResult/Role'); -const SpecificationSearchResult = require('./src/core/searchResult/Specifications'); -const UserSearchResult = require('./src/core/searchResult/User'); +// defined by webpack plugin +declare var BUILT: any; if (typeof window !== 'undefined' && typeof BUILT === 'undefined') { throw new Error('It looks like you are using the Nodejs version of Kuzzle SDK ' + @@ -18,7 +8,20 @@ if (typeof window !== 'undefined' && typeof BUILT === 'undefined') { 'Learn more at https://github.com/kuzzleio/sdk-javascript/tree/master#browser'); } -module.exports = { +import { Kuzzle } from './src/Kuzzle'; +import { Http, WebSocket } from './src/protocols'; +import * as BaseController from './src/controllers/Base'; +import * as KuzzleAbstractProtocol from './src/protocols/abstract/Base'; +import * as KuzzleEventEmitter from './src/core/KuzzleEventEmitter'; + +import * as SearchResultBase from './src/core/searchResult/SearchResultBase'; +import * as DocumentSearchResult from './src/core/searchResult/Document'; +import * as ProfileSearchResult from './src/core/searchResult/Profile'; +import * as RoleSearchResult from './src/core/searchResult/Role'; +import * as SpecificationSearchResult from './src/core/searchResult/Specifications'; +import * as UserSearchResult from './src/core/searchResult/User'; + +const exported = { Kuzzle, Http, WebSocket, @@ -31,4 +34,7 @@ module.exports = { RoleSearchResult, SpecificationSearchResult, UserSearchResult -}; +} + +export default exported; + diff --git a/src/Kuzzle.js b/src/Kuzzle.ts similarity index 86% rename from src/Kuzzle.js rename to src/Kuzzle.ts index 24e0b2b8b..1dce88107 100644 --- a/src/Kuzzle.js +++ b/src/Kuzzle.ts @@ -1,37 +1,91 @@ -const KuzzleEventEmitter = require('./core/KuzzleEventEmitter'); -const AuthController = require('./controllers/Auth'); -const BulkController = require('./controllers/Bulk'); -const CollectionController = require('./controllers/Collection'); -const DocumentController = require('./controllers/Document'); -const IndexController = require('./controllers/Index'); -const RealtimeController = require('./controllers/Realtime'); -const ServerController = require('./controllers/Server'); -const SecurityController = require('./controllers/Security'); -const MemoryStorageController = require('./controllers/MemoryStorage'); -const uuidv4 = require('./utils/uuidv4'); -const proxify = require('./utils/proxify'); - -const - events = [ - 'connected', - 'discarded', - 'disconnected', - 'loginAttempt', - 'networkError', - 'offlineQueuePush', - 'offlineQueuePop', - 'queryError', - 'reconnected', - 'tokenExpired' - ]; - -class Kuzzle extends KuzzleEventEmitter { +import { KuzzleEventEmitter } from './core/KuzzleEventEmitter'; + +import { AuthController } from './controllers/Auth'; +import { BulkController } from './controllers/Bulk'; +import { CollectionController } from './controllers/Collection'; +import { DocumentController } from './controllers/Document'; +import { IndexController } from './controllers/Index'; +import { RealtimeController } from './controllers/Realtime'; +import { ServerController } from './controllers/Server'; +import { SecurityController } from './controllers/Security'; +import { MemoryStorageController } from './controllers/MemoryStorage'; + +import { uuidv4 } from './utils/uuidv4'; +import { proxify } from './utils/proxify'; +import { JSONObject } from './utils/interfaces'; + +// defined by webpack plugin +declare var SDKVERSION: any; + +const events = [ + 'connected', + 'discarded', + 'disconnected', + 'loginAttempt', + 'networkError', + 'offlineQueuePush', + 'offlineQueuePop', + 'queryError', + 'reconnected', + 'tokenExpired' +]; + +export class Kuzzle extends KuzzleEventEmitter { + /** + * Protocol used by the SDK to communicate with Kuzzle. + */ + public protocol: any; + /** + * If true, automatically renews all subscriptions on a reconnected event. + */ + public autoResubscribe: boolean; + /** + * Timeout before sending again a similar event. + */ + public eventTimeout: number; + /** + * SDK version. + */ + public sdkVersion: string; + /** + * SDK name (e.g: js@7.4.2). + */ + public sdkName: string; + /** + * Common volatile data that will be sent to all future requests. + */ + public volatile: JSONObject; + + public auth: AuthController; + public bulk: any; + public collection: any; + public document: any; + public index: any; + public ms: any; + public realtime: any; + public security: any; + public server: any; + + private _protectedEvents: any; + private _offlineQueue: any; + private _autoQueue: any; + private _autoReplay: any; + private _offlineQueueLoader: any; + private _queuing: boolean; + private _queueFilter: any; + private _queueMaxSize: any; + private _queueTTL: any; + private _replayInterval: any; + private _tokenExpiredInterval: any; + private _lastTokenExpired: any; + + private __proxy__: any; /** * @param protocol - the protocol to use * @param [options] - Kuzzle options */ - constructor(protocol, options = {}) { + constructor(protocol: any, options: any = {}) { super(); if (protocol === undefined || protocol === null) { @@ -115,7 +169,7 @@ class Kuzzle extends KuzzleEventEmitter { this._autoQueue = true; this._autoReplay = true; } - this.queuing = false; + this._queuing = false; this._lastTokenExpired = null; @@ -123,7 +177,7 @@ class Kuzzle extends KuzzleEventEmitter { seal: true, name: 'kuzzle', exposeApi: true - }); + }) as Kuzzle; } get authenticated () { @@ -392,7 +446,7 @@ class Kuzzle extends KuzzleEventEmitter { * @param {object} [options] - Optional arguments * @returns {Promise} */ - query (request = {}, options = {}) { + query (request: any = {}, options: any = {}) { if (typeof request !== 'object' || Array.isArray(request)) { throw new Error(`Kuzzle.query: Invalid request: ${JSON.stringify(request)}`); } @@ -438,7 +492,7 @@ class Kuzzle extends KuzzleEventEmitter { queuable = queuable && this.queueFilter(request); } - if (this.queuing) { + if (this._queuing) { if (queuable) { this._cleanQueue(); this.emit('offlineQueuePush', {request}); @@ -464,7 +518,7 @@ Discarded request: ${JSON.stringify(request)}`)); * Starts the requests queuing. */ startQueuing () { - this.queuing = true; + this._queuing = true; return this; } @@ -472,7 +526,7 @@ Discarded request: ${JSON.stringify(request)}`)); * Stops the requests queuing. */ stopQueuing () { - this.queuing = false; + this._queuing = false; return this; } @@ -633,4 +687,4 @@ Discarded request: ${JSON.stringify(request)}`)); } -module.exports = Kuzzle; +module.exports = { Kuzzle }; diff --git a/src/controllers/Auth.js b/src/controllers/Auth.js deleted file mode 100644 index 0dcb9a0e5..000000000 --- a/src/controllers/Auth.js +++ /dev/null @@ -1,358 +0,0 @@ -const Jwt = require('../core/Jwt'); -const BaseController = require('./Base'); -const User = require('../core/security/User'); - -/** - * Auth controller - * - * @param kuzzle - * @constructor - */ -class AuthController extends BaseController { - - /** - * constructor - * @param kuzzle - */ - constructor (kuzzle) { - super(kuzzle, 'auth'); - - this._authenticationToken = null; - - this.kuzzle.on('tokenExpired', () => { - this._authenticationToken = null; - }); - } - - get authenticationToken () { - return this._authenticationToken; - } - - set authenticationToken (encodedJwt) { - if (encodedJwt === undefined || encodedJwt === null) { - this._authenticationToken = null; - } - else if (typeof encodedJwt === 'string') { - this._authenticationToken = new Jwt(encodedJwt); - } - else { - throw new Error(`Invalid token argument: ${encodedJwt}`); - } - } - - /** - * Do not add the token for the checkToken route, to avoid getting a token error when - * a developer simply wish to verify his token - * - * @param {object} request - */ - authenticateRequest (request) { - if ( !this.authenticationToken - || (request.controller === 'auth' - && (request.action === 'checkToken' || request.action === 'login')) - ) { - return; - } - - request.jwt = this.authenticationToken.encodedJwt; - } - - /** - * Creates a new API key for the currently loggued user. - * - * @param {String} description - API key description - * @param {Object} [options] - { _id, expiresIn, refresh } - * - * @returns {Promise.} ApiKey { _id, _source } - */ - createApiKey(description, options = {}) { - const request = { - action: 'createApiKey', - _id: options._id, - expiresIn: options.expiresIn, - refresh: options.refresh, - body: { - description - } - }; - - return this.query(request) - .then(response => response.result); - } - - /** - * Deletes an API key for the currently loggued user. - * - * @param {String} id - API key ID - * @param {Object} [options] - { refresh } - * - * @returns {Promise} - */ - deleteApiKey(id, options = {}) { - const request = { - action: 'deleteApiKey', - _id: id, - refresh: options.refresh - }; - - return this.query(request) - .then(() => {}); - } - - /** - * Searches API keys for the currently loggued user. - * - * @param {Object} [query] - Search query - * @param {Object} [options] - { from, size } - * - * @returns {Promise.} - { hits, total } - */ - searchApiKeys(query = {}, options = {}) { - const request = { - action: 'searchApiKeys', - from: options.from, - size: options.size, - body: query - }; - - return this.query(request) - .then(response => response.result); - } - - /** - * Checks whether a given jwt token still represents a valid session in Kuzzle. - * - * @param {string} token The jwt token to check - * @return {Promise|*|PromiseLike|Promise} - */ - checkToken (token) { - if (token === undefined && this.authenticationToken) { - token = this.authenticationToken.encodedJwt; - } - - return this.query({ - action: 'checkToken', - body: { token } - }, { queuable: false }) - .then(response => response.result); - } - - /** - * Create credentials of the specified for the current user. - * - * @param credentials - * @param strategy - * @param options - * @returns {Promise|*|PromiseLike|Promise} - */ - createMyCredentials (strategy, credentials, options = {}) { - return this.query({ - strategy, - action: 'createMyCredentials', - body: credentials - }, options) - .then(response => response.result); - } - - /** - * Check the existence of the specified 's credentials for the current user. - * - * @param strategy - * @returns {Promise|*|PromiseLike|Promise} - */ - credentialsExist (strategy, options = {}) { - return this.query({ - strategy, - action: 'credentialsExist' - }, options) - .then(response => response.result); - } - - /** - * Delete credentials of the specified for the current user. - * - * @param strategy - * @param options - * @returns {Promise|*|PromiseLike|Promise} - */ - deleteMyCredentials (strategy, options = {}) { - return this.query({ - strategy, - action: 'deleteMyCredentials' - }, options) - .then(response => response.result.acknowledged); - } - - /** - * Fetches the current user. - * - * @returns {Promise|*|PromiseLike|Promise} - */ - getCurrentUser (options = {}) { - return this.query({ - action: 'getCurrentUser' - }, options) - .then(response => new User(this.kuzzle, response.result._id, response.result._source)); - } - - /** - * Get credential information of the specified for the current user. - * - * @param strategy - * @returns {Promise|*|PromiseLike|Promise} - */ - getMyCredentials(strategy, options = {}) { - return this.query({ - strategy, - action: 'getMyCredentials' - }, options) - .then(response => response.result); - } - - /** - * Gets the rights array of the currently logged user. - * - * @param {object} [options] - Optional parameters - * @returns {Promise|*|PromiseLike|Promise} - */ - getMyRights (options = {}) { - return this.query({ - action: 'getMyRights' - }, options) - .then(response => response.result.hits); - } - - /** - * Get all the strategies registered in Kuzzle by all auth plugins - * - * @param {object} [options] - Optional parameters - * @returns {Promise|*|PromiseLike|Promise} - */ - getStrategies (options = {}) { - return this.query({ - action: 'getStrategies' - }, options) - .then(response => response.result); - } - - /** - * Send login request to kuzzle with credentials - * If login success, store the jwt into kuzzle object - * - * @param strategy - * @param credentials - * @param expiresIn - * @returns {Promise|*|PromiseLike|Promise} - */ - login (strategy, credentials = {}, expiresIn = null) { - const request = { - strategy, - expiresIn, - body: credentials, - action: 'login' - }; - - return this.query(request, {queuable: false, verb: 'POST'}) - .then(response => { - try { - this._authenticationToken = new Jwt(response.result.jwt); - - this.kuzzle.emit('loginAttempt', {success: true}); - } - catch (err) { - return Promise.reject(err); - } - - return response.result.jwt; - }) - .catch(err => { - this.kuzzle.emit('loginAttempt', {success: false, error: err.message}); - throw err; - }); - } - - /** - * Send logout request to kuzzle with jwt. - * - * @returns {Promise|*|PromiseLike|Promise} - */ - logout () { - return this.query({ - action: 'logout' - }, {queuable: false}) - .then(() => { - this._authenticationToken = null; - }); - } - - /** - * Update credentials of the specified for the current user. - * - * @param strategy - * @param credentals - * @param options - * @returns {Promise|*|PromiseLike|Promise} - */ - updateMyCredentials (strategy, credentials, options = {}) { - return this.query({ - strategy, - body: credentials, - action: 'updateMyCredentials' - }, options) - .then(response => response.result); - } - - /** - * Update current user in Kuzzle. - * - * @param {object} body - a plain javascript object representing the user's modification - * @param {object} [options] - (optional) arguments - * @returns {Promise|*|PromiseLike|Promise} - */ - updateSelf (body, options = {}) { - return this.query({ - body, - action: 'updateSelf' - }, options) - .then(response => new User(this.kuzzle, response.result._id, response.result._source)); - } - - /** - * Validate credentials of the specified for the current user. - * - * @param strategy - * @param credentials - * @param options - * @returns {Promise|*|PromiseLike|Promise} - */ - validateMyCredentials (strategy, credentials, options = {}) { - return this.query({ - strategy, - body: credentials, - action: 'validateMyCredentials' - }, options) - .then(response => response.result); - } - - /** - * Refresh an authentication token - * - * @param {Object} options - * @returns {Promise.} - */ - refreshToken(options = {}) { - const query = { - action: 'refreshToken', - expiresIn: options.expiresIn - }; - - return this.query(query, options) - .then(response => { - this._authenticationToken = new Jwt(response.result.jwt); - - return response.result; - }); - } -} - -module.exports = AuthController; diff --git a/src/controllers/Auth.ts b/src/controllers/Auth.ts new file mode 100644 index 000000000..7825fd521 --- /dev/null +++ b/src/controllers/Auth.ts @@ -0,0 +1,502 @@ +import { Jwt } from '../core/Jwt'; +import { BaseController } from './Base'; +import { User } from '../core/security/User'; +import { JSONObject } from '../utils/interfaces'; + +export interface ApiKey { + /** + * ApiKey unique ID + */ + _id: string; + /** + * ApiKey content + */ + _source: { + /** + * User kuid + */ + userId: string; + /** + * Expiration date in UNIX micro-timestamp format (-1 if the token never expires) + */ + expiresAt: number; + /** + * Original TTL in ms + */ + ttl: number; + /** + * API key description + */ + description: string; + /** + * Authentication token associated with this API key + */ + token: string; + } +} + +/** + * Auth controller + * + * @param kuzzle + * @constructor + */ +export class AuthController extends BaseController { + private _authenticationToken: any | null; + + /** + * constructor + * @param kuzzle + */ + constructor (kuzzle) { + super(kuzzle, 'auth'); + + this._authenticationToken = null; + + this.kuzzle.on('tokenExpired', () => { + this._authenticationToken = null; + }); + } + + /** + * Authentication token in use + */ + get authenticationToken (): any | null { + return this._authenticationToken; + } + + set authenticationToken (encodedJwt: any) { + if (encodedJwt === undefined || encodedJwt === null) { + this._authenticationToken = null; + } + else if (typeof encodedJwt === 'string') { + this._authenticationToken = new Jwt(encodedJwt); + } + else { + throw new Error(`Invalid token argument: ${encodedJwt}`); + } + } + + /** + * Do not add the token for the checkToken route, to avoid getting a token error when + * a developer simply wish to verify his token + */ + authenticateRequest (request: any) { + if ( !this.authenticationToken + || (request.controller === 'auth' + && (request.action === 'checkToken' || request.action === 'login')) + ) { + return; + } + + request.jwt = this.authenticationToken.encodedJwt; + } + + /** + * Creates a new API key for the currently loggued user. + * + * @param description API key description + * @param options Additional options + * - "_id" API key unique ID + * - "refresh" If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * - "expiresIn" Expiration duration + * + * @returns The created API key + */ + createApiKey( + description: string, + options: { _id?: string, expiresIn?: number, refresh?: string } = {} + ) { + const request = { + action: 'createApiKey', + _id: options._id, + expiresIn: options.expiresIn, + refresh: options.refresh, + body: { + description + } + }; + + return this.query(request) + .then(response => response.result as ApiKey); + } + + /** + * Deletes an API key for the currently loggued user. + * + * @param id API key ID + * @param options Additional options + * - "refresh" If set to `wait_for`, Kuzzle will not respond until the API key is indexed + */ + deleteApiKey(id: string, options: { refresh?: string } = {}) { + const request = { + action: 'deleteApiKey', + _id: id, + refresh: options.refresh + }; + + return this.query(request) + .then(() => null); + } + + /** + * Searches API keys for the currently loggued user. + * + * @param query Search query + * @param options Additional options + * - "from" Offset of the first document to fetch + * - "size" Maximum number of documents to retrieve per page + * + * @returns A search result object + */ + searchApiKeys( + query: JSONObject = {}, + options: { from?: number, size?: number } = {} + ) { + const request = { + action: 'searchApiKeys', + from: options.from, + size: options.size, + body: query + }; + + return this.query(request) + .then(response => response.result as { + /** + * Array of found ApiKeys + */ + hits: ApiKey[], + /** + * Total number of API keys found + */ + total: number + }); + } + + /** + * Checks whether a given jwt token still represents a valid session in Kuzzle. + * + * @param token The jwt token to check (default to current SDK token) + * + * @returns A token validity object + */ + checkToken (token?: string) { + if (token === undefined && this.authenticationToken) { + token = this.authenticationToken.encodedJwt; + } + + return this.query({ + action: 'checkToken', + body: { token } + }, { queuable: false }) + .then(response => response.result as { + /** + * Tell if the token is valid or not + */ + valid: boolean, + /** + * Explain why the token is invalid + */ + state: string, + /** + * Token expiration timestamp + */ + expiresAt: number + }); + } + + /** + * Create credentials of the specified strategy for the current user. + * + * @param strategy New credentials + * @param credentials Name of the strategy to use + * @param options Additional options + * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns An object representing the new credentials. + * The content depends on the authentication strategy + */ + createMyCredentials ( + strategy: string, + credentials: JSONObject, + options: { queuable?: boolean } = {} + ) { + return this.query({ + strategy, + action: 'createMyCredentials', + body: credentials + }, options) + .then(response => response.result as JSONObject); + } + + /** + * Check the existence of the specified strategy's credentials for the current user. + * + * @param strategy Name of the strategy to use + * @param options Additional options + * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns A boolean indicating if the credentials exists + */ + credentialsExist (strategy: string, options: { queuable?: boolean } = {}) { + return this.query({ + strategy, + action: 'credentialsExist' + }, options) + .then(response => response.result as boolean); + } + + /** + * Delete credentials of the specified strategy for the current user. + * + * @param strategy Name of the strategy to use + * @param options Additional options + * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + */ + deleteMyCredentials (strategy: string, options: { queuable?: boolean } = {}) { + return this.query({ + strategy, + action: 'deleteMyCredentials' + }, options) + .then(response => response.result.acknowledged as boolean); + } + + /** + * Fetches the current user. + * @param options Additional options + * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns Currently loggued User + */ + getCurrentUser (options: { queuable?: boolean } = {}) { + return this.query({ + action: 'getCurrentUser' + }, options) + .then(response => new User( + this.kuzzle, + response.result._id, + response.result._source)); + } + + /** + * Get credential information of the specified strategy for the current user. + * + * @param strategy Name of the strategy to use + * @param options Additional options + * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns An object representing the credentials for the provided authentication strategy. + * Its content depends on the authentication strategy. + */ + getMyCredentials(strategy, options: { queuable?: boolean } = {}) { + return this.query({ + strategy, + action: 'getMyCredentials' + }, options) + .then(response => response.result as JSONObject); + } + + /** + * Gets the rights array of the currently logged user. + * + * @param options Additional options + * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns An array containing user rights objects + */ + getMyRights (options: { queuable?: boolean } = {}) { + return this.query({ + action: 'getMyRights' + }, options) + .then(response => response.result.hits as { + /** + * Controller on wich the rights are applied + */ + controller: string, + /** + * Action on wich the rights are applied + */ + action: string, + /** + * Index on wich the rights are applied + */ + index: string, + /** + * Collection on wich the rights are applied + */ + collection: string, + /** + * Rights ("allowed" or "denied") + */ + value: string + }[]); + } + + /** + * Get all the strategies registered in Kuzzle by all auth plugins + * + * @param options Additional options + * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns An array of available strategies names + */ + getStrategies (options: { queuable?: boolean } = {}) { + return this.query({ + action: 'getStrategies' + }, options) + .then(response => response.result as string[]); + } + + /** + * Send login request to kuzzle with credentials + * If login success, store the jwt into kuzzle object + * + * @param strategy Name of the strategy to use + * @param credentials Credentials object for the strategy + * @param expiresIn Expiration time in ms library format. (e.g. "2h") + * + * @returns The encrypted JSON Web Token + */ + login (strategy: string, credentials: JSONObject, expiresIn: string | null = null) { + const request = { + strategy, + expiresIn, + body: credentials, + action: 'login' + }; + + return this.query(request, {queuable: false, verb: 'POST'}) + .then(response => { + try { + this._authenticationToken = new Jwt(response.result.jwt); + + this.kuzzle.emit('loginAttempt', {success: true}); + } + catch (err) { + return Promise.reject(err); + } + + return response.result.jwt as string; + }) + .catch(err => { + this.kuzzle.emit('loginAttempt', {success: false, error: err.message}); + throw err; + }); + } + + /** + * Send logout request to kuzzle with jwt. + */ + logout () { + return this.query({ + action: 'logout' + }, {queuable: false}) + .then(() => { + this._authenticationToken = null; + }); + } + + /** + * Update credentials of the specified strategy for the current user. + * + * @param strategy Name of the strategy to use + * @param credentials Updated credentials + * @param options Additional options + * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns An object representing the updated credentials. + * The content depends on the authentication strategy + */ + updateMyCredentials ( + strategy: string, + credentials: JSONObject, + options: { queuable?: boolean } = {} + ) { + return this.query({ + strategy, + body: credentials, + action: 'updateMyCredentials' + }, options) + .then(response => response.result as JSONObject); + } + + /** + * Update current user in Kuzzle. + * This route cannot update the list of associated security profiles. + * + * @param {object} content - User custom information + * @param options Additional options + * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns Currently loggued User + */ + updateSelf (content: JSONObject, options: { queuable?: boolean } = {}) { + return this.query({ + body: content, + action: 'updateSelf' + }, options) + .then(response => new User( + this.kuzzle, + response.result._id, + response.result._source)); + } + + /** + * Validate credentials of the specified strategy for the current user. + * + * @param strategy Name of the strategy to use + * @param credentials Credentials to validate + * @param options Additional options + * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + */ + validateMyCredentials (strategy, credentials, options: { queuable?: boolean } = {}) { + return this.query({ + strategy, + body: credentials, + action: 'validateMyCredentials' + }, options) + .then(response => response.result as boolean); + } + + /** + * Refresh the SDK current authentication token + * + * @param options Additional options + * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - "expiresIn" Expiration duration + * + * @returns The refreshed token + */ + refreshToken(options: { queuable?: boolean, expiresIn?: number } = {}) { + const query = { + action: 'refreshToken', + expiresIn: options.expiresIn + }; + + return this.query(query, options) + .then(response => { + this._authenticationToken = new Jwt(response.result.jwt); + + return response.result as { + /** + * Token unique ID + */ + _id: string; + /** + * Expiration date in UNIX micro-timestamp format (-1 if the token never expires) + */ + expiresAt: number; + /** + * Authentication token associated with this API key + */ + jwt: string; + /** + * Original TTL in ms + */ + ttl: number; + }; + }); + } +} + +module.exports = { AuthController }; diff --git a/src/controllers/Base.js b/src/controllers/Base.js deleted file mode 100644 index 9aa3a8f0e..000000000 --- a/src/controllers/Base.js +++ /dev/null @@ -1,35 +0,0 @@ -class BaseController { - - /** - * @param {Kuzzle} kuzzle - Kuzzle SDK object. - * @param {string} name - Controller full name for API request. - */ - constructor (kuzzle, name) { - Reflect.defineProperty(this, '_kuzzle', { - value: kuzzle - }); - - this._name = name; - } - - get kuzzle () { - return this._kuzzle; - } - - get name () { - return this._name; - } - - /** - * @param {object} request - * @param {object} [options] - Optional arguments - * @returns {Promise} - */ - query (request = {}, options = {}) { - request.controller = request.controller || this.name; - - return this._kuzzle.query(request, options); - } -} - -module.exports = BaseController; diff --git a/src/controllers/Base.ts b/src/controllers/Base.ts new file mode 100644 index 000000000..b67d63f58 --- /dev/null +++ b/src/controllers/Base.ts @@ -0,0 +1,44 @@ +import { KuzzleRequest, JSONObject } from '../utils/interfaces'; + +export class BaseController { + private _name: string; + private _kuzzle: any; + + /** + * @param {Kuzzle} kuzzle - Kuzzle SDK object. + * @param {string} name - Controller full name for API request. + */ + constructor (kuzzle: any, name: string) { + Reflect.defineProperty(this, '_kuzzle', { + value: kuzzle + }); + + this._name = name; + } + + protected get kuzzle () { + return this._kuzzle; + } + + /** + * Controller name + */ + get name () { + return this._name; + } + + /** + * Sends a query to Kuzzle. + * If not set, the "controller" property of the request will be the controller name. + * + * @param request + * @param options + */ + query (request: KuzzleRequest = {}, options: any = {}): Promise { + request.controller = request.controller || this.name; + + return this._kuzzle.query(request, options); + } +} + +module.exports = { BaseController }; diff --git a/src/controllers/Bulk.js b/src/controllers/Bulk.js index 2c48f1ce9..d46a6fdc0 100644 --- a/src/controllers/Bulk.js +++ b/src/controllers/Bulk.js @@ -1,4 +1,4 @@ -const BaseController = require('./Base'); +const { BaseController } = require('./Base'); class BulkController extends BaseController { constructor (kuzzle) { @@ -95,4 +95,4 @@ class BulkController extends BaseController { } -module.exports = BulkController; +module.exports = { BulkController }; diff --git a/src/controllers/Collection.js b/src/controllers/Collection.js index c3ed130de..97727ab6b 100644 --- a/src/controllers/Collection.js +++ b/src/controllers/Collection.js @@ -1,5 +1,5 @@ -const BaseController = require('./Base'); -const SpecificationsSearchResult = require('../core/searchResult/Specifications'); +const { BaseController } = require('./Base'); +const { SpecificationsSearchResult } = require('../core/searchResult/Specifications'); class CollectionController extends BaseController { @@ -148,4 +148,4 @@ class CollectionController extends BaseController { } } -module.exports = CollectionController; +module.exports = { CollectionController }; diff --git a/src/controllers/Document.js b/src/controllers/Document.js index 629391cb0..debd4a798 100644 --- a/src/controllers/Document.js +++ b/src/controllers/Document.js @@ -1,4 +1,4 @@ -const BaseController = require('./Base'); +const { BaseController } = require('./Base'); const DocumentSearchResult = require('../core/searchResult/Document'); class DocumentController extends BaseController { @@ -251,4 +251,4 @@ class DocumentController extends BaseController { } } -module.exports = DocumentController; +module.exports = { DocumentController }; diff --git a/src/controllers/Index.js b/src/controllers/Index.js index b073d03ea..626d8c949 100644 --- a/src/controllers/Index.js +++ b/src/controllers/Index.js @@ -1,4 +1,4 @@ -const BaseController = require('./Base'); +const { BaseController } = require('./Base'); class IndexController extends BaseController { @@ -55,4 +55,4 @@ class IndexController extends BaseController { } } -module.exports = IndexController; +module.exports = { IndexController }; diff --git a/src/controllers/MemoryStorage.js b/src/controllers/MemoryStorage.js index 9b77acaeb..36d404adf 100644 --- a/src/controllers/MemoryStorage.js +++ b/src/controllers/MemoryStorage.js @@ -465,4 +465,4 @@ function mapScanResults (results) { }; } -module.exports = MemoryStorageController; +module.exports = { MemoryStorageController }; diff --git a/src/controllers/Realtime.js b/src/controllers/Realtime.js index e2847e01b..e68478ba2 100644 --- a/src/controllers/Realtime.js +++ b/src/controllers/Realtime.js @@ -1,8 +1,8 @@ -const BaseController = require('./Base'); +const { BaseController } = require('./Base'); const Room = require('../core/Room'); -class RealTimeController extends BaseController { +class RealtimeController extends BaseController { /** * @param {Kuzzle} kuzzle */ @@ -125,4 +125,4 @@ class RealTimeController extends BaseController { } -module.exports = RealTimeController; +module.exports = { RealtimeController }; diff --git a/src/controllers/Security.js b/src/controllers/Security.js index a04f3ed8d..85ce9eac7 100644 --- a/src/controllers/Security.js +++ b/src/controllers/Security.js @@ -1,10 +1,10 @@ -const BaseController = require('./Base'); +const { BaseController } = require('./Base'); const Role = require('../core/security/Role'); -const RoleSearchResult = require('../core/searchResult/Role'); -const Profile = require('../core/security/Profile'); -const ProfileSearchResult = require('../core/searchResult/Profile'); -const User = require('../core/security/User'); -const UserSearchResult = require('../core/searchResult/User'); +const { RoleSearchResult } = require('../core/searchResult/Role'); +const { Profile } = require('../core/security/Profile'); +const { ProfileSearchResult } = require('../core/searchResult/Profile'); +const { User } = require('../core/security/User'); +const { UserSearchResult } = require('../core/searchResult/User'); class SecurityController extends BaseController { /** @@ -512,4 +512,4 @@ class SecurityController extends BaseController { } } -module.exports = SecurityController; +module.exports = { SecurityController }; diff --git a/src/controllers/Server.js b/src/controllers/Server.js index ece80e71e..2e94c1277 100644 --- a/src/controllers/Server.js +++ b/src/controllers/Server.js @@ -110,4 +110,4 @@ class ServerController extends BaseControler { } } -module.exports = ServerController; +module.exports = { ServerController }; diff --git a/src/core/Jwt.js b/src/core/Jwt.js index 9899323cf..5cdde4321 100644 --- a/src/core/Jwt.js +++ b/src/core/Jwt.js @@ -71,4 +71,4 @@ class Jwt { } } -module.exports = Jwt; +module.exports = { Jwt }; diff --git a/src/core/KuzzleEventEmitter.js b/src/core/KuzzleEventEmitter.js index d4dd8205d..e88ee825c 100644 --- a/src/core/KuzzleEventEmitter.js +++ b/src/core/KuzzleEventEmitter.js @@ -142,4 +142,4 @@ class KuzzleEventEmitter { } } -module.exports = KuzzleEventEmitter; +module.exports = { KuzzleEventEmitter }; diff --git a/src/core/Room.js b/src/core/Room.js index 7f6b30937..ddf0c4af4 100644 --- a/src/core/Room.js +++ b/src/core/Room.js @@ -1,7 +1,7 @@ class Room { /** - * @param {RealTimeController} controller + * @param {RealtimeController} controller * @param {string} index * @param {string} collection * @param {object} body diff --git a/src/core/searchResult/Document.js b/src/core/searchResult/Document.js index 8a175be77..f4104eedc 100644 --- a/src/core/searchResult/Document.js +++ b/src/core/searchResult/Document.js @@ -1,4 +1,4 @@ -const SearchResultBase = require('./SearchResultBase'); +const { SearchResultBase } = require('./SearchResultBase'); class DocumentsSearchResult extends SearchResultBase { @@ -16,4 +16,4 @@ class DocumentsSearchResult extends SearchResultBase { } } -module.exports = DocumentsSearchResult; +module.exports = { DocumentsSearchResult }; diff --git a/src/core/searchResult/Profile.js b/src/core/searchResult/Profile.js index be27cd96d..ac7de860b 100644 --- a/src/core/searchResult/Profile.js +++ b/src/core/searchResult/Profile.js @@ -1,5 +1,5 @@ -const Profile = require('../security/Profile'); -const SearchResultBase = require('./SearchResultBase'); +const { Profile } = require('../security/Profile'); +const { SearchResultBase } = require('./SearchResultBase'); class ProfileSearchResult extends SearchResultBase { constructor (kuzzle, request, options, response) { @@ -26,4 +26,4 @@ class ProfileSearchResult extends SearchResultBase { } } -module.exports = ProfileSearchResult; +module.exports = { ProfileSearchResult }; diff --git a/src/core/searchResult/Role.js b/src/core/searchResult/Role.js index d03574051..3f5ad2228 100644 --- a/src/core/searchResult/Role.js +++ b/src/core/searchResult/Role.js @@ -1,5 +1,5 @@ const Role = require('../security/Role'); -const SearchResultBase = require('./SearchResultBase'); +const { SearchResultBase } = require('./SearchResultBase'); class RoleSearchResult extends SearchResultBase { @@ -32,4 +32,4 @@ class RoleSearchResult extends SearchResultBase { } } -module.exports = RoleSearchResult; +module.exports = { RoleSearchResult }; diff --git a/src/core/searchResult/SearchResultBase.js b/src/core/searchResult/SearchResultBase.js index fca170b93..b9c8af7f2 100644 --- a/src/core/searchResult/SearchResultBase.js +++ b/src/core/searchResult/SearchResultBase.js @@ -129,4 +129,4 @@ class SearchResultBase { } -module.exports = SearchResultBase; +module.exports = { SearchResultBase }; diff --git a/src/core/searchResult/Specifications.js b/src/core/searchResult/Specifications.js index c0c167d28..15d2d2410 100644 --- a/src/core/searchResult/Specifications.js +++ b/src/core/searchResult/Specifications.js @@ -1,4 +1,4 @@ -const SearchResultBase = require('./SearchResultBase'); +const { SearchResultBase } = require('./SearchResultBase'); class SpecificationsSearchResult extends SearchResultBase { @@ -11,4 +11,4 @@ class SpecificationsSearchResult extends SearchResultBase { } } -module.exports = SpecificationsSearchResult; +module.exports = { SpecificationsSearchResult }; diff --git a/src/core/searchResult/User.js b/src/core/searchResult/User.js index 58e6a28b6..20ec93dcb 100644 --- a/src/core/searchResult/User.js +++ b/src/core/searchResult/User.js @@ -1,5 +1,5 @@ -const SearchResultBase = require('./SearchResultBase'); -const User = require('../security/User'); +const { SearchResultBase } = require('./SearchResultBase'); +const { User } = require('../security/User'); class UserSearchResult extends SearchResultBase { @@ -24,4 +24,4 @@ class UserSearchResult extends SearchResultBase { } } -module.exports = UserSearchResult; +module.exports = { UserSearchResult }; diff --git a/src/core/security/Profile.js b/src/core/security/Profile.js index 4cf2de8f5..3926a7c90 100644 --- a/src/core/security/Profile.js +++ b/src/core/security/Profile.js @@ -1,4 +1,4 @@ -class Profile { +export class Profile { /** * * @param {Kuzzle} kuzzle @@ -32,5 +32,5 @@ class Profile { } } -module.exports = Profile; +module.exports = { Profile }; diff --git a/src/core/security/User.js b/src/core/security/User.ts similarity index 52% rename from src/core/security/User.js rename to src/core/security/User.ts index 75e19014c..08ef8208f 100644 --- a/src/core/security/User.js +++ b/src/core/security/User.ts @@ -1,4 +1,18 @@ -class User { +import { JSONObject } from "../../utils/interfaces"; +import { Profile } from './Profile'; + +export class User { + /** + * Kuid (Kuzzle unique ID) + */ + public _id: string; + /** + * Custom content + */ + public content: JSONObject; + + private _kuzzle: any; + /** * * @param {Kuzzle} kuzzle @@ -13,25 +27,29 @@ class User { this.content = content; } - get kuzzle () { + private get kuzzle () { return this._kuzzle; } - get profileIds () { + /** + * Array of profile IDs + */ + get profileIds (): string[] { return this.content.profileIds || []; } /** - * @returns {Promise<[Profile]>} + * Gets user profile definitions */ - getProfiles () { + getProfiles (): Promise { if (!this.profileIds || this.profileIds.length === 0) { return Promise.resolve([]); } + return this.kuzzle.security.mGetProfiles(this.profileIds); } } -module.exports = User; +module.exports = { User }; diff --git a/src/protocols/abstract/Base.js b/src/protocols/abstract/Base.js index 99deb6469..258c6082e 100644 --- a/src/protocols/abstract/Base.js +++ b/src/protocols/abstract/Base.js @@ -1,8 +1,8 @@ 'use strict'; const KuzzleError = require('../../KuzzleError'); -const uuidv4 = require('../../utils/uuidv4'); -const KuzzleEventEmitter = require('../../core/KuzzleEventEmitter'); +const { uuidv4 } = require('../../utils/uuidv4'); +const { KuzzleEventEmitter } = require('../../core/KuzzleEventEmitter'); const PendingRequest = require('./PendingRequest'); class KuzzleAbstractProtocol extends KuzzleEventEmitter { @@ -139,4 +139,4 @@ Discarded request: ${JSON.stringify(request)}`)); } -module.exports = KuzzleAbstractProtocol; +module.exports = { KuzzleAbstractProtocol }; diff --git a/src/protocols/abstract/Realtime.js b/src/protocols/abstract/Realtime.js index 53ee5e311..0dab94019 100644 --- a/src/protocols/abstract/Realtime.js +++ b/src/protocols/abstract/Realtime.js @@ -1,6 +1,6 @@ 'use strict'; -const KuzzleAbstractProtocol = require('./Base'); +const { KuzzleAbstractProtocol } = require('./Base'); class BaseProtocolRealtime extends KuzzleAbstractProtocol { constructor (host, options = {}) { diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts new file mode 100644 index 000000000..3da296f4d --- /dev/null +++ b/src/utils/interfaces.ts @@ -0,0 +1,20 @@ +'use strict'; + +/** + * An interface representing an object with string key and any value + */ +export interface JSONObject { + [key: string]: JSONObject | any +} + + +export interface KuzzleRequest extends JSONObject { + controller?: string; + action?: string; + index?: string; + collection?: string; + _id?: string; + jwt?: string; + volatile?: JSONObject; + body?: JSONObject; +} diff --git a/src/utils/proxify.js b/src/utils/proxify.js index 1dd3768da..58fbf4cb0 100644 --- a/src/utils/proxify.js +++ b/src/utils/proxify.js @@ -132,4 +132,4 @@ const proxify = (obj, opts = {}) => { return new Proxy(obj, handler); }; -module.exports = proxify; +module.exports = { proxify }; diff --git a/src/utils/uuidv4.js b/src/utils/uuidv4.js index deb629b21..80a8ded69 100644 --- a/src/utils/uuidv4.js +++ b/src/utils/uuidv4.js @@ -3,7 +3,7 @@ // // cf amazing https://gist.github.com/jed/982883 -const b = (a) => a +const uuidv4 = (a) => a ? ( a ^ Math.random() @@ -21,4 +21,4 @@ const b = (a) => a b ); -module.exports = b; +module.exports = { uuidv4 }; diff --git a/test/controllers/auth.test.js b/test/controllers/auth.test.js index a3e774792..f58b8c331 100644 --- a/test/controllers/auth.test.js +++ b/test/controllers/auth.test.js @@ -1,10 +1,10 @@ const sinon = require('sinon'); const should = require('should'); -const KuzzleEventEmitter = require('../../src/core/KuzzleEventEmitter'); -const Jwt = require('../../src/core/Jwt'); -const AuthController = require('../../src/controllers/Auth'); -const User = require('../../src/core/security/User'); +const { KuzzleEventEmitter } = require('../../src/core/KuzzleEventEmitter'); +const { Jwt } = require('../../src/core/Jwt'); +const { AuthController } = require('../../src/controllers/Auth'); +const { User } = require('../../src/core/security/User'); const generateJwt = require('../mocks/generateJwt.mock'); describe('Auth Controller', () => { diff --git a/test/controllers/bulk.test.js b/test/controllers/bulk.test.js index 750a6792a..cc41dc0ec 100644 --- a/test/controllers/bulk.test.js +++ b/test/controllers/bulk.test.js @@ -1,5 +1,5 @@ const - BulkController = require('../../src/controllers/Bulk'), + { BulkController } = require('../../src/controllers/Bulk'), sinon = require('sinon'), should = require('should'); diff --git a/test/controllers/collection.test.js b/test/controllers/collection.test.js index 8d33ba0fa..03e83489f 100644 --- a/test/controllers/collection.test.js +++ b/test/controllers/collection.test.js @@ -1,6 +1,6 @@ const - CollectionController = require('../../src/controllers/Collection'), - SpecificationsSearchResult = require('../../src/core/searchResult/Specifications'), + { CollectionController } = require('../../src/controllers/Collection'), + { SpecificationsSearchResult } = require('../../src/core/searchResult/Specifications'), sinon = require('sinon'), should = require('should'); diff --git a/test/controllers/document.test.js b/test/controllers/document.test.js index 349a3fd1b..71fb7ca88 100644 --- a/test/controllers/document.test.js +++ b/test/controllers/document.test.js @@ -1,5 +1,5 @@ const - DocumentController = require('../../src/controllers/Document'), + { DocumentController } = require('../../src/controllers/Document'), DocumentSearchResult = require('../../src/core/searchResult/Document'), sinon = require('sinon'), should = require('should'); diff --git a/test/controllers/index.test.js b/test/controllers/index.test.js index 09eba3bd1..409a9ed13 100644 --- a/test/controllers/index.test.js +++ b/test/controllers/index.test.js @@ -1,5 +1,5 @@ const - IndexController = require('../../src/controllers/Index'), + { IndexController } = require('../../src/controllers/Index'), sinon = require('sinon'), should = require('should'); diff --git a/test/controllers/memoryStorage.test.js b/test/controllers/memoryStorage.test.js index 63010b794..913cd0529 100644 --- a/test/controllers/memoryStorage.test.js +++ b/test/controllers/memoryStorage.test.js @@ -1,5 +1,5 @@ const - MemoryStorageController = require('../../src/controllers/MemoryStorage'), + { MemoryStorageController } = require('../../src/controllers/MemoryStorage'), should = require('should'), sinon = require('sinon'); diff --git a/test/controllers/realtime.test.js b/test/controllers/realtime.test.js index 549cd1b89..968e8404d 100644 --- a/test/controllers/realtime.test.js +++ b/test/controllers/realtime.test.js @@ -2,11 +2,11 @@ const mockrequire = require('mock-require'); const sinon = require('sinon'); const should = require('should'); -const KuzzleEventEmitter = require('../../src/core/KuzzleEventEmitter'); -const AuthController = require('../../src/controllers/Auth'); +const { KuzzleEventEmitter } = require('../../src/core/KuzzleEventEmitter'); +const { AuthController } = require('../../src/controllers/Auth'); const RealtimeController = require('../../src/controllers/Realtime'); const generateJwt = require('../mocks/generateJwt.mock'); -const uuidv4 = require('../../src/utils/uuidv4'); +const { uuidv4 } = require('../../src/utils/uuidv4'); describe('Realtime Controller', () => { const options = {opt: 'in'}; diff --git a/test/controllers/security.test.js b/test/controllers/security.test.js index e19f576bc..da95c0b29 100644 --- a/test/controllers/security.test.js +++ b/test/controllers/security.test.js @@ -1,10 +1,10 @@ -const SecurityController = require('../../src/controllers/Security'); -const Profile = require('../../src/core/security/Profile'); +const { SecurityController } = require('../../src/controllers/Security'); +const { Profile } = require('../../src/core/security/Profile'); const Role = require('../../src/core/security/Role'); -const User = require('../../src/core/security/User'); -const ProfileSearchResult = require('../../src/core/searchResult/Profile'); -const RoleSearchResult = require('../../src/core/searchResult/Role'); -const UserSearchResult = require('../../src/core/searchResult/User'); +const { User } = require('../../src/core/security/User'); +const { ProfileSearchResult } = require('../../src/core/searchResult/Profile'); +const { RoleSearchResult } = require('../../src/core/searchResult/Role'); +const { UserSearchResult } = require('../../src/core/searchResult/User'); const sinon = require('sinon'); const should = require('should'); diff --git a/test/controllers/server.test.js b/test/controllers/server.test.js index 243eb9467..193a13ff7 100644 --- a/test/controllers/server.test.js +++ b/test/controllers/server.test.js @@ -1,5 +1,5 @@ const - ServerController = require('../../src/controllers/Server'), + { ServerController } = require('../../src/controllers/Server'), sinon = require('sinon'), should = require('should'); diff --git a/test/core/Jwt.test.js b/test/core/Jwt.test.js index 699c27599..3609070e9 100644 --- a/test/core/Jwt.test.js +++ b/test/core/Jwt.test.js @@ -9,7 +9,7 @@ describe('Jwt', () => { authenticationToken; beforeEach(() => { - Jwt = rewire('../../src/core/Jwt'); + { Jwt } = rewire('../../src/core/Jwt'); }); describe('#constructor', () => { diff --git a/test/core/room.test.js b/test/core/room.test.js index 1b79153b2..6e7172269 100644 --- a/test/core/room.test.js +++ b/test/core/room.test.js @@ -1,6 +1,6 @@ const Room = require('../../src/core/Room'), - KuzzleEventEmitter = require('../../src/core/KuzzleEventEmitter'), + { KuzzleEventEmitter } = require('../../src/core/KuzzleEventEmitter'), sinon = require('sinon'), should = require('should'); diff --git a/test/core/searchResult/profile.test.js b/test/core/searchResult/profile.test.js index 8df5c0803..8e10e26dd 100644 --- a/test/core/searchResult/profile.test.js +++ b/test/core/searchResult/profile.test.js @@ -1,6 +1,6 @@ const - ProfileSearchResult = require('../../../src/core/searchResult/Profile'), - Profile = require('../../../src/core/security/Profile'), + { ProfileSearchResult } = require('../../../src/core/searchResult/Profile'), + { Profile } = require('../../../src/core/security/Profile'), sinon = require('sinon'), should = require('should'); diff --git a/test/core/searchResult/role.test.js b/test/core/searchResult/role.test.js index 554199b49..cdf2b1c0e 100644 --- a/test/core/searchResult/role.test.js +++ b/test/core/searchResult/role.test.js @@ -1,5 +1,5 @@ const - RoleSearchResult = require('../../../src/core/searchResult/Role'), + { RoleSearchResult } = require('../../../src/core/searchResult/Role'), Role = require('../../../src/core/security/Role'), sinon = require('sinon'), should = require('should'); diff --git a/test/core/searchResult/specifications.test.js b/test/core/searchResult/specifications.test.js index 156a9e69f..c524409d1 100644 --- a/test/core/searchResult/specifications.test.js +++ b/test/core/searchResult/specifications.test.js @@ -1,5 +1,5 @@ const - SpecificationsSearchResult = require('../../../src/core/searchResult/Specifications'), + { SpecificationsSearchResult } = require('../../../src/core/searchResult/Specifications'), sinon = require('sinon'), should = require('should'); diff --git a/test/core/searchResult/user.test.js b/test/core/searchResult/user.test.js index c696db429..e0d6583de 100644 --- a/test/core/searchResult/user.test.js +++ b/test/core/searchResult/user.test.js @@ -1,6 +1,6 @@ const - UserSearchResult = require('../../../src/core/searchResult/User'), - User = require('../../../src/core/security/User'), + { UserSearchResult } = require('../../../src/core/searchResult/User'), + { User } = require('../../../src/core/security/User'), sinon = require('sinon'), should = require('should'); diff --git a/test/core/security/profile.test.js b/test/core/security/profile.test.js index 90dea92ae..175f143b0 100644 --- a/test/core/security/profile.test.js +++ b/test/core/security/profile.test.js @@ -1,4 +1,4 @@ -const Profile = require('../../../src/core/security/Profile'); +const { Profile } = require('../../../src/core/security/Profile'); const sinon = require('sinon'); const should = require('should'); diff --git a/test/core/security/user.test.js b/test/core/security/user.test.js index 9d36dbc8d..57054e5b5 100644 --- a/test/core/security/user.test.js +++ b/test/core/security/user.test.js @@ -1,5 +1,5 @@ const - User = require('../../../src/core/security/User'), + { User } = require('../../../src/core/security/User'), sinon = require('sinon'), should = require('should'); diff --git a/test/kuzzle/connect.test.js b/test/kuzzle/connect.test.js index c3a191ff3..63c784b55 100644 --- a/test/kuzzle/connect.test.js +++ b/test/kuzzle/connect.test.js @@ -3,7 +3,7 @@ const sinon = require('sinon'), ProtocolMock = require('../mocks/protocol.mock'), generateJwt = require('../mocks/generateJwt.mock'), - Kuzzle = require('../../src/Kuzzle'); + { Kuzzle } = require('../../src/Kuzzle'); describe('Kuzzle connect', () => { @@ -98,7 +98,7 @@ describe('Kuzzle connect', () => { it('should keep a valid JWT at reconnection', () => { const - jwt = generateJwt(), + jwt = generateJwt(), kuzzle = new Kuzzle(protocols.somewhereagain); kuzzle.auth.checkToken = sinon.stub().resolves({ @@ -116,7 +116,7 @@ describe('Kuzzle connect', () => { it('should empty the JWT at reconnection if it has expired', () => { const - jwt = generateJwt(), + jwt = generateJwt(), kuzzle = new Kuzzle(protocols.somewhereagain); kuzzle.auth.checkToken = sinon.stub().resolves({ diff --git a/test/kuzzle/constructor.test.js b/test/kuzzle/constructor.test.js index 46aaa219d..f18316898 100644 --- a/test/kuzzle/constructor.test.js +++ b/test/kuzzle/constructor.test.js @@ -1,16 +1,16 @@ const should = require('should'), sinon = require('sinon'), - Kuzzle = require('../../src/Kuzzle'), - AuthController = require('../../src/controllers/Auth'), - BulkController = require('../../src/controllers/Bulk'), - CollectionController = require('../../src/controllers/Collection'), - DocumentController = require('../../src/controllers/Document'), - IndexController = require('../../src/controllers/Index'), - MemoryStorageController = require('../../src/controllers/MemoryStorage'), - SecurityController = require('../../src/controllers/Security'), - ServerController = require('../../src/controllers/Server'), - RealTimeController = require('../../src/controllers/Realtime'), + { Kuzzle } = require('../../src/Kuzzle'), + { AuthController } = require('../../src/controllers/Auth'), + { BulkController } = require('../../src/controllers/Bulk'), + { CollectionController } = require('../../src/controllers/Collection'), + { DocumentController } = require('../../src/controllers/Document'), + { IndexController } = require('../../src/controllers/Index'), + { MemoryStorageController } = require('../../src/controllers/MemoryStorage'), + { SecurityController } = require('../../src/controllers/Security'), + { ServerController } = require('../../src/controllers/Server'), + { RealtimeController } = require('../../src/controllers/Realtime'), { WebSocket, // eslint-disable-line no-redeclare Http @@ -65,7 +65,7 @@ describe('Kuzzle constructor', () => { should(kuzzle.ms).be.an.instanceof(MemoryStorageController); should(kuzzle.security).be.an.instanceof(SecurityController); should(kuzzle.server).be.an.instanceof(ServerController); - should(kuzzle.realtime).be.an.instanceof(RealTimeController); + should(kuzzle.realtime).be.an.instanceof(RealtimeController); }); it('should expose the documented properties with their default values', () => { @@ -123,7 +123,7 @@ describe('Kuzzle constructor', () => { should(kuzzle.ms).be.an.instanceof(MemoryStorageController); should(kuzzle.security).be.an.instanceof(SecurityController); should(kuzzle.server).be.an.instanceof(ServerController); - should(kuzzle.realtime).be.an.instanceof(RealTimeController); + should(kuzzle.realtime).be.an.instanceof(RealtimeController); should(kuzzle.protocol).be.an.instanceof(ProtocolMock); should(kuzzle.sdkVersion).be.a.String().and.be.equal(version); should(kuzzle.jwt).be.null(); diff --git a/test/kuzzle/eventEmitter.test.js b/test/kuzzle/eventEmitter.test.js index 819a100dd..bcdf7e752 100644 --- a/test/kuzzle/eventEmitter.test.js +++ b/test/kuzzle/eventEmitter.test.js @@ -1,7 +1,7 @@ const should = require('should'), sinon = require('sinon'), - KuzzleEventEmitter = require('../../src/core/KuzzleEventEmitter'); + { KuzzleEventEmitter } = require('../../src/core/KuzzleEventEmitter'); describe('Event emitter', () => { const diff --git a/test/kuzzle/getters.test.js b/test/kuzzle/getters.test.js index 258a50a34..13cdad2fe 100644 --- a/test/kuzzle/getters.test.js +++ b/test/kuzzle/getters.test.js @@ -2,11 +2,11 @@ const should = require('should'), ProtocolMock = require('../mocks/protocol.mock'), generateJwt = require('../mocks/generateJwt.mock'), - Kuzzle = require('../../src/Kuzzle'); + { Kuzzle } = require('../../src/Kuzzle'); describe('Kuzzle getters', () => { let - jwt, + jwt, kuzzle; beforeEach(() => { diff --git a/test/kuzzle/protocol.test.js b/test/kuzzle/protocol.test.js index 1f702e185..234e17e69 100644 --- a/test/kuzzle/protocol.test.js +++ b/test/kuzzle/protocol.test.js @@ -3,7 +3,7 @@ const sinon = require('sinon'), ProtocolMock = require('../mocks/protocol.mock'), generateJwt = require('../mocks/generateJwt.mock'), - Kuzzle = require('../../src/Kuzzle'); + { Kuzzle } = require('../../src/Kuzzle'); describe('Kuzzle protocol methods', () => { let kuzzle; diff --git a/test/kuzzle/proxy.test.js b/test/kuzzle/proxy.test.js index 4f9b57387..6449e9481 100644 --- a/test/kuzzle/proxy.test.js +++ b/test/kuzzle/proxy.test.js @@ -1,6 +1,6 @@ const should = require('should'), - Kuzzle = require('../../src/Kuzzle'), + { Kuzzle } = require('../../src/Kuzzle'), ProtocolMock = require('../mocks/protocol.mock'); describe('Kuzzle proxy', () => { @@ -10,7 +10,7 @@ describe('Kuzzle proxy', () => { const kuzzle = new Kuzzle(protocolMock); should(() => { kuzzle.jvt = 'foobar'; - }).throwError('Cannot set a value to the undefined \'jvt\' property in \'kuzzle\''); + }).throwError('Cannot set a value to the undefined \'jvt\' property in \'kuzzle\''); }); }); diff --git a/test/kuzzle/query.test.js b/test/kuzzle/query.test.js index 75d439fdb..2a849dc41 100644 --- a/test/kuzzle/query.test.js +++ b/test/kuzzle/query.test.js @@ -3,7 +3,7 @@ const sinon = require('sinon'), ProtocolMock = require('../mocks/protocol.mock'), generateJwt = require('../mocks/generateJwt.mock'), - Kuzzle = require('../../src/Kuzzle'); + { Kuzzle } = require('../../src/Kuzzle'); describe('Kuzzle query management', () => { describe('#query', () => { diff --git a/test/kuzzle/queue.test.js b/test/kuzzle/queue.test.js index 9d99a183f..e34657dbc 100644 --- a/test/kuzzle/queue.test.js +++ b/test/kuzzle/queue.test.js @@ -2,7 +2,7 @@ const should = require('should'), sinon = require('sinon'), ProtocolMock = require('../mocks/protocol.mock'), - Kuzzle = require('../../src/Kuzzle'); + { Kuzzle } = require('../../src/Kuzzle'); describe('Kuzzle queue', () => { let kuzzle; diff --git a/test/kuzzle/setters.test.js b/test/kuzzle/setters.test.js index 2aa4e45a1..de19dd92b 100644 --- a/test/kuzzle/setters.test.js +++ b/test/kuzzle/setters.test.js @@ -3,8 +3,8 @@ const sinon = require('sinon'), ProtocolMock = require('../mocks/protocol.mock'), generateJwt = require('../mocks/generateJwt.mock'), - Jwt = require('../../src/core/Jwt'), - Kuzzle = require('../../src/Kuzzle'); + { Jwt } = require('../../src/core/Jwt'), + { Kuzzle } = require('../../src/Kuzzle'); describe('Kuzzle setters', () => { let kuzzle; diff --git a/test/kuzzle/useController.test.js b/test/kuzzle/useController.test.js index 2afd1fb58..8cb79634d 100644 --- a/test/kuzzle/useController.test.js +++ b/test/kuzzle/useController.test.js @@ -2,8 +2,8 @@ const should = require('should'), sinon = require('sinon'), ProtocolMock = require('../mocks/protocol.mock'), - BaseController = require('../../src/controllers/Base'), - Kuzzle = require('../../src/Kuzzle'); + { BaseController } = require('../../src/controllers/Base'), + { Kuzzle } = require('../../src/Kuzzle'); class CustomController extends BaseController { constructor (kuzzle) { diff --git a/test/mocks/protocol.mock.js b/test/mocks/protocol.mock.js index e01fab468..d6a414b0e 100644 --- a/test/mocks/protocol.mock.js +++ b/test/mocks/protocol.mock.js @@ -1,6 +1,6 @@ const sinon = require('sinon'), - KuzzleEventEmitter = require('../../src/core/KuzzleEventEmitter'); + { KuzzleEventEmitter } = require('../../src/core/KuzzleEventEmitter'); class ProtocolMock extends KuzzleEventEmitter { diff --git a/test/mocks/window.mock.js b/test/mocks/window.mock.js index 45b26940c..b813731fb 100644 --- a/test/mocks/window.mock.js +++ b/test/mocks/window.mock.js @@ -1,6 +1,6 @@ const sinon = require('sinon'), - KuzzleEventEmitter = require('../../src/core/KuzzleEventEmitter'); + { KuzzleEventEmitter } = require('../../src/core/KuzzleEventEmitter'); // A class to mock the global window object class WindowMock extends KuzzleEventEmitter { diff --git a/test/utils/proxify.test.js b/test/utils/proxify.test.js index 70916f0d6..7ac5f8629 100644 --- a/test/utils/proxify.test.js +++ b/test/utils/proxify.test.js @@ -1,7 +1,7 @@ const should = require('should'), sinon = require('sinon'), - proxify = require('../../src/utils/proxify'); + { proxify } = require('../../src/utils/proxify'); describe('proxify', () => { let From bbecc78cc7f6191c6f01058c98808726aba9528f Mon Sep 17 00:00:00 2001 From: Aschen Date: Tue, 14 Jul 2020 11:44:31 +0200 Subject: [PATCH 03/15] Adds Profile and Role --- .eslintignore | 2 + .gitignore | 2 + .../core-classes/profile/properties/index.md | 1 + package.json | 2 +- src/Kuzzle.ts | 3 +- src/controllers/Security.js | 2 +- src/core/searchResult/Role.js | 2 +- src/core/security/{Profile.js => Profile.ts} | 27 ++++++- src/core/security/{Role.js => Role.ts} | 16 +++- src/core/security/User.ts | 4 +- src/utils/interfaces.ts | 78 ++++++++++++++++++- test/controllers/security.test.js | 2 +- test/core/Jwt.test.js | 2 +- test/core/searchResult/role.test.js | 2 +- tsconfig.json | 19 +++++ 15 files changed, 149 insertions(+), 15 deletions(-) rename src/core/security/{Profile.js => Profile.ts} (57%) rename src/core/security/{Role.js => Role.ts} (50%) create mode 100644 tsconfig.json diff --git a/.eslintignore b/.eslintignore index e8697a479..90bea7150 100644 --- a/.eslintignore +++ b/.eslintignore @@ -6,4 +6,6 @@ src/Kuzzle.js src/controllers/Auth.js src/controllers/Base.js src/core/security/User.js +src/core/security/Profile.js +src/core/security/Role.js src/utils/interfaces.js diff --git a/.gitignore b/.gitignore index 3e9605a45..3e79082de 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,6 @@ src/Kuzzle.js src/controllers/Auth.js src/controllers/Base.js src/core/security/User.js +src/core/security/Profile.js +src/core/security/Role.js src/utils/interfaces.js diff --git a/doc/7/core-classes/profile/properties/index.md b/doc/7/core-classes/profile/properties/index.md index c6dddc9eb..c404b9ec3 100644 --- a/doc/7/core-classes/profile/properties/index.md +++ b/doc/7/core-classes/profile/properties/index.md @@ -12,6 +12,7 @@ order: 10 |--- |--- |--- | | `_id` |
string
| Profile ID | | `policies` |
object[]
| Array of policies for this profile | +| `rateLimit` |
number
| Maximum number of requests per second and per node with this profile | ### policies diff --git a/package.json b/package.json index 3ceeae0e5..08f266c1a 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "test:unit": "nyc --reporter=text-summary --reporter=lcov mocha", "test:functional": "cucumber-js --exit --fail-fast", "test:lint": "npm run test:lint:js && npm run test:lint:ts", - "test:lint:js": "eslint --max-warnings=0 index.js ./src ./test ./features", + "test:lint:js": "eslint --max-warnings=0 ./src ./test ./features", "test:lint:ts": "eslint ./src --ext .ts --config .eslintc-ts.json", "build": "npm run build-ts && node build.js", "build-ts": "tsc --build tsconfig.json", diff --git a/src/Kuzzle.ts b/src/Kuzzle.ts index 1dce88107..2d93f7b07 100644 --- a/src/Kuzzle.ts +++ b/src/Kuzzle.ts @@ -15,7 +15,7 @@ import { proxify } from './utils/proxify'; import { JSONObject } from './utils/interfaces'; // defined by webpack plugin -declare var SDKVERSION: any; +declare const SDKVERSION: any; const events = [ 'connected', @@ -118,6 +118,7 @@ export class Kuzzle extends KuzzleEventEmitter { : 200; this.sdkVersion = typeof SDKVERSION === 'undefined' + // eslint-disable-next-line @typescript-eslint/no-var-requires ? require('../package').version : SDKVERSION; diff --git a/src/controllers/Security.js b/src/controllers/Security.js index 85ce9eac7..26e5b24aa 100644 --- a/src/controllers/Security.js +++ b/src/controllers/Security.js @@ -1,5 +1,5 @@ const { BaseController } = require('./Base'); -const Role = require('../core/security/Role'); +const { Role } = require('../core/security/Role'); const { RoleSearchResult } = require('../core/searchResult/Role'); const { Profile } = require('../core/security/Profile'); const { ProfileSearchResult } = require('../core/searchResult/Profile'); diff --git a/src/core/searchResult/Role.js b/src/core/searchResult/Role.js index 3f5ad2228..241e310da 100644 --- a/src/core/searchResult/Role.js +++ b/src/core/searchResult/Role.js @@ -1,4 +1,4 @@ -const Role = require('../security/Role'); +const { Role } = require('../security/Role'); const { SearchResultBase } = require('./SearchResultBase'); class RoleSearchResult extends SearchResultBase { diff --git a/src/core/security/Profile.js b/src/core/security/Profile.ts similarity index 57% rename from src/core/security/Profile.js rename to src/core/security/Profile.ts index 3926a7c90..e8f62995d 100644 --- a/src/core/security/Profile.js +++ b/src/core/security/Profile.ts @@ -1,4 +1,25 @@ +import { Role } from './Role'; +import { ProfilePolicy } from '../../utils/interfaces'; + export class Profile { + /** + * Profile unique ID + */ + public _id: string; + + /** + * Maximum number of requests per second and per node with this profile + */ + public rateLimit: number; + + /** + * Array of policies + */ + public policies: ProfilePolicy[] | []; + + + private _kuzzle: any; + /** * * @param {Kuzzle} kuzzle @@ -14,14 +35,14 @@ export class Profile { this.policies = content && content.policies ? content.policies : []; } - get kuzzle () { + protected get kuzzle () { return this._kuzzle; } /** - * @returns {Promise<[Role]>} + * Gets the associated roles definitions from the API. */ - getRoles (options = {}) { + getRoles (options = {}): Promise { if (!this.policies || this.policies.length === 0) { return Promise.resolve([]); } diff --git a/src/core/security/Role.js b/src/core/security/Role.ts similarity index 50% rename from src/core/security/Role.js rename to src/core/security/Role.ts index d372cba4c..a1dc12b0d 100644 --- a/src/core/security/Role.js +++ b/src/core/security/Role.ts @@ -1,4 +1,16 @@ +import { RoleRightsDefinition } from '../../utils/interfaces'; + class Role { + /** + * Role unique ID + */ + public _id: string; + /** + * List of rights on controllers/actions + */ + public controllers: RoleRightsDefinition; + + private _kuzzle: any; /** * @param {Kuzzle} kuzzle @@ -13,10 +25,10 @@ class Role { this.controllers = controllers; } - get kuzzle () { + protected get kuzzle () { return this._kuzzle; } } -module.exports = Role; +module.exports = { Role }; diff --git a/src/core/security/User.ts b/src/core/security/User.ts index 08ef8208f..498330e9e 100644 --- a/src/core/security/User.ts +++ b/src/core/security/User.ts @@ -1,4 +1,4 @@ -import { JSONObject } from "../../utils/interfaces"; +import { JSONObject } from '../../utils/interfaces'; import { Profile } from './Profile'; export class User { @@ -39,7 +39,7 @@ export class User { } /** - * Gets user profile definitions + * Gets user profile definitions from the API */ getProfiles (): Promise { if (!this.profileIds || this.profileIds.length === 0) { diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index 3da296f4d..56e743613 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -7,7 +7,11 @@ export interface JSONObject { [key: string]: JSONObject | any } - +/** + * Kuzzle API request + * + * @see https://docs.kuzzle.io/core/2/api/essentials/query-syntax/#other-protocols + */ export interface KuzzleRequest extends JSONObject { controller?: string; action?: string; @@ -18,3 +22,75 @@ export interface KuzzleRequest extends JSONObject { volatile?: JSONObject; body?: JSONObject; } + +/** + * A profile policy is composed of a roleId to define API rights + * and an optional array of restrictions on index and collections + * + * @example + * { + * "roleId": "editor", + * "restrictedTo": { + * "index": "blog", + * "collections": [ + * "articles" + * ] + * } + * } + * + * @see https://docs.kuzzle.io/core/2/guides/essentials/security/#defining-profiles + */ +export interface ProfilePolicy { + /** + * Role unique ID used by this policy + */ + roleId: string; + + /** + * Optional array of restrictions on which the rights are gonne be applied + */ + restrictedTo?: { + /** + * Index name. + * Rights will only be applied on this index. + */ + index: string; + + /** + * Collection names. + * Rights will only be applied on those collections. + */ + collections?: string[]; + } +} + +/** + * Role list of rights definition for controllers and actions. + * + * @example + * + * { + * auth: { + * actions: { + * getCurrentUser: true, + * getMyCredentials: true, + * getMyRights: true, + * logout: true + * } + * }, + * realtime: { + * actions: { + * "*": true + * } + * } + * } + * + * @see https://docs.kuzzle.io/core/2/guides/essentials/security#defining-roles + */ +export interface RoleRightsDefinition { + [key: string]: { + actions: { + [key: string]: boolean + } + } +} \ No newline at end of file diff --git a/test/controllers/security.test.js b/test/controllers/security.test.js index da95c0b29..f42316844 100644 --- a/test/controllers/security.test.js +++ b/test/controllers/security.test.js @@ -1,6 +1,6 @@ const { SecurityController } = require('../../src/controllers/Security'); const { Profile } = require('../../src/core/security/Profile'); -const Role = require('../../src/core/security/Role'); +const { Role } = require('../../src/core/security/Role'); const { User } = require('../../src/core/security/User'); const { ProfileSearchResult } = require('../../src/core/searchResult/Profile'); const { RoleSearchResult } = require('../../src/core/searchResult/Role'); diff --git a/test/core/Jwt.test.js b/test/core/Jwt.test.js index 3609070e9..371f9f72b 100644 --- a/test/core/Jwt.test.js +++ b/test/core/Jwt.test.js @@ -9,7 +9,7 @@ describe('Jwt', () => { authenticationToken; beforeEach(() => { - { Jwt } = rewire('../../src/core/Jwt'); + ({ Jwt } = rewire('../../src/core/Jwt')); }); describe('#constructor', () => { diff --git a/test/core/searchResult/role.test.js b/test/core/searchResult/role.test.js index cdf2b1c0e..5f92d858a 100644 --- a/test/core/searchResult/role.test.js +++ b/test/core/searchResult/role.test.js @@ -1,6 +1,6 @@ const { RoleSearchResult } = require('../../../src/core/searchResult/Role'), - Role = require('../../../src/core/security/Role'), + { Role } = require('../../../src/core/security/Role'), sinon = require('sinon'), should = require('should'); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 000000000..87d3ed6ac --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "declaration": true, + "listEmittedFiles": true, + "module": "commonjs", + "target": "es2020", + "moduleResolution": "node", + "sourceMap": true, + "baseUrl": "." + }, + "rootDir": "src/", + "include": [ + "index.ts", + "src/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file From 6be8eadfdb4e4d44289ef3f783dd3f6bd004e38d Mon Sep 17 00:00:00 2001 From: Aschen Date: Tue, 14 Jul 2020 12:14:17 +0200 Subject: [PATCH 04/15] Fix tests --- .mocharc.json | 2 +- src/Kuzzle.ts | 9 +++- src/controllers/Auth.ts | 42 +++---------------- src/controllers/Document.js | 4 +- src/controllers/MemoryStorage.js | 4 +- src/controllers/Server.js | 4 +- src/core/security/Profile.ts | 4 +- src/core/security/Role.ts | 2 +- src/core/security/User.ts | 4 +- src/protocols/Http.js | 4 +- src/utils/interfaces.ts | 39 +++++++++++++++++- src/utils/uuidv4.js | 4 +- test/controllers/document.test.js | 20 ++++----- test/controllers/realtime.test.js | 7 ++-- test/core/searchResult/document.test.js | 40 +++++++++--------- test/kuzzle/listenersManagement.test.js | 55 +++++++++---------------- test/kuzzle/query.test.js | 6 +-- test/protocol/Base.test.js | 12 +++--- 18 files changed, 129 insertions(+), 133 deletions(-) diff --git a/.mocharc.json b/.mocharc.json index 4237f1313..79c73899e 100644 --- a/.mocharc.json +++ b/.mocharc.json @@ -3,5 +3,5 @@ "recursive": true, "slow": 2000, "timeout": 10000, - "require": ["should-sinon"] + "require": ["should-sinon", "ts-node/register"] } diff --git a/src/Kuzzle.ts b/src/Kuzzle.ts index 2d93f7b07..c864e0040 100644 --- a/src/Kuzzle.ts +++ b/src/Kuzzle.ts @@ -320,8 +320,11 @@ export class Kuzzle extends KuzzleEventEmitter { } protectedEvent.lastEmitted = now; } + return this._superEmit(eventName, ...payload) + } - super.emit(eventName, ...payload); + _superEmit (eventName, ...payload) { + return super.emit(eventName, ...payload); } /** @@ -414,6 +417,10 @@ export class Kuzzle extends KuzzleEventEmitter { throw new Error(`[${event}] is not a known event. Known events: ${events.toString()}`); } + return this._superAddListener(event, listener); + } + + _superAddListener (event, listener) { return super.addListener(event, listener); } diff --git a/src/controllers/Auth.ts b/src/controllers/Auth.ts index 7825fd521..abe88d9f5 100644 --- a/src/controllers/Auth.ts +++ b/src/controllers/Auth.ts @@ -1,39 +1,7 @@ import { Jwt } from '../core/Jwt'; import { BaseController } from './Base'; import { User } from '../core/security/User'; -import { JSONObject } from '../utils/interfaces'; - -export interface ApiKey { - /** - * ApiKey unique ID - */ - _id: string; - /** - * ApiKey content - */ - _source: { - /** - * User kuid - */ - userId: string; - /** - * Expiration date in UNIX micro-timestamp format (-1 if the token never expires) - */ - expiresAt: number; - /** - * Original TTL in ms - */ - ttl: number; - /** - * API key description - */ - description: string; - /** - * Authentication token associated with this API key - */ - token: string; - } -} +import { JSONObject, ApiKey } from '../utils/interfaces'; /** * Auth controller @@ -165,7 +133,7 @@ export class AuthController extends BaseController { /** * Array of found ApiKeys */ - hits: ApiKey[], + hits: Array, /** * Total number of API keys found */ @@ -308,7 +276,7 @@ export class AuthController extends BaseController { return this.query({ action: 'getMyRights' }, options) - .then(response => response.result.hits as { + .then(response => response.result.hits as Array<{ /** * Controller on wich the rights are applied */ @@ -329,7 +297,7 @@ export class AuthController extends BaseController { * Rights ("allowed" or "denied") */ value: string - }[]); + }>); } /** @@ -344,7 +312,7 @@ export class AuthController extends BaseController { return this.query({ action: 'getStrategies' }, options) - .then(response => response.result as string[]); + .then(response => response.result as Array); } /** diff --git a/src/controllers/Document.js b/src/controllers/Document.js index debd4a798..cc88e76b8 100644 --- a/src/controllers/Document.js +++ b/src/controllers/Document.js @@ -1,5 +1,5 @@ const { BaseController } = require('./Base'); -const DocumentSearchResult = require('../core/searchResult/Document'); +const { DocumentsSearchResult } = require('../core/searchResult/Document'); class DocumentController extends BaseController { @@ -183,7 +183,7 @@ class DocumentController extends BaseController { search (index, collection, body = {}, options = {}) { return this._search(index, collection, body, options) .then(({ response, request, opts }) => ( - new DocumentSearchResult(this.kuzzle, request, opts, response.result) + new DocumentsSearchResult(this.kuzzle, request, opts, response.result) )); } diff --git a/src/controllers/MemoryStorage.js b/src/controllers/MemoryStorage.js index 36d404adf..4ddf0ee1d 100644 --- a/src/controllers/MemoryStorage.js +++ b/src/controllers/MemoryStorage.js @@ -1,4 +1,4 @@ -const BaseControler = require('./Base'); +const { BaseController } = require('./Base'); // Parameter mutualization const getId = {getter: true, required: ['_id']}; @@ -188,7 +188,7 @@ const commands = { * @param {object} kuzzle - Kuzzle instance to inherit from * @constructor */ -class MemoryStorageController extends BaseControler { +class MemoryStorageController extends BaseController { constructor (kuzzle) { super(kuzzle, 'ms'); diff --git a/src/controllers/Server.js b/src/controllers/Server.js index 2e94c1277..6f5eb6b5d 100644 --- a/src/controllers/Server.js +++ b/src/controllers/Server.js @@ -1,10 +1,10 @@ -const BaseControler = require('./Base'); +const { BaseController } = require('./Base'); /** * @class ServerController * @property {Kuzzle} kuzzle - The Kuzzle SDK Instance */ -class ServerController extends BaseControler { +class ServerController extends BaseController { /** * @param {Kuzzle} kuzzle - The Kuzzle SDK Instance diff --git a/src/core/security/Profile.ts b/src/core/security/Profile.ts index e8f62995d..12e18010a 100644 --- a/src/core/security/Profile.ts +++ b/src/core/security/Profile.ts @@ -15,7 +15,7 @@ export class Profile { /** * Array of policies */ - public policies: ProfilePolicy[] | []; + public policies: Array; private _kuzzle: any; @@ -42,7 +42,7 @@ export class Profile { /** * Gets the associated roles definitions from the API. */ - getRoles (options = {}): Promise { + getRoles (options = {}): Promise> { if (!this.policies || this.policies.length === 0) { return Promise.resolve([]); } diff --git a/src/core/security/Role.ts b/src/core/security/Role.ts index a1dc12b0d..5e2d4bbca 100644 --- a/src/core/security/Role.ts +++ b/src/core/security/Role.ts @@ -1,6 +1,6 @@ import { RoleRightsDefinition } from '../../utils/interfaces'; -class Role { +export class Role { /** * Role unique ID */ diff --git a/src/core/security/User.ts b/src/core/security/User.ts index 498330e9e..1e54da94c 100644 --- a/src/core/security/User.ts +++ b/src/core/security/User.ts @@ -34,14 +34,14 @@ export class User { /** * Array of profile IDs */ - get profileIds (): string[] { + get profileIds (): Array { return this.content.profileIds || []; } /** * Gets user profile definitions from the API */ - getProfiles (): Promise { + getProfiles (): Promise> { if (!this.profileIds || this.profileIds.length === 0) { return Promise.resolve([]); } diff --git a/src/protocols/Http.js b/src/protocols/Http.js index 8052773ec..2e31f3f8e 100644 --- a/src/protocols/Http.js +++ b/src/protocols/Http.js @@ -1,9 +1,9 @@ 'use strict'; const staticHttpRoutes = require('./routes.json'); -const BaseProtocol = require('./abstract/Base'); +const { KuzzleAbstractProtocol } = require('./abstract/Base'); -class HttpProtocol extends BaseProtocol { +class HttpProtocol extends KuzzleAbstractProtocol { constructor(host, options = {}) { super(host, options, 'http'); diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index 56e743613..7732cdb03 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -60,7 +60,7 @@ export interface ProfilePolicy { * Collection names. * Rights will only be applied on those collections. */ - collections?: string[]; + collections?: Array; } } @@ -93,4 +93,39 @@ export interface RoleRightsDefinition { [key: string]: boolean } } -} \ No newline at end of file +} + +/** + * ApiKey + */ +export interface ApiKey { + /** + * ApiKey unique ID + */ + _id: string; + /** + * ApiKey content + */ + _source: { + /** + * User kuid + */ + userId: string; + /** + * Expiration date in UNIX micro-timestamp format (-1 if the token never expires) + */ + expiresAt: number; + /** + * Original TTL in ms + */ + ttl: number; + /** + * API key description + */ + description: string; + /** + * Authentication token associated with this API key + */ + token: string; + } +} diff --git a/src/utils/uuidv4.js b/src/utils/uuidv4.js index 80a8ded69..d4eb8b83f 100644 --- a/src/utils/uuidv4.js +++ b/src/utils/uuidv4.js @@ -3,7 +3,7 @@ // // cf amazing https://gist.github.com/jed/982883 -const uuidv4 = (a) => a +const uuidv4 = a => a ? ( a ^ Math.random() @@ -18,7 +18,7 @@ const uuidv4 = (a) => a -1e11 ).replace( /[018]/g, - b + uuidv4 ); module.exports = { uuidv4 }; diff --git a/test/controllers/document.test.js b/test/controllers/document.test.js index 71fb7ca88..516e15501 100644 --- a/test/controllers/document.test.js +++ b/test/controllers/document.test.js @@ -1,8 +1,8 @@ -const - { DocumentController } = require('../../src/controllers/Document'), - DocumentSearchResult = require('../../src/core/searchResult/Document'), - sinon = require('sinon'), - should = require('should'); +const sinon = require('sinon'); +const should = require('should'); + +const { DocumentController } = require('../../src/controllers/Document'); +const { DocumentsSearchResult } = require('../../src/core/searchResult/Document'); describe('Document Controller', () => { const options = {opt: 'in'}; @@ -366,7 +366,7 @@ describe('Document Controller', () => { }); describe('search', () => { - it('should call document/search query and return a Promise which resolves a DocumentSearchResult instance', () => { + it('should call document/search query and return a Promise which resolves a DocumentsSearchResult instance', () => { const result = { scrollId: 'scroll-id', hits: [ @@ -394,7 +394,7 @@ describe('Document Controller', () => { scroll: undefined }, options); - should(res).be.an.instanceOf(DocumentSearchResult); + should(res).be.an.instanceOf(DocumentsSearchResult); should(res._options).match(options); should(res._options.verb).be.eql('POST'); should(res._response).be.equal(result); @@ -403,7 +403,7 @@ describe('Document Controller', () => { }); }); - it('should call document/search query and return a Promise which resolves a DocumentSearchResult instance', () => { + it('should call document/search query and return a Promise which resolves a DocumentsSearchResult instance', () => { const result = { scrollId: 'scroll-id', hits: [ @@ -433,7 +433,7 @@ describe('Document Controller', () => { scroll: undefined }, options); - should(res).be.an.instanceOf(DocumentSearchResult); + should(res).be.an.instanceOf(DocumentsSearchResult); should(res._options).match(options); should(res._options.verb).be.eql('GET'); should(res._response).be.equal(result); @@ -469,7 +469,7 @@ describe('Document Controller', () => { scroll: '10s' }, {}); - should(res).be.an.instanceOf(DocumentSearchResult); + should(res).be.an.instanceOf(DocumentsSearchResult); should(res._options).match({ verb: 'POST' }); should(res._response).be.equal(result); should(res.fetched).be.equal(2); diff --git a/test/controllers/realtime.test.js b/test/controllers/realtime.test.js index 968e8404d..c264a24cb 100644 --- a/test/controllers/realtime.test.js +++ b/test/controllers/realtime.test.js @@ -4,7 +4,7 @@ const should = require('should'); const { KuzzleEventEmitter } = require('../../src/core/KuzzleEventEmitter'); const { AuthController } = require('../../src/controllers/Auth'); -const RealtimeController = require('../../src/controllers/Realtime'); +const { RealtimeController } = require('../../src/controllers/Realtime'); const generateJwt = require('../mocks/generateJwt.mock'); const { uuidv4 } = require('../../src/utils/uuidv4'); @@ -110,8 +110,9 @@ describe('Realtime Controller', () => { return room; }); - const MockRealtimeController = - mockrequire.reRequire('../../src/controllers/Realtime'); + const reRequire = mockrequire.reRequire('../../src/controllers/Realtime'); + const MockRealtimeController = reRequire.RealtimeController; + kuzzle.realtime = new MockRealtimeController(kuzzle); }); diff --git a/test/core/searchResult/document.test.js b/test/core/searchResult/document.test.js index eade0043f..4e18cefb7 100644 --- a/test/core/searchResult/document.test.js +++ b/test/core/searchResult/document.test.js @@ -1,9 +1,9 @@ -const - DocumentSearchResult = require('../../../src/core/searchResult/Document'), - sinon = require('sinon'), - should = require('should'); +const sinon = require('sinon'); +const should = require('should'); -describe('DocumentSearchResult', () => { +const { DocumentsSearchResult } = require('../../../src/core/searchResult/Document'); + +describe('DocumentsSearchResult', () => { const options = {opt: 'in'}; let @@ -31,7 +31,7 @@ describe('DocumentSearchResult', () => { }); describe('constructor', () => { - it('should create a DocumentSearchResult instance with good properties', () => { + it('should create a DocumentsSearchResult instance with good properties', () => { response = { hits: [ {_id: 'document1', _score: 0.9876, _source: {foo: 'bar'}}, @@ -40,7 +40,7 @@ describe('DocumentSearchResult', () => { total: 3 }; - searchResult = new DocumentSearchResult(kuzzle, request, options, response); + searchResult = new DocumentsSearchResult(kuzzle, request, options, response); should(searchResult._request).be.equal(request); should(searchResult._options).be.equal(options); @@ -67,7 +67,7 @@ describe('DocumentSearchResult', () => { total: 2 }; - searchResult = new DocumentSearchResult(kuzzle, request, options, response); + searchResult = new DocumentsSearchResult(kuzzle, request, options, response); return searchResult.next() .then(result => { @@ -87,7 +87,7 @@ describe('DocumentSearchResult', () => { total: 30 }; - searchResult = new DocumentSearchResult(kuzzle, request, options, response); + searchResult = new DocumentsSearchResult(kuzzle, request, options, response); return should(searchResult.next()) .be.rejectedWith('Unable to retrieve next results from search: missing scrollId, from/sort, or from/size params'); @@ -116,12 +116,12 @@ describe('DocumentSearchResult', () => { aggregations: 'aggregations', total: 30 }; - searchResult = new DocumentSearchResult(kuzzle, request, options, response); + searchResult = new DocumentsSearchResult(kuzzle, request, options, response); kuzzle.query.resolves({result: nextResponse}); }); - it('should call document/scroll action with scrollId parameter and resolve to a new DocumentSearchResult', () => { + it('should call document/scroll action with scrollId parameter and resolve to a new DocumentsSearchResult', () => { return searchResult.next() .then(nextSearchResult => { should(kuzzle.query) @@ -133,7 +133,7 @@ describe('DocumentSearchResult', () => { scrollId: 'scroll-id' }, options); should(nextSearchResult).not.be.equal(searchResult); - should(nextSearchResult).be.instanceOf(DocumentSearchResult); + should(nextSearchResult).be.instanceOf(DocumentsSearchResult); }); }); @@ -175,12 +175,12 @@ describe('DocumentSearchResult', () => { aggregations: 'aggregations', total: 30 }; - searchResult = new DocumentSearchResult(kuzzle, request, options, response); + searchResult = new DocumentsSearchResult(kuzzle, request, options, response); kuzzle.query.resolves({result: nextResponse}); }); - it('should call document/search action with search_after parameter and resolve to a new DocumentSearchResult', () => { + it('should call document/search action with search_after parameter and resolve to a new DocumentsSearchResult', () => { return searchResult.next() .then(nextSearchResult => { should(kuzzle.query) @@ -200,7 +200,7 @@ describe('DocumentSearchResult', () => { size: 2 }, options); should(nextSearchResult).not.be.equal(searchResult); - should(nextSearchResult).be.instanceOf(DocumentSearchResult); + should(nextSearchResult).be.instanceOf(DocumentsSearchResult); }); }); @@ -219,7 +219,7 @@ describe('DocumentSearchResult', () => { it('should reject with an error if the sort is invalid', () => { request.body.sort = []; - searchResult = new DocumentSearchResult(kuzzle, request, options, response); + searchResult = new DocumentsSearchResult(kuzzle, request, options, response); return should(searchResult.next()) .be.rejected(); @@ -227,7 +227,7 @@ describe('DocumentSearchResult', () => { it('should reject if the sort combination does not allow to retrieve all the documents', () => { response.hits = []; - searchResult = new DocumentSearchResult(kuzzle, request, options, response); + searchResult = new DocumentsSearchResult(kuzzle, request, options, response); return should(searchResult.next()) .be.rejected(); @@ -256,7 +256,7 @@ describe('DocumentSearchResult', () => { aggregations: 'aggregations', total: 30 }; - searchResult = new DocumentSearchResult(kuzzle, request, options, response); + searchResult = new DocumentsSearchResult(kuzzle, request, options, response); kuzzle.query.resolves({result: nextResponse}); }); @@ -273,7 +273,7 @@ describe('DocumentSearchResult', () => { }); - it('should call document/search action with from/size parameters and resolve to a new DocumentSearchResult', () => { + it('should call document/search action with from/size parameters and resolve to a new DocumentsSearchResult', () => { return searchResult.next() .then(nextSearchResult => { should(kuzzle.query) @@ -288,7 +288,7 @@ describe('DocumentSearchResult', () => { from: 2 }, options); should(nextSearchResult).not.be.equal(searchResult); - should(nextSearchResult).be.instanceOf(DocumentSearchResult); + should(nextSearchResult).be.instanceOf(DocumentsSearchResult); }); }); diff --git a/test/kuzzle/listenersManagement.test.js b/test/kuzzle/listenersManagement.test.js index 06025882a..5ae65e5b8 100644 --- a/test/kuzzle/listenersManagement.test.js +++ b/test/kuzzle/listenersManagement.test.js @@ -1,35 +1,17 @@ -const - should = require('should'), - sinon = require('sinon'), - proxyquire = require('proxyquire'), - ProtocolMock = require('../mocks/protocol.mock'); +const should = require('should'); +const sinon = require('sinon'); -describe('Kuzzle listeners management', () => { - const - addListenerStub = sinon.stub(), - emitStub = sinon.stub(); - - let - Kuzzle, - kuzzle; - - before(() => { - const KuzzleEventEmitterMock = function() {}; - - KuzzleEventEmitterMock.prototype.addListener = addListenerStub; - KuzzleEventEmitterMock.prototype.emit = emitStub; - - Kuzzle = proxyquire('../../src/Kuzzle', { - './core/KuzzleEventEmitter': KuzzleEventEmitterMock - }); - }); +const ProtocolMock = require('../mocks/protocol.mock'); +const { Kuzzle } = require('../../src/Kuzzle'); +describe('Kuzzle listeners management', () => { + let kuzzle; beforeEach(() => { const protocol = new ProtocolMock(); kuzzle = new Kuzzle(protocol, {eventTimeout: 20}); - addListenerStub.reset(); - emitStub.reset(); + sinon.stub(kuzzle, '_superAddListener'); + sinon.stub(kuzzle, '_superEmit'); }); it('should only listen to allowed events', () => { @@ -46,16 +28,19 @@ describe('Kuzzle listeners management', () => { 'tokenExpired' ]; - should(function() {kuzzle.addListener('foo', sinon.stub());}).throw('[foo] is not a known event. Known events: ' + knownEvents.toString()); + should(function() { + kuzzle.addListener('foo', sinon.stub()); + }).throw('[foo] is not a known event. Known events: ' + knownEvents.toString()); let i; for (i = 0; i < knownEvents.length; i++) { kuzzle.addListener(knownEvents[i], sinon.stub()); } - should(addListenerStub.callCount).be.exactly(10); + should(kuzzle._superAddListener).have.called(10); + for (i = 0; i < knownEvents.length; i++) { - should(addListenerStub.getCall(i)).be.calledWith(knownEvents[i]); + should(kuzzle._superAddListener.getCall(i)).be.calledWith(knownEvents[i]); } }); @@ -68,18 +53,18 @@ describe('Kuzzle listeners management', () => { kuzzle.emit('offlineQueuePush', 'bar'); setTimeout(function () { - should(emitStub).be.calledTwice(); - should(emitStub.firstCall).be.calledWith('connected', 'foo'); - should(emitStub.secondCall).be.calledWith('offlineQueuePush', 'bar'); + should(kuzzle._superEmit).be.calledTwice(); + should(kuzzle._superEmit.firstCall).be.calledWith('connected', 'foo'); + should(kuzzle._superEmit.secondCall).be.calledWith('offlineQueuePush', 'bar'); - emitStub.reset(); + kuzzle._superEmit.reset(); setTimeout(function () { kuzzle.emit('connected', 'bar'); kuzzle.emit('connected', 'bar'); setTimeout(function () { - should(emitStub).be.calledOnce(); - should(emitStub).be.calledWith('connected', 'bar'); + should(kuzzle._superEmit).be.calledOnce(); + should(kuzzle._superEmit).be.calledWith('connected', 'bar'); done(); }, 0); }, 30); diff --git a/test/kuzzle/query.test.js b/test/kuzzle/query.test.js index 2a849dc41..fbba9e7b9 100644 --- a/test/kuzzle/query.test.js +++ b/test/kuzzle/query.test.js @@ -179,7 +179,7 @@ describe('Kuzzle query management', () => { }); it('should queue the request if queing and queuable', () => { - kuzzle.queuing = true; + kuzzle._queuing = true; const eventStub = sinon.stub(); kuzzle.addListener('offlineQueuePush', eventStub); @@ -197,7 +197,7 @@ describe('Kuzzle query management', () => { }); it('should not queue if the request is not queuable', () => { - kuzzle.queuing = true; + kuzzle._queuing = true; const eventStub = sinon.stub(); kuzzle.addListener('discarded', eventStub); @@ -222,7 +222,7 @@ describe('Kuzzle query management', () => { }); it('should not queue if queueFilter is set and says so', () => { - kuzzle.queuing = true; + kuzzle._queuing = true; kuzzle.queueFilter = () => false; return kuzzle.query({controller: 'foo', action: 'bar'}, {queuable: true}) diff --git a/test/protocol/Base.test.js b/test/protocol/Base.test.js index 524aa786b..bd2652459 100644 --- a/test/protocol/Base.test.js +++ b/test/protocol/Base.test.js @@ -2,7 +2,7 @@ const should = require('should'), sinon = require('sinon'), KuzzleError = require('../../src/KuzzleError'), - AbstractWrapper = require('../../src/protocols/abstract/Base'), + { KuzzleAbstractProtocol } = require('../../src/protocols/abstract/Base'), PendingRequest = require('../../src/protocols/abstract/PendingRequest'); describe('Common Protocol', () => { @@ -11,7 +11,7 @@ describe('Common Protocol', () => { protocol; beforeEach(function () { - protocol = new AbstractWrapper('somewhere'); + protocol = new KuzzleAbstractProtocol('somewhere'); protocol.send = function(request) { protocol.emit(request.requestId, request.response); }; @@ -20,23 +20,23 @@ describe('Common Protocol', () => { describe('#constructor', () => { it('should accept string as port', () => { - protocol = new AbstractWrapper('somewhere', { port: '443' }); + protocol = new KuzzleAbstractProtocol('somewhere', { port: '443' }); should(protocol.port).be.eql(443); }); it('should use 7512 when no port is given or when port is not a parseable number', () => { - protocol = new AbstractWrapper('somewhere', { port: 'foobar' }); + protocol = new KuzzleAbstractProtocol('somewhere', { port: 'foobar' }); should(protocol.port).be.eql(7512); - protocol = new AbstractWrapper('somewhere'); + protocol = new KuzzleAbstractProtocol('somewhere'); should(protocol.port).be.eql(7512); }); it('should accept number as port', () => { - protocol = new AbstractWrapper('somewhere', { port: 443 }); + protocol = new KuzzleAbstractProtocol('somewhere', { port: 443 }); should(protocol.port).be.eql(443); }); From 64b540267672d1629dc97b2b0f286fe193f0b37b Mon Sep 17 00:00:00 2001 From: Aschen Date: Tue, 14 Jul 2020 12:17:03 +0200 Subject: [PATCH 05/15] Disable webpack linter --- .gitignore | 1 + webpack.config.js | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index 3e79082de..b22a32b2c 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ test-*.js # Typescript related files *.d.ts *.js.map +index.js src/Kuzzle.js src/controllers/Auth.js src/controllers/Base.js diff --git a/webpack.config.js b/webpack.config.js index ddca56227..6c4e33c46 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -27,11 +27,6 @@ module.exports = { use: { loader: 'babel-loader' } - }, - { - test: /\.?js$/, - exclude: /node_modules/, - loader: 'eslint-loader' } ] }, From 872ff4e69ac47d879d247efacdc93f4ad57cd44a Mon Sep 17 00:00:00 2001 From: Aschen Date: Tue, 14 Jul 2020 12:29:47 +0200 Subject: [PATCH 06/15] Fix export --- index.ts | 1 + src/Kuzzle.ts | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/index.ts b/index.ts index 26f7dd211..2fbee87ec 100644 --- a/index.ts +++ b/index.ts @@ -38,3 +38,4 @@ const exported = { export default exported; +module.exports = exported; \ No newline at end of file diff --git a/src/Kuzzle.ts b/src/Kuzzle.ts index c864e0040..19fa26176 100644 --- a/src/Kuzzle.ts +++ b/src/Kuzzle.ts @@ -314,13 +314,14 @@ export class Kuzzle extends KuzzleEventEmitter { if (protectedEvent) { if ( protectedEvent.lastEmitted - && protectedEvent.lastEmitted > now - this.eventTimeout) - { + && protectedEvent.lastEmitted > now - this.eventTimeout + ) { return false; } protectedEvent.lastEmitted = now; } - return this._superEmit(eventName, ...payload) + + return this._superEmit(eventName, ...payload); } _superEmit (eventName, ...payload) { From 5122c9262750101ef7f0d04d29fbb68f69cd42bd Mon Sep 17 00:00:00 2001 From: Aschen Date: Thu, 16 Jul 2020 09:18:50 +0200 Subject: [PATCH 07/15] Trigger CI --- src/Kuzzle.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Kuzzle.ts b/src/Kuzzle.ts index 19fa26176..99e4b9aea 100644 --- a/src/Kuzzle.ts +++ b/src/Kuzzle.ts @@ -14,7 +14,7 @@ import { uuidv4 } from './utils/uuidv4'; import { proxify } from './utils/proxify'; import { JSONObject } from './utils/interfaces'; -// defined by webpack plugin +// Defined by webpack plugin declare const SDKVERSION: any; const events = [ From 32946d78b3acb6bc171b50ed57bd081d77e1d892 Mon Sep 17 00:00:00 2001 From: Aschen Date: Thu, 16 Jul 2020 12:20:45 +0200 Subject: [PATCH 08/15] Add links --- src/controllers/Auth.ts | 241 +++++++++++++++++++++++++--------------- 1 file changed, 150 insertions(+), 91 deletions(-) diff --git a/src/controllers/Auth.ts b/src/controllers/Auth.ts index abe88d9f5..832ad95a3 100644 --- a/src/controllers/Auth.ts +++ b/src/controllers/Auth.ts @@ -63,6 +63,8 @@ export class AuthController extends BaseController { /** * Creates a new API key for the currently loggued user. * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/create-api-key + * * @param description API key description * @param options Additional options * - "_id" API key unique ID @@ -74,7 +76,7 @@ export class AuthController extends BaseController { createApiKey( description: string, options: { _id?: string, expiresIn?: number, refresh?: string } = {} - ) { + ): Promise { const request = { action: 'createApiKey', _id: options._id, @@ -86,17 +88,19 @@ export class AuthController extends BaseController { }; return this.query(request) - .then(response => response.result as ApiKey); + .then(response => response.result); } /** * Deletes an API key for the currently loggued user. * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/delete-api-key + * * @param id API key ID * @param options Additional options * - "refresh" If set to `wait_for`, Kuzzle will not respond until the API key is indexed */ - deleteApiKey(id: string, options: { refresh?: string } = {}) { + deleteApiKey(id: string, options: { refresh?: string } = {}): Promise { const request = { action: 'deleteApiKey', _id: id, @@ -110,6 +114,8 @@ export class AuthController extends BaseController { /** * Searches API keys for the currently loggued user. * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/search-api-keys + * * @param query Search query * @param options Additional options * - "from" Offset of the first document to fetch @@ -120,7 +126,16 @@ export class AuthController extends BaseController { searchApiKeys( query: JSONObject = {}, options: { from?: number, size?: number } = {} - ) { + ): Promise<{ + /** + * Array of found ApiKeys + */ + hits: Array, + /** + * Total number of API keys found + */ + total: number + }> { const request = { action: 'searchApiKeys', from: options.from, @@ -129,26 +144,32 @@ export class AuthController extends BaseController { }; return this.query(request) - .then(response => response.result as { - /** - * Array of found ApiKeys - */ - hits: Array, - /** - * Total number of API keys found - */ - total: number - }); + .then(response => response.result); } /** * Checks whether a given jwt token still represents a valid session in Kuzzle. * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/check-token + * * @param token The jwt token to check (default to current SDK token) * * @returns A token validity object */ - checkToken (token?: string) { + checkToken (token?: string): Promise<{ + /** + * Tell if the token is valid or not + */ + valid: boolean, + /** + * Explain why the token is invalid + */ + state: string, + /** + * Token expiration timestamp + */ + expiresAt: number + }> { if (token === undefined && this.authenticationToken) { token = this.authenticationToken.encodedJwt; } @@ -157,25 +178,14 @@ export class AuthController extends BaseController { action: 'checkToken', body: { token } }, { queuable: false }) - .then(response => response.result as { - /** - * Tell if the token is valid or not - */ - valid: boolean, - /** - * Explain why the token is invalid - */ - state: string, - /** - * Token expiration timestamp - */ - expiresAt: number - }); + .then(response => response.result); } /** * Create credentials of the specified strategy for the current user. * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/create-my-credentials + * * @param strategy New credentials * @param credentials Name of the strategy to use * @param options Additional options @@ -188,55 +198,68 @@ export class AuthController extends BaseController { strategy: string, credentials: JSONObject, options: { queuable?: boolean } = {} - ) { + ): Promise { return this.query({ strategy, action: 'createMyCredentials', body: credentials }, options) - .then(response => response.result as JSONObject); + .then(response => response.result); } /** * Check the existence of the specified strategy's credentials for the current user. * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/credentials-exist + * * @param strategy Name of the strategy to use * @param options Additional options * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again * * @returns A boolean indicating if the credentials exists */ - credentialsExist (strategy: string, options: { queuable?: boolean } = {}) { + credentialsExist ( + strategy: string, + options: { queuable?: boolean } = {} + ): Promise { return this.query({ strategy, action: 'credentialsExist' }, options) - .then(response => response.result as boolean); + .then(response => response.result); } /** * Delete credentials of the specified strategy for the current user. * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/delete-my-credentials + * * @param strategy Name of the strategy to use * @param options Additional options * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again */ - deleteMyCredentials (strategy: string, options: { queuable?: boolean } = {}) { + deleteMyCredentials ( + strategy: string, + options: { queuable?: boolean } = {} + ): Promise { return this.query({ strategy, action: 'deleteMyCredentials' }, options) - .then(response => response.result.acknowledged as boolean); + .then(response => response.result.acknowledged); } /** * Fetches the current user. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/get-current-user + * * @param options Additional options * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again * * @returns Currently loggued User */ - getCurrentUser (options: { queuable?: boolean } = {}) { + getCurrentUser (options: { queuable?: boolean } = {}): Promise { return this.query({ action: 'getCurrentUser' }, options) @@ -249,6 +272,8 @@ export class AuthController extends BaseController { /** * Get credential information of the specified strategy for the current user. * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/get-my-credentials + * * @param strategy Name of the strategy to use * @param options Additional options * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again @@ -256,76 +281,91 @@ export class AuthController extends BaseController { * @returns An object representing the credentials for the provided authentication strategy. * Its content depends on the authentication strategy. */ - getMyCredentials(strategy, options: { queuable?: boolean } = {}) { + getMyCredentials( + strategy: string, + options: { queuable?: boolean } = {} + ): Promise { return this.query({ strategy, action: 'getMyCredentials' }, options) - .then(response => response.result as JSONObject); + .then(response => response.result); } /** * Gets the rights array of the currently logged user. * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/get-my-rights + * * @param options Additional options * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again * * @returns An array containing user rights objects */ - getMyRights (options: { queuable?: boolean } = {}) { + getMyRights ( + options: { queuable?: boolean } = {} + ): Promise> { return this.query({ action: 'getMyRights' }, options) - .then(response => response.result.hits as Array<{ - /** - * Controller on wich the rights are applied - */ - controller: string, - /** - * Action on wich the rights are applied - */ - action: string, - /** - * Index on wich the rights are applied - */ - index: string, - /** - * Collection on wich the rights are applied - */ - collection: string, - /** - * Rights ("allowed" or "denied") - */ - value: string - }>); + .then(response => response.result.hits); } /** * Get all the strategies registered in Kuzzle by all auth plugins * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/get-strategies + * * @param options Additional options * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again * * @returns An array of available strategies names */ - getStrategies (options: { queuable?: boolean } = {}) { + getStrategies (options: { queuable?: boolean } = {}): Promise> { return this.query({ action: 'getStrategies' }, options) - .then(response => response.result as Array); + .then(response => response.result); } /** * Send login request to kuzzle with credentials * If login success, store the jwt into kuzzle object * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/login + * * @param strategy Name of the strategy to use * @param credentials Credentials object for the strategy * @param expiresIn Expiration time in ms library format. (e.g. "2h") * * @returns The encrypted JSON Web Token */ - login (strategy: string, credentials: JSONObject, expiresIn: string | null = null) { + login ( + strategy: string, + credentials: JSONObject, + expiresIn: string = null + ): Promise { const request = { strategy, expiresIn, @@ -344,7 +384,7 @@ export class AuthController extends BaseController { return Promise.reject(err); } - return response.result.jwt as string; + return response.result.jwt; }) .catch(err => { this.kuzzle.emit('loginAttempt', {success: false, error: err.message}); @@ -354,11 +394,13 @@ export class AuthController extends BaseController { /** * Send logout request to kuzzle with jwt. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/logout */ - logout () { + logout (): Promise { return this.query({ action: 'logout' - }, {queuable: false}) + }, { queuable: false }) .then(() => { this._authenticationToken = null; }); @@ -367,6 +409,8 @@ export class AuthController extends BaseController { /** * Update credentials of the specified strategy for the current user. * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/update-my-credentials + * * @param strategy Name of the strategy to use * @param credentials Updated credentials * @param options Additional options @@ -379,26 +423,31 @@ export class AuthController extends BaseController { strategy: string, credentials: JSONObject, options: { queuable?: boolean } = {} - ) { + ): Promise { return this.query({ strategy, body: credentials, action: 'updateMyCredentials' }, options) - .then(response => response.result as JSONObject); + .then(response => response.result); } /** * Update current user in Kuzzle. * This route cannot update the list of associated security profiles. * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/update-self + * * @param {object} content - User custom information * @param options Additional options * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again * * @returns Currently loggued User */ - updateSelf (content: JSONObject, options: { queuable?: boolean } = {}) { + updateSelf ( + content: JSONObject, + options: { queuable?: boolean } = {} + ): Promise { return this.query({ body: content, action: 'updateSelf' @@ -412,30 +461,57 @@ export class AuthController extends BaseController { /** * Validate credentials of the specified strategy for the current user. * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/validate-my-credentials + * * @param strategy Name of the strategy to use * @param credentials Credentials to validate * @param options Additional options * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again */ - validateMyCredentials (strategy, credentials, options: { queuable?: boolean } = {}) { + validateMyCredentials ( + strategy: string, + credentials: JSONObject, + options: { queuable?: boolean } = {} + ): Promise { return this.query({ strategy, body: credentials, action: 'validateMyCredentials' }, options) - .then(response => response.result as boolean); + .then(response => response.result); } /** * Refresh the SDK current authentication token * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/refresh-token + * * @param options Additional options * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again * - "expiresIn" Expiration duration * * @returns The refreshed token */ - refreshToken(options: { queuable?: boolean, expiresIn?: number } = {}) { + refreshToken( + options: { queuable?: boolean, expiresIn?: number } = {} + ): Promise<{ + /** + * Token unique ID + */ + _id: string; + /** + * Expiration date in UNIX micro-timestamp format (-1 if the token never expires) + */ + expiresAt: number; + /** + * Authentication token associated with this API key + */ + jwt: string; + /** + * Original TTL in ms + */ + ttl: number; + }> { const query = { action: 'refreshToken', expiresIn: options.expiresIn @@ -445,24 +521,7 @@ export class AuthController extends BaseController { .then(response => { this._authenticationToken = new Jwt(response.result.jwt); - return response.result as { - /** - * Token unique ID - */ - _id: string; - /** - * Expiration date in UNIX micro-timestamp format (-1 if the token never expires) - */ - expiresAt: number; - /** - * Authentication token associated with this API key - */ - jwt: string; - /** - * Original TTL in ms - */ - ttl: number; - }; + return response.result; }); } } From 2f62b54222422057787eafde04dbc76fb857134f Mon Sep 17 00:00:00 2001 From: Aschen Date: Thu, 16 Jul 2020 12:30:31 +0200 Subject: [PATCH 09/15] fix tests --- .../m-delete-profiles/snippets/m-delete-profiles.test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/7/controllers/security/m-delete-profiles/snippets/m-delete-profiles.test.yml b/doc/7/controllers/security/m-delete-profiles/snippets/m-delete-profiles.test.yml index b017d8aea..6fbf0d363 100644 --- a/doc/7/controllers/security/m-delete-profiles/snippets/m-delete-profiles.test.yml +++ b/doc/7/controllers/security/m-delete-profiles/snippets/m-delete-profiles.test.yml @@ -8,4 +8,4 @@ hooks: }' kuzzle:7512/profiles/profile${i}/_create done template: default -expected: '^\[ ''profile1'', ''profile2'', ''profile3'', ''profile4'', ''profile5'' \]$' +expected: '^\[ ''profile\d'', ''profile\d'', ''profile\d'', ''profile\d', ''profile\d'' \]$' From 64d2f7718b56d484cc915d414ed2a65ed216feb8 Mon Sep 17 00:00:00 2001 From: Adrien Maret Date: Tue, 21 Jul 2020 10:54:53 +0200 Subject: [PATCH 10/15] Update src/controllers/Auth.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sébastien Cottinet --- src/controllers/Auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/Auth.ts b/src/controllers/Auth.ts index 832ad95a3..1f8fc72aa 100644 --- a/src/controllers/Auth.ts +++ b/src/controllers/Auth.ts @@ -47,7 +47,7 @@ export class AuthController extends BaseController { /** * Do not add the token for the checkToken route, to avoid getting a token error when - * a developer simply wish to verify his token + * a developer simply wishes to verify their token */ authenticateRequest (request: any) { if ( !this.authenticationToken From f157e19c1deb5aea9d03d97e53f6fdfc3d68da2b Mon Sep 17 00:00:00 2001 From: Adrien Maret Date: Tue, 21 Jul 2020 11:26:37 +0200 Subject: [PATCH 11/15] Update src/controllers/Auth.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sébastien Cottinet --- src/controllers/Auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/Auth.ts b/src/controllers/Auth.ts index 1f8fc72aa..f16ad9a58 100644 --- a/src/controllers/Auth.ts +++ b/src/controllers/Auth.ts @@ -493,7 +493,7 @@ export class AuthController extends BaseController { * @returns The refreshed token */ refreshToken( - options: { queuable?: boolean, expiresIn?: number } = {} + options: { queuable?: boolean, expiresIn?: number|string } = {} ): Promise<{ /** * Token unique ID From ad87c2ecbfcacafccf4da29c13b513257487392b Mon Sep 17 00:00:00 2001 From: Adrien Maret Date: Tue, 21 Jul 2020 11:26:45 +0200 Subject: [PATCH 12/15] Update src/controllers/Auth.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sébastien Cottinet --- src/controllers/Auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/controllers/Auth.ts b/src/controllers/Auth.ts index f16ad9a58..b04a7fb3d 100644 --- a/src/controllers/Auth.ts +++ b/src/controllers/Auth.ts @@ -364,7 +364,7 @@ export class AuthController extends BaseController { login ( strategy: string, credentials: JSONObject, - expiresIn: string = null + expiresIn?: string|number ): Promise { const request = { strategy, From 8bb630b30ef56529a9ea4bcf8301210b31c8d25e Mon Sep 17 00:00:00 2001 From: Aschen Date: Tue, 21 Jul 2020 11:30:04 +0200 Subject: [PATCH 13/15] apply pr review --- src/controllers/Auth.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/controllers/Auth.ts b/src/controllers/Auth.ts index 832ad95a3..676c970ba 100644 --- a/src/controllers/Auth.ts +++ b/src/controllers/Auth.ts @@ -375,14 +375,9 @@ export class AuthController extends BaseController { return this.query(request, {queuable: false, verb: 'POST'}) .then(response => { - try { - this._authenticationToken = new Jwt(response.result.jwt); - - this.kuzzle.emit('loginAttempt', {success: true}); - } - catch (err) { - return Promise.reject(err); - } + this._authenticationToken = new Jwt(response.result.jwt); + + this.kuzzle.emit('loginAttempt', {success: true}); return response.result.jwt; }) From 36e6649461e22760a9c01ae203ef85fe2326840d Mon Sep 17 00:00:00 2001 From: Aschen Date: Tue, 21 Jul 2020 11:35:35 +0200 Subject: [PATCH 14/15] Use sinon.stub on KuzzleEventEmitter --- test/kuzzle/listenersManagement.test.js | 27 +++++++++++++++---------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/test/kuzzle/listenersManagement.test.js b/test/kuzzle/listenersManagement.test.js index 5ae65e5b8..0bd0599dc 100644 --- a/test/kuzzle/listenersManagement.test.js +++ b/test/kuzzle/listenersManagement.test.js @@ -1,8 +1,9 @@ const should = require('should'); const sinon = require('sinon'); -const ProtocolMock = require('../mocks/protocol.mock'); const { Kuzzle } = require('../../src/Kuzzle'); +const { KuzzleEventEmitter } =require('../../src/core/KuzzleEventEmitter'); +const ProtocolMock = require('../mocks/protocol.mock'); describe('Kuzzle listeners management', () => { let kuzzle; @@ -10,8 +11,12 @@ describe('Kuzzle listeners management', () => { beforeEach(() => { const protocol = new ProtocolMock(); kuzzle = new Kuzzle(protocol, {eventTimeout: 20}); - sinon.stub(kuzzle, '_superAddListener'); - sinon.stub(kuzzle, '_superEmit'); + sinon.stub(KuzzleEventEmitter.prototype, 'addListener').resolves(); + sinon.stub(KuzzleEventEmitter.prototype, 'emit').resolves(); + }); + + afterEach(() => { + sinon.restore(); }); it('should only listen to allowed events', () => { @@ -37,10 +42,10 @@ describe('Kuzzle listeners management', () => { kuzzle.addListener(knownEvents[i], sinon.stub()); } - should(kuzzle._superAddListener).have.called(10); + should(KuzzleEventEmitter.prototype.addListener).have.called(10); for (i = 0; i < knownEvents.length; i++) { - should(kuzzle._superAddListener.getCall(i)).be.calledWith(knownEvents[i]); + should(KuzzleEventEmitter.prototype.addListener.getCall(i)).be.calledWith(knownEvents[i]); } }); @@ -53,18 +58,18 @@ describe('Kuzzle listeners management', () => { kuzzle.emit('offlineQueuePush', 'bar'); setTimeout(function () { - should(kuzzle._superEmit).be.calledTwice(); - should(kuzzle._superEmit.firstCall).be.calledWith('connected', 'foo'); - should(kuzzle._superEmit.secondCall).be.calledWith('offlineQueuePush', 'bar'); + should(KuzzleEventEmitter.prototype.emit).be.calledTwice(); + should(KuzzleEventEmitter.prototype.emit.firstCall).be.calledWith('connected', 'foo'); + should(KuzzleEventEmitter.prototype.emit.secondCall).be.calledWith('offlineQueuePush', 'bar'); - kuzzle._superEmit.reset(); + KuzzleEventEmitter.prototype.emit.reset(); setTimeout(function () { kuzzle.emit('connected', 'bar'); kuzzle.emit('connected', 'bar'); setTimeout(function () { - should(kuzzle._superEmit).be.calledOnce(); - should(kuzzle._superEmit).be.calledWith('connected', 'bar'); + should(KuzzleEventEmitter.prototype.emit).be.calledOnce(); + should(KuzzleEventEmitter.prototype.emit).be.calledWith('connected', 'bar'); done(); }, 0); }, 30); From 6cdd726c27fc1a11eee75a6cc598b06dcceb1d12 Mon Sep 17 00:00:00 2001 From: Adrien Maret Date: Tue, 21 Jul 2020 17:27:55 +0200 Subject: [PATCH 15/15] Add typescript support for Document controller (#527) Adds TS support for the Document controller --- .eslintignore | 3 + .gitignore | 3 + doc/7/controllers/document/validate/index.md | 2 +- src/Kuzzle.ts | 8 +- src/controllers/Auth.ts | 36 +- src/controllers/Document.js | 254 ------ src/controllers/Document.ts | 805 ++++++++++++++++++ .../searchResult/{Document.js => Document.ts} | 6 +- ...earchResultBase.js => SearchResultBase.ts} | 70 +- src/utils/interfaces.ts | 56 ++ tsconfig.json | 3 +- 11 files changed, 961 insertions(+), 285 deletions(-) delete mode 100644 src/controllers/Document.js create mode 100644 src/controllers/Document.ts rename src/core/searchResult/{Document.js => Document.ts} (65%) rename src/core/searchResult/{SearchResultBase.js => SearchResultBase.ts} (70%) diff --git a/.eslintignore b/.eslintignore index 90bea7150..d849ec6cc 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,8 +4,11 @@ src/**/*.js.map src/Kuzzle.js src/controllers/Auth.js +src/controllers/Document.js src/controllers/Base.js src/core/security/User.js src/core/security/Profile.js src/core/security/Role.js src/utils/interfaces.js +src/core/searchResult/SearchResultBase.js +src/core/searchResult/Document.js diff --git a/.gitignore b/.gitignore index b22a32b2c..1a1af6994 100644 --- a/.gitignore +++ b/.gitignore @@ -32,8 +32,11 @@ test-*.js index.js src/Kuzzle.js src/controllers/Auth.js +src/controllers/Document.js src/controllers/Base.js src/core/security/User.js src/core/security/Profile.js src/core/security/Role.js src/utils/interfaces.js +src/core/searchResult/SearchResultBase.js +src/core/searchResult/Document.js diff --git a/doc/7/controllers/document/validate/index.md b/doc/7/controllers/document/validate/index.md index e274a21c1..b87199cda 100644 --- a/doc/7/controllers/document/validate/index.md +++ b/doc/7/controllers/document/validate/index.md @@ -7,7 +7,7 @@ description: Validate a document # validate -Validates data against existing validation rules. +Validates a document against existing validation rules. Note that if no validation specifications are set for the ``/``, the document will always be valid. diff --git a/src/Kuzzle.ts b/src/Kuzzle.ts index 99e4b9aea..98a27138e 100644 --- a/src/Kuzzle.ts +++ b/src/Kuzzle.ts @@ -12,7 +12,7 @@ import { MemoryStorageController } from './controllers/MemoryStorage'; import { uuidv4 } from './utils/uuidv4'; import { proxify } from './utils/proxify'; -import { JSONObject } from './utils/interfaces'; +import { JSONObject, KuzzleRequest } from './utils/interfaces'; // Defined by webpack plugin declare const SDKVERSION: any; @@ -59,7 +59,7 @@ export class Kuzzle extends KuzzleEventEmitter { public auth: AuthController; public bulk: any; public collection: any; - public document: any; + public document: DocumentController; public index: any; public ms: any; public realtime: any; @@ -332,7 +332,7 @@ export class Kuzzle extends KuzzleEventEmitter { * Connects to a Kuzzle instance using the provided host name * @returns {Promise} */ - connect () { + connect (): Promise { if (this.protocol.isReady()) { return Promise.resolve(); } @@ -455,7 +455,7 @@ export class Kuzzle extends KuzzleEventEmitter { * @param {object} [options] - Optional arguments * @returns {Promise} */ - query (request: any = {}, options: any = {}) { + query (request: KuzzleRequest = {}, options: JSONObject = {}): Promise { if (typeof request !== 'object' || Array.isArray(request)) { throw new Error(`Kuzzle.query: Invalid request: ${JSON.stringify(request)}`); } diff --git a/src/controllers/Auth.ts b/src/controllers/Auth.ts index b23e6b9e8..2d8ef3ce6 100644 --- a/src/controllers/Auth.ts +++ b/src/controllers/Auth.ts @@ -67,9 +67,9 @@ export class AuthController extends BaseController { * * @param description API key description * @param options Additional options - * - "_id" API key unique ID - * - "refresh" If set to `wait_for`, Kuzzle will not respond until the API key is indexed - * - "expiresIn" Expiration duration + * - `_id` API key unique ID + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * - `expiresIn` Expiration duration * * @returns The created API key */ @@ -98,7 +98,7 @@ export class AuthController extends BaseController { * * @param id API key ID * @param options Additional options - * - "refresh" If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed */ deleteApiKey(id: string, options: { refresh?: string } = {}): Promise { const request = { @@ -118,8 +118,8 @@ export class AuthController extends BaseController { * * @param query Search query * @param options Additional options - * - "from" Offset of the first document to fetch - * - "size" Maximum number of documents to retrieve per page + * - `from` Offset of the first document to fetch + * - `size` Maximum number of documents to retrieve per page * * @returns A search result object */ @@ -189,7 +189,7 @@ export class AuthController extends BaseController { * @param strategy New credentials * @param credentials Name of the strategy to use * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns An object representing the new credentials. * The content depends on the authentication strategy @@ -214,7 +214,7 @@ export class AuthController extends BaseController { * * @param strategy Name of the strategy to use * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns A boolean indicating if the credentials exists */ @@ -236,7 +236,7 @@ export class AuthController extends BaseController { * * @param strategy Name of the strategy to use * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again */ deleteMyCredentials ( strategy: string, @@ -255,7 +255,7 @@ export class AuthController extends BaseController { * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/get-current-user * * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns Currently loggued User */ @@ -276,7 +276,7 @@ export class AuthController extends BaseController { * * @param strategy Name of the strategy to use * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns An object representing the credentials for the provided authentication strategy. * Its content depends on the authentication strategy. @@ -298,7 +298,7 @@ export class AuthController extends BaseController { * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/get-my-rights * * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns An array containing user rights objects */ @@ -338,7 +338,7 @@ export class AuthController extends BaseController { * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/get-strategies * * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns An array of available strategies names */ @@ -409,7 +409,7 @@ export class AuthController extends BaseController { * @param strategy Name of the strategy to use * @param credentials Updated credentials * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns An object representing the updated credentials. * The content depends on the authentication strategy @@ -435,7 +435,7 @@ export class AuthController extends BaseController { * * @param {object} content - User custom information * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns Currently loggued User */ @@ -461,7 +461,7 @@ export class AuthController extends BaseController { * @param strategy Name of the strategy to use * @param credentials Credentials to validate * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again */ validateMyCredentials ( strategy: string, @@ -482,8 +482,8 @@ export class AuthController extends BaseController { * @see https://docs.kuzzle.io/sdk/js/7/controllers/auth/refresh-token * * @param options Additional options - * - "queuable" If true, queues the request during downtime, until connected to Kuzzle again - * - "expiresIn" Expiration duration + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `expiresIn` Expiration duration * * @returns The refreshed token */ diff --git a/src/controllers/Document.js b/src/controllers/Document.js deleted file mode 100644 index cc88e76b8..000000000 --- a/src/controllers/Document.js +++ /dev/null @@ -1,254 +0,0 @@ -const { BaseController } = require('./Base'); -const { DocumentsSearchResult } = require('../core/searchResult/Document'); - -class DocumentController extends BaseController { - - /** - * @param {Kuzzle} kuzzle - */ - constructor (kuzzle) { - super(kuzzle, 'document'); - } - - count (index, collection, body, options = {}) { - const request = { - index, - collection, - body, - action: 'count' - }; - - return this.query(request, options) - .then(response => response.result.count); - } - - create (index, collection, document, _id = null, options = {}) { - const request = { - index, - collection, - _id, - body: document, - action: 'create' - }; - return this.query(request, options) - .then(response => response.result); - } - - createOrReplace (index, collection, _id, body, options = {}) { - const request = { - index, - collection, - _id, - body, - action: 'createOrReplace' - }; - - return this.query(request, options) - .then(response => response.result); - } - - delete (index, collection, _id, options = {}) { - const request = { - index, - collection, - _id, - action: 'delete' - }; - - return this.query(request, options) - .then(response => response.result._id); - } - - deleteByQuery(index, collection, body = {}, options = {}) { - const request = { - index, - collection, - body, - action: 'deleteByQuery' - }; - - return this.query(request, options) - .then(response => response.result.ids); - } - - exists (index, collection, _id, options = {}) { - const request = { - index, - collection, - _id, - action: 'exists' - }; - - return this.query(request, options) - .then(response => response.result); - } - - get (index, collection, _id, options = {}) { - const request = { - index, - collection, - _id, - action: 'get' - }; - - return this.query(request, options) - .then(response => response.result); - } - - mCreate (index, collection, documents, options = {}) { - const request = { - index, - collection, - body: {documents}, - action: 'mCreate' - }; - - return this.query(request, options) - .then(response => response.result); - } - - mCreateOrReplace (index, collection, documents, options = {}) { - const request = { - index, - collection, - body: {documents}, - action: 'mCreateOrReplace' - }; - - return this.query(request, options) - .then(response => response.result); - } - - mDelete (index, collection, ids, options = {}) { - const request = { - index, - collection, - body: {ids}, - action: 'mDelete' - }; - - return this.query(request, options) - .then(response => response.result); - } - - mGet (index, collection, ids, options = {}) { - const request = { - index, - collection, - action: 'mGet', - body: {ids} - }; - - return this.query(request, options) - .then(response => response.result); - } - - mReplace (index, collection, documents, options = {}) { - const request = { - index, - collection, - body: {documents}, - action: 'mReplace' - }; - - return this.query(request, options) - .then(response => response.result); - } - - mUpdate (index, collection, documents, options = {}) { - const request = { - index, - collection, - body: {documents}, - action: 'mUpdate' - }; - - return this.query(request, options) - .then(response => response.result); - } - - replace (index, collection, _id, body, options = {}) { - const request = { - index, - collection, - _id, - body, - action: 'replace' - }; - - return this.query(request, options) - .then(response => response.result); - } - - search (index, collection, body = {}, options = {}) { - return this._search(index, collection, body, options) - .then(({ response, request, opts }) => ( - new DocumentsSearchResult(this.kuzzle, request, opts, response.result) - )); - } - - _search (index, collection, body = {}, options = {}) { - const request = { - index, - collection, - body: null, - action: 'search', - }; - if ( this.kuzzle.protocol.name === 'http' - && options.verb - && options.verb.toLowerCase() === 'get' - ) { - request.searchBody = body; - } - else { - request.body = body; - } - for (const opt of ['from', 'size', 'scroll']) { - request[opt] = options[opt]; - } - - const opts = { verb: options.verb || 'POST', ...options }; - return this.query(request, opts) - .then(response => ({ response, request, opts })); - } - - update (index, collection, _id, body, options = {}) { - const request = { - index, - collection, - _id, - body, - action: 'update', - retryOnConflict: options.retryOnConflict, - source: options.source - }; - - return this.query(request, options) - .then(response => response.result); - } - - updateByQuery(index, collection, searchQuery, changes, options = {}) { - const request = { - index, - collection, - body: {query: searchQuery, changes}, - action: 'updateByQuery', - source: options.source - }; - - return this.query(request, options) - .then(response => response.result); - } - - validate (index, collection, body, options = {}) { - return this.query({ - index, - collection, - body, - action: 'validate' - }, options) - .then(response => response.result); - } -} - -module.exports = { DocumentController }; diff --git a/src/controllers/Document.ts b/src/controllers/Document.ts new file mode 100644 index 000000000..c79d48e69 --- /dev/null +++ b/src/controllers/Document.ts @@ -0,0 +1,805 @@ +import { BaseController } from './Base'; +import { SearchResult } from '../core/searchResult/SearchResultBase'; +import { DocumentsSearchResult } from '../core/searchResult/Document'; +import { JSONObject, Document, DocumentHit } from '../utils/interfaces'; + +export class DocumentController extends BaseController { + constructor (kuzzle) { + super(kuzzle, 'document'); + } + + /** + * Counts documents in a collection. + * + * A query can be provided to alter the count result, + * otherwise returns the total number of documents in the collection. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/count/ + * + * @param index Index name + * @param collection Collection name + * @param query Query to match + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns The number of matching documents + */ + count ( + index: string, + collection: string, + body: JSONObject = null, + options: { queuable?: boolean } = {} + ): Promise { + const request = { + index, + collection, + body, + action: 'count' + }; + + return this.query(request, options) + .then(response => response.result.count); + } + + /** + * Creates a new document in the persistent data storage. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/create/ + * + * @param index Index name + * @param collection Collection name + * @param content Document content + * @param _id Optional document ID + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns The created document + */ + create ( + index: string, + collection: string, + content: JSONObject, + _id: string = null, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise { + const request = { + index, + collection, + _id, + body: content, + action: 'create' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Creates a new document in the persistent data storage, + * or replaces its content if it already exists. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/create-or-replace/ + * + * @param index Index name + * @param collection Collection name + * @param id Document ID + * @param content Document content + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns The created or replaced document + */ + createOrReplace ( + index: string, + collection: string, + _id: string, + content: JSONObject, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise { + const request = { + index, + collection, + _id, + body: content, + action: 'createOrReplace' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Deletes a document. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/delete/ + * + * @param index Index name + * @param collection Collection name + * @param _id Document ID + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns The document ID + */ + delete ( + index: string, + collection: string, + _id: string, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise { + const request = { + index, + collection, + _id, + action: 'delete' + }; + + return this.query(request, options) + .then(response => response.result._id); + } + + /** + * Deletes documents matching the provided search query. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/delete-by-query/ + * + * @param index Index name + * @param collection Collection name + * @param query Query to match + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns The deleted documents IDs + */ + deleteByQuery( + index: string, + collection: string, + query: JSONObject = {}, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise> { + const request = { + index, + collection, + body: query, + action: 'deleteByQuery' + }; + + return this.query(request, options) + .then(response => response.result.ids); + } + + /** + * Checks if the given document exists. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/exists/ + * + * @param index Index name + * @param collection Collection name + * @param _id Document ID + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns True if the document exists + */ + exists ( + index: string, + collection: string, + _id: string, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise { + const request = { + index, + collection, + _id, + action: 'exists' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Gets a document. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/get/ + * + * @param index Index name + * @param collection Collection name + * @param _id Document ID + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns The document + */ + get ( + index: string, + collection: string, + _id: string, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise { + const request = { + index, + collection, + _id, + action: 'get' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Creates multiple documents. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/m-create/ + * + * @param index Index name + * @param collection Collection name + * @param documents Documents to create + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + mCreate ( + index: string, + collection: string, + documents: Array<{ + /** + * Optional document ID + */ + _id?: string; + /** + * Document content + */ + body: JSONObject; + }>, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise<{ + /** + * Array of successfully created documents + */ + successes: Array; + /** + * Array of failed creation + */ + errors: Array<{ + /** + * Document that cause the error + */ + document: Document; + /** + * HTTP error status + */ + status: number; + /** + * Human readable reason + */ + reason: string; + }>; + }> { + const request = { + index, + collection, + body: { documents }, + action: 'mCreate' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Creates or replaces multiple documents. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/m-create-or-replace/ + * + * @param index Index name + * @param collection Collection name + * @param documents Documents to create + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + mCreateOrReplace ( + index: string, + collection: string, + documents: Array<{ + /** + * Document ID + */ + _id: string; + /** + * Document content + */ + body: JSONObject; + }>, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise<{ + /** + * Array of successfully created documents + */ + successes: Array; + /** + * Array of failed creation + */ + errors: Array<{ + /** + * Document that cause the error + */ + document: Document; + /** + * HTTP error status + */ + status: number; + /** + * Human readable reason + */ + reason: string; + }>; + }> { + const request = { + index, + collection, + body: { documents }, + action: 'mCreateOrReplace' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Deletes multiple documents. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/m-delete/ + * + * @param index Index name + * @param collection Collection name + * @param ids Document IDs + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + mDelete ( + index: string, + collection: string, + ids: Array, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise<{ + /** + * Array of successfully deleted documents IDS + */ + successes: Array; + /** + * Array of failed deletion + */ + errors: Array<{ + /** + * Document ID + */ + id: string; + /** + * Human readable reason + */ + reason: string; + }>; + }> { + const request = { + index, + collection, + body: {ids}, + action: 'mDelete' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Gets multiple documents. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/m-get/ + * + * @param index Index name + * @param collection Collection name + * @param ids Document IDs + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `verb` (HTTP only) Forces the verb of the route + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + mGet ( + index: string, + collection: string, + ids: Array, + options: { queuable?: boolean, verb?: string } = {} + ): Promise<{ + /** + * Array of successfully retrieved documents + */ + successes: Array; + /** + * Array of the IDs of not found documents. + */ + errors: Array; + }> { + const request = { + index, + collection, + action: 'mGet', + body: { ids } + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Replaces multiple documents. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/m-replace/ + * + * @param index Index name + * @param collection Collection name + * @param documents Documents to create + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + mReplace ( + index: string, + collection: string, + documents: Array<{ + /** + * Document ID + */ + _id: string; + /** + * Document content + */ + body: JSONObject; + }>, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise<{ + /** + * Array of successfully replaced documents + */ + successes: Array; + /** + * Array of failed creation + */ + errors: Array<{ + /** + * Document that cause the error + */ + document: Document; + /** + * HTTP error status + */ + status: number; + /** + * Human readable reason + */ + reason: string; + }>; + }> { + const request = { + index, + collection, + body: { documents }, + action: 'mReplace' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Updates multiple documents. + * + * Conflicts may occur if the same document gets updated multiple times + * within a short timespan in a database cluster. (See `retryOnConflict`) + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/m-update/ + * + * @param index Index name + * @param collection Collection name + * @param documents Documents to create + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * - `retryOnConflict` Number of times the database layer should retry in case of version conflict + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + mUpdate ( + index: string, + collection: string, + documents: Array<{ + /** + * Document ID + */ + _id: string; + /** + * Document content + */ + body: JSONObject; + }>, + options: { queuable?: boolean, refresh?: string, retryOnConflict?: number } = {} + ): Promise<{ + /** + * Array of successfully updated documents + */ + successes: Array; + /** + * Array of failed creation + */ + errors: Array<{ + /** + * Document that cause the error + */ + document: Document; + /** + * HTTP error status + */ + status: number; + /** + * Human readable reason + */ + reason: string; + }>; + }> { + const request = { + index, + collection, + body: {documents}, + action: 'mUpdate' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Replaces the content of an existing document. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/replace/ + * + * @param index Index name + * @param collection Collection name + * @param id Document ID + * @param content Document content + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * + * @returns The replaced document + */ + replace ( + index: string, + collection: string, + _id: string, + content: JSONObject, + options: { queuable?: boolean, refresh?: string } = {} + ): Promise { + const request = { + index, + collection, + _id, + body: content, + action: 'replace' + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Searches documents. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/search/ + * + * @param index Index name + * @param collection Collection name + * @param query Search query + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `from` Offset of the first document to fetch + * - `size` Maximum number of documents to retrieve per page + * - `scroll` When set, gets a forward-only cursor having its ttl set to the given value (e.g. `30s`) + * - `verb` (HTTP only) Forces the verb of the route + * + * @returns A SearchResult + */ + search ( + index: string, + collection: string, + query: JSONObject = {}, + options: { + queuable?: boolean; + from?: number; + size?: number; + scroll?: string; + verb?: string; + } = {} + ): Promise> { + return this._search(index, collection, query, options) + .then(({ response, request, opts }) => ( + new DocumentsSearchResult(this.kuzzle, request, opts, response.result) + )); + } + + private _search ( + index: string, + collection: string, + body: JSONObject = {}, + options: JSONObject = {} + ) { + const request: any = { + index, + collection, + body: null, + action: 'search', + }; + if ( this.kuzzle.protocol.name === 'http' + && options.verb + && options.verb.toLowerCase() === 'get' + ) { + request.searchBody = body; + } + else { + request.body = body; + } + for (const opt of ['from', 'size', 'scroll']) { + request[opt] = options[opt]; + } + + const opts = { verb: options.verb || 'POST', ...options }; + return this.query(request, opts) + .then(response => ({ response, request, opts })); + } + + /** + * Updates the content of an existing document. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/update/ + * + * @param index Index name + * @param collection Collection name + * @param id Document ID + * @param content Document content + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * - `retryOnConflict` Number of times the database layer should retry in case of version conflict + * - `source` If true, returns the updated document inside the response + * + * @returns The replaced document + */ + update ( + index: string, + collection: string, + _id: string, + content: JSONObject, + options: { + queuable?: boolean, + refresh?: string, + retryOnConflict?: number, + source?: boolean + } = {} + ): Promise { + const request = { + index, + collection, + _id, + body: content, + action: 'update', + retryOnConflict: options.retryOnConflict, + source: options.source + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Updates documents matching the provided search query. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/update-by-query/ + * + * @param index Index name + * @param collection Collection name + * @param query Query to match + * @param changes Partial changes to apply to the documents + * @param options Additional options + * - `refresh` If set to `wait_for`, Kuzzle will not respond until the API key is indexed + * - `source` If true, returns the updated document inside the response + * + * @returns An object containing 2 arrays: "successes" and "errors" + */ + updateByQuery( + index: string, + collection: string, + query: JSONObject, + changes: JSONObject, + options: { refresh?: string, source?: boolean } = {} + ): Promise<{ + /** + * Array of successfully updated documents + */ + successes: Array; + /** + * Array of failed creation + */ + errors: Array<{ + /** + * Document that cause the error + */ + document: Document; + /** + * HTTP error status + */ + status: number; + /** + * Human readable reason + */ + reason: string; + }>; + }> { + const request = { + index, + collection, + body: { query, changes }, + action: 'updateByQuery', + source: options.source + }; + + return this.query(request, options) + .then(response => response.result); + } + + /** + * Validates a document against existing validation rules. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/document/validate/ + * + * @param index Index name + * @param collection Collection name + * @param content Document content + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns True if the document is valid + */ + validate ( + index: string, + collection: string, + content: JSONObject, + options: { queuable?: boolean } = {} + ): Promise { + return this.query({ + index, + collection, + body: content, + action: 'validate' + }, options) + .then(response => response.result); + } +} + +module.exports = { DocumentController }; diff --git a/src/core/searchResult/Document.js b/src/core/searchResult/Document.ts similarity index 65% rename from src/core/searchResult/Document.js rename to src/core/searchResult/Document.ts index f4104eedc..9902d6864 100644 --- a/src/core/searchResult/Document.js +++ b/src/core/searchResult/Document.ts @@ -1,7 +1,7 @@ -const { SearchResultBase } = require('./SearchResultBase'); - -class DocumentsSearchResult extends SearchResultBase { +import { SearchResultBase } from './SearchResultBase'; +import { DocumentHit } from '../../utils/interfaces'; +export class DocumentsSearchResult extends SearchResultBase { /** * @param {Kuzzle} kuzzle * @param {object} query diff --git a/src/core/searchResult/SearchResultBase.js b/src/core/searchResult/SearchResultBase.ts similarity index 70% rename from src/core/searchResult/SearchResultBase.js rename to src/core/searchResult/SearchResultBase.ts index b9c8af7f2..a6c2da445 100644 --- a/src/core/searchResult/SearchResultBase.js +++ b/src/core/searchResult/SearchResultBase.ts @@ -1,4 +1,59 @@ -class SearchResultBase { +import { JSONObject, KuzzleRequest } from '../../utils/interfaces'; +import { Kuzzle } from '../../Kuzzle'; + +export interface SearchResult { + /** + * Search aggregations + */ + aggregations?: JSONObject; + + /** + * Page results + */ + hits: Array; + + /** + * Total number of items that can be retrieved + */ + total: number; + + /** + * Number of retrieved items so far + */ + fetched: number; + + /** + * Advances through the search results and returns the next page of items. + * + * @see https://docs.kuzzle.io/sdk/js/7/core-classes/search-result/next/ + * + * @example + * while (result) { + * // process result.hits here + * result = await result.next(); + * } + * + * @returns A SearchResult or null if no more pages + */ + next (): Promise | null>; +} + +export class SearchResultBase implements SearchResult { + protected _searchAction: string; + protected _scrollAction: string; + protected _controller: string; + protected _request: KuzzleRequest; + protected _kuzzle: Kuzzle; + protected _options: JSONObject; + protected _response: JSONObject; + + public aggregations?: JSONObject; + + public hits: Array; + + public total: number; + + public fetched: number; /** * @@ -7,7 +62,12 @@ class SearchResultBase { * @param {object} options * @param {object} response */ - constructor (kuzzle, request = {}, options = {}, response = {}) { + constructor ( + kuzzle: Kuzzle, + request: KuzzleRequest = {}, + options: any = {}, + response: any = {} + ) { Reflect.defineProperty(this, '_kuzzle', { value: kuzzle }); @@ -31,7 +91,7 @@ class SearchResultBase { this.total = response.total || 0; } - next () { + next (): Promise | null> { if (this.fetched >= this.total) { return Promise.resolve(null); } @@ -120,7 +180,9 @@ class SearchResultBase { } _buildNextSearchResult (response) { - const nextSearchResult = new this.constructor(this._kuzzle, this._request, this._options, response.result); + const Constructor: any = this.constructor; + + const nextSearchResult = new Constructor(this._kuzzle, this._request, this._options, response.result); nextSearchResult.fetched += this.fetched; return nextSearchResult; diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index 7732cdb03..6062231a3 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -129,3 +129,59 @@ export interface ApiKey { token: string; } } + +/** + * Kuzzle document + * + * @property _id + * @property _version + * @property _source + */ +export interface Document { + /** + * Document unique ID + */ + _id?: string; + /** + * Document Version (generated by Elasticsearch) + */ + _version?: number; + /** + * Document Content + */ + _source?: { + [key: string]: JSONObject | any, + /** + * Kuzzle metadata + * @see https://docs.kuzzle.io/core/2/guides/essentials/document-metadata/ + */ + _kuzzle_info?: { + /** + * Kuid of the user who created the document + */ + author: string, + /** + * Creation date in micro-timestamp + */ + createdAt: number, + /** + * Kuid of the user who last updated the document + */ + updater: string | null, + /** + * Update date in micro-timestamp + */ + updatedAt: number | null + } + }; +} + +/** + * Document retrieved from a search + */ +export interface DocumentHit extends Document { + /** + * Relevance score + */ + _score: number; +} diff --git a/tsconfig.json b/tsconfig.json index 87d3ed6ac..1ea049850 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,8 @@ "target": "es2020", "moduleResolution": "node", "sourceMap": true, - "baseUrl": "." + "baseUrl": ".", + "resolveJsonModule": true }, "rootDir": "src/", "include": [