From 1bcc0d331d749fe3f6920a7c0cd70b3d4175f553 Mon Sep 17 00:00:00 2001 From: Aschen Date: Tue, 14 Jul 2020 09:32:24 +0200 Subject: [PATCH 01/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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/24] 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 d3a2e1ee7235f596037505d9ae1893a46453a38e Mon Sep 17 00:00:00 2001 From: Aschen Date: Thu, 16 Jul 2020 09:58:26 +0200 Subject: [PATCH 08/24] WIP --- src/controllers/Document.js | 254 --------------------- src/controllers/Document.ts | 426 ++++++++++++++++++++++++++++++++++++ src/utils/interfaces.ts | 42 ++++ 3 files changed, 468 insertions(+), 254 deletions(-) delete mode 100644 src/controllers/Document.js create mode 100644 src/controllers/Document.ts 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..4c719d7da --- /dev/null +++ b/src/controllers/Document.ts @@ -0,0 +1,426 @@ +import { BaseController } from './Base'; +import { DocumentsSearchResult } from '../core/searchResult/Document'; +import { JSONObject, Document } 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. + * + * @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, + 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. + * + * @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. + * + * @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. + * + * @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. + * + * @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. + * + * @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. + * + * @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. + * + * @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); + } + + 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/utils/interfaces.ts b/src/utils/interfaces.ts index 7732cdb03..d0804bccc 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -129,3 +129,45 @@ export interface ApiKey { token: string; } } + +/** + * Kuzzle document + */ +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 + } + }; +} \ No newline at end of file From 1529dfe51f17f32c2cc4995e81dd4be7901f48ba Mon Sep 17 00:00:00 2001 From: Aschen Date: Thu, 16 Jul 2020 09:58:52 +0200 Subject: [PATCH 09/24] WIP --- .eslintignore | 1 + .gitignore | 1 + 2 files changed, 2 insertions(+) diff --git a/.eslintignore b/.eslintignore index 90bea7150..acfc19d98 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,6 +4,7 @@ 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 diff --git a/.gitignore b/.gitignore index b22a32b2c..368b2af73 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,7 @@ 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 From dce47d34e96c090b1564e3d305e7bb16b8fc5fd1 Mon Sep 17 00:00:00 2001 From: Aschen Date: Thu, 16 Jul 2020 12:07:38 +0200 Subject: [PATCH 10/24] Document ok --- .eslintignore | 2 + .gitignore | 2 + doc/7/controllers/document/validate/index.md | 2 +- src/controllers/Auth.ts | 36 +- src/controllers/Document.ts | 450 ++++++++++- .../searchResult/{Document.js => Document.ts} | 3 +- ...earchResultBase.js => SearchResultBase.ts} | 72 +- src/protocols/routes.json | 716 +++++++++--------- src/utils/interfaces.ts | 2 +- 9 files changed, 864 insertions(+), 421 deletions(-) rename src/core/searchResult/{Document.js => Document.ts} (86%) rename src/core/searchResult/{SearchResultBase.js => SearchResultBase.ts} (69%) diff --git a/.eslintignore b/.eslintignore index acfc19d98..d849ec6cc 100644 --- a/.eslintignore +++ b/.eslintignore @@ -10,3 +10,5 @@ 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 368b2af73..1a1af6994 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ 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/controllers/Auth.ts b/src/controllers/Auth.ts index abe88d9f5..aa343502f 100644 --- a/src/controllers/Auth.ts +++ b/src/controllers/Auth.ts @@ -65,9 +65,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 */ @@ -94,7 +94,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 } = {}) { const request = { @@ -112,8 +112,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 */ @@ -179,7 +179,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 @@ -202,7 +202,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 */ @@ -219,7 +219,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, options: { queuable?: boolean } = {}) { return this.query({ @@ -232,7 +232,7 @@ export class AuthController extends BaseController { /** * Fetches the 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 */ @@ -251,7 +251,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. @@ -268,7 +268,7 @@ export class AuthController extends BaseController { * 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 + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns An array containing user rights objects */ @@ -304,7 +304,7 @@ export class AuthController extends BaseController { * 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 + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns An array of available strategies names */ @@ -370,7 +370,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 @@ -394,7 +394,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 */ @@ -415,7 +415,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, credentials, options: { queuable?: boolean } = {}) { return this.query({ @@ -430,8 +430,8 @@ export class AuthController extends BaseController { * 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 + * - `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.ts b/src/controllers/Document.ts index 4c719d7da..b37eecd12 100644 --- a/src/controllers/Document.ts +++ b/src/controllers/Document.ts @@ -1,5 +1,5 @@ import { BaseController } from './Base'; -import { DocumentsSearchResult } from '../core/searchResult/Document'; +import { SearchResult } from '../core/searchResult/SearchResultBase'; import { JSONObject, Document } from '../utils/interfaces'; export class DocumentController extends BaseController { @@ -13,11 +13,13 @@ export class DocumentController extends BaseController { * 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 + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again * * @returns The number of matching documents */ @@ -41,13 +43,15 @@ export class DocumentController extends BaseController { /** * 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 + * - `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 */ @@ -74,13 +78,15 @@ export class DocumentController extends BaseController { * 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 + * - `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 */ @@ -106,12 +112,14 @@ export class DocumentController extends BaseController { /** * 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 + * - `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 */ @@ -135,12 +143,14 @@ export class DocumentController extends BaseController { /** * 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 + * - `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 */ @@ -164,12 +174,14 @@ export class DocumentController extends BaseController { /** * 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 + * - `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 */ @@ -193,12 +205,14 @@ export class DocumentController extends BaseController { /** * 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 + * - `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 */ @@ -222,12 +236,14 @@ export class DocumentController extends BaseController { /** * 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 + * - `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" */ @@ -279,11 +295,61 @@ export class DocumentController extends BaseController { .then(response => response.result); } - mCreateOrReplace (index, collection, documents, options = {}) { + /** + * 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}, + body: { documents }, action: 'mCreateOrReplace' }; @@ -291,7 +357,44 @@ export class DocumentController extends BaseController { .then(response => response.result); } - mDelete (index, collection, ids, options = {}) { + /** + * 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, @@ -303,23 +406,101 @@ export class DocumentController extends BaseController { .then(response => response.result); } - mGet (index, collection, ids, options = {}) { + /** + * 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} + body: { ids } }; return this.query(request, options) .then(response => response.result); } - mReplace (index, collection, documents, options = {}) { + /** + * 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}, + body: { documents }, action: 'mReplace' }; @@ -327,7 +508,61 @@ export class DocumentController extends BaseController { .then(response => response.result); } - mUpdate (index, collection, documents, options = {}) { + /** + * 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, @@ -339,12 +574,33 @@ export class DocumentController extends BaseController { .then(response => response.result); } - replace (index, collection, _id, body, options = {}) { + /** + * 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, + body: content, action: 'replace' }; @@ -352,15 +608,48 @@ export class DocumentController extends BaseController { .then(response => response.result); } - search (index, collection, body = {}, options = {}) { - return this._search(index, collection, body, options) + /** + * 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) )); } - _search (index, collection, body = {}, options = {}) { - const request = { + private _search ( + index: string, + collection: string, + body: JSONObject = {}, + options: JSONObject = {} + ) { + const request: any = { index, collection, body: null, @@ -384,12 +673,40 @@ export class DocumentController extends BaseController { .then(response => ({ response, request, opts })); } - update (index, collection, _id, body, options = {}) { + /** + * 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?: boolean, + source?: boolean + } = {} + ): Promise { const request = { index, collection, _id, - body, + body: content, action: 'update', retryOnConflict: options.retryOnConflict, source: options.source @@ -399,11 +716,54 @@ export class DocumentController extends BaseController { .then(response => response.result); } - updateByQuery(index, collection, searchQuery, changes, options = {}) { + /** + * 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: searchQuery, changes}, + body: { query, changes }, action: 'updateByQuery', source: options.source }; @@ -412,11 +772,29 @@ export class DocumentController extends BaseController { .then(response => response.result); } - validate (index, collection, body, options = {}) { + /** + * 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, + body: content, action: 'validate' }, options) .then(response => response.result); diff --git a/src/core/searchResult/Document.js b/src/core/searchResult/Document.ts similarity index 86% rename from src/core/searchResult/Document.js rename to src/core/searchResult/Document.ts index f4104eedc..e9ffaa778 100644 --- a/src/core/searchResult/Document.js +++ b/src/core/searchResult/Document.ts @@ -1,7 +1,6 @@ -const { SearchResultBase } = require('./SearchResultBase'); +import { SearchResultBase } from './SearchResultBase'; 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 69% rename from src/core/searchResult/SearchResultBase.js rename to src/core/searchResult/SearchResultBase.ts index b9c8af7f2..09006cbdd 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; +} + +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 { if (this.fetched >= this.total) { return Promise.resolve(null); } @@ -55,7 +115,7 @@ class SearchResultBase { // It resulting in having less fetched documents than the total and thus the SDK // try to fetch the next results page but it's empty if (! hit) { - return Promise.reject(new Error('Unable to retrieve all results from search: the sort combination must identify one item only. Add document "_id" to the sort.')); + return Promise.reject(new Error('Unable to retrieve all results from search: the sort combination must identify one item only. Add document `_id` to the sort.')); } request.body.search_after = []; @@ -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/protocols/routes.json b/src/protocols/routes.json index f07f9d939..3cbc11f42 100644 --- a/src/protocols/routes.json +++ b/src/protocols/routes.json @@ -1,1452 +1,1452 @@ { "auth": { "login": { - "verb": "POST", + `verb`: "POST", "url": "/_login/:strategy" }, "logout": { "url": "/_logout", - "verb": "POST" + `verb`: "POST" }, "getCurrentUser": { "url": "/users/_me", - "verb": "GET" + `verb`: "GET" }, "getMyRights": { "url": "/users/_me/_rights", - "verb": "GET" + `verb`: "GET" }, "checkToken": { "url": "/_checkToken", - "verb": "POST" + `verb`: "POST" }, "updateSelf": { "url": "/_updateSelf", - "verb": "PUT" + `verb`: "PUT" }, "getStrategies": { "url": "/strategies", - "verb": "GET" + `verb`: "GET" }, "createMyCredentials": { "url": "/credentials/:strategy/_me/_create", - "verb": "POST" + `verb`: "POST" }, "updateMyCredentials": { "url": "/credentials/:strategy/_me/_update", - "verb": "PUT" + `verb`: "PUT" }, "credentialsExist": { "url": "/credentials/:strategy/_me/_exists", - "verb": "GET" + `verb`: "GET" }, "validateMyCredentials": { "url": "/credentials/:strategy/_me/_validate", - "verb": "POST" + `verb`: "POST" }, "deleteMyCredentials": { "url": "/credentials/:strategy/_me", - "verb": "DELETE" + `verb`: "DELETE" }, "getMyCredentials": { "url": "/credentials/:strategy/_me", - "verb": "GET" + `verb`: "GET" }, "refreshToken": { "url": "/_refreshToken", - "verb": "POST" + `verb`: "POST" } }, "bulk": { "deleteByQuery": { "url": "/:index/:collection/_bulk/_query", - "verb": "DELETE" + `verb`: "DELETE" }, "import": { - "verb": "POST", + `verb`: "POST", "url": "/:index/:collection/_bulk" }, "write": { - "verb": "POST", + `verb`: "POST", "url": "/:index/:collection/_write" }, "mWrite": { - "verb": "POST", + `verb`: "POST", "url": "/:index/:collection/_mWrite" } }, "document": { "create": { - "verb": "POST", + `verb`: "POST", "url": "/:index/:collection/_create" }, "scroll": { "url": "/_scroll/:scrollId", - "verb": "GET" + `verb`: "GET" }, "exists": { "url": "/:index/:collection/:_id/_exists", - "verb": "GET" + `verb`: "GET" }, "get": { "url": "/:index/:collection/:_id", - "verb": "GET" + `verb`: "GET" }, "mGet": { "url": "/:index/:collection/_mGet", - "verb": "POST" + `verb`: "POST" }, "count": { "url": "/:index/:collection/_count", - "verb": "POST" + `verb`: "POST" }, "mCreate": { "url": "/:index/:collection/_mCreate", - "verb": "POST" + `verb`: "POST" }, "createOrReplace": { "url": "/:index/:collection/:_id", - "verb": "PUT" + `verb`: "PUT" }, "mCreateOrReplace": { "url": "/:index/:collection/_mCreateOrReplace", - "verb": "PUT" + `verb`: "PUT" }, "update": { "url": "/:index/:collection/:_id/_update", - "verb": "PUT" + `verb`: "PUT" }, "updateByQuery": { "url": "/:index/:collection/_query", - "verb": "PUT" + `verb`: "PUT" }, "mUpdate": { "url": "/:index/:collection/_mUpdate", - "verb": "PUT" + `verb`: "PUT" }, "replace": { "url": "/:index/:collection/:_id/_replace", - "verb": "PUT" + `verb`: "PUT" }, "mReplace": { "url": "/:index/:collection/_mReplace", - "verb": "PUT" + `verb`: "PUT" }, "delete": { "url": "/:index/:collection/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "mDelete": { "url": "/:index/:collection/_mDelete", - "verb": "DELETE" + `verb`: "DELETE" }, "deleteByQuery": { "url": "/:index/:collection/_query", - "verb": "DELETE" + `verb`: "DELETE" }, "validate": { "url": "/:index/:collection/_validate", - "verb": "POST" + `verb`: "POST" }, "search": { "url": "/:index/:collection/_search", - "verb": "GET" + `verb`: "GET" } }, "security": { "createFirstAdmin": { - "verb": "POST", + `verb`: "POST", "url": "/_createFirstAdmin" }, "createRestrictedUser": { - "verb": "POST", + `verb`: "POST", "url": "/users/_createRestricted" }, "createUser": { - "verb": "POST", + `verb`: "POST", "url": "/users/_create" }, "getRoleMapping": { "url": "/roles/_mapping", - "verb": "GET" + `verb`: "GET" }, "updateRoleMapping": { "url": "/roles/_mapping", - "verb": "PUT" + `verb`: "PUT" }, "getProfileMapping": { "url": "/profiles/_mapping", - "verb": "GET" + `verb`: "GET" }, "updateProfileMapping": { "url": "/profiles/_mapping", - "verb": "PUT" + `verb`: "PUT" }, "getUserMapping": { "url": "/users/_mapping", - "verb": "GET" + `verb`: "GET" }, "updateUserMapping": { "url": "/users/_mapping", - "verb": "PUT" + `verb`: "PUT" }, "getRole": { "url": "/roles/:_id", - "verb": "GET" + `verb`: "GET" }, "mGetRoles": { "url": "/roles/_mGet", - "verb": "POST" + `verb`: "POST" }, "searchRoles": { "url": "/roles/_search", - "verb": "POST" + `verb`: "POST" }, "createOrReplaceRole": { "url": "/roles/:_id", - "verb": "PUT" + `verb`: "PUT" }, "createRole": { "url": "/roles/:_id/_create", - "verb": "POST" + `verb`: "POST" }, "deleteRole": { "url": "/roles/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "getProfile": { "url": "/profiles/:_id", - "verb": "GET" + `verb`: "GET" }, "mGetProfiles": { "url": "/profiles/_mGet", - "verb": "POST" + `verb`: "POST" }, "createOrReplaceProfile": { "url": "/profiles/:_id", - "verb": "PUT" + `verb`: "PUT" }, "createProfile": { "url": "/profiles/:_id/_create", - "verb": "POST" + `verb`: "POST" }, "deleteProfile": { "url": "/profiles/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "searchProfiles": { "url": "/profiles/_search", - "verb": "POST" + `verb`: "POST" }, "getUser": { "url": "/users/:_id", - "verb": "GET" + `verb`: "GET" }, "getProfileRights": { "url": "/profiles/:_id/_rights", - "verb": "GET" + `verb`: "GET" }, "getUserRights": { "url": "/users/:_id/_rights", - "verb": "GET" + `verb`: "GET" }, "searchUsers": { "url": "/users/_search", - "verb": "POST" + `verb`: "POST" }, "deleteUser": { "url": "/users/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "updateUser": { "url": "/users/:_id/_update", - "verb": "PUT" + `verb`: "PUT" }, "replaceUser": { "url": "/users/:_id/_replace", - "verb": "PUT" + `verb`: "PUT" }, "updateProfile": { "url": "/profiles/:_id/_update", - "verb": "PUT" + `verb`: "PUT" }, "updateRole": { "url": "/roles/:_id/_update", - "verb": "PUT" + `verb`: "PUT" }, "mDeleteProfiles": { "url": "/profiles/_mDelete", - "verb": "POST" + `verb`: "POST" }, "mDeleteRoles": { "url": "/roles/_mDelete", - "verb": "POST" + `verb`: "POST" }, "mDeleteUsers": { "url": "/users/_mDelete", - "verb": "POST" + `verb`: "POST" }, "scrollUsers": { "url": "/users/_scroll/:scrollId", - "verb": "GET" + `verb`: "GET" }, "scrollProfiles": { "url": "/profiles/_scroll/:scrollId", - "verb": "GET" + `verb`: "GET" }, "createCredentials": { "url": "/credentials/:strategy/:_id/_create", - "verb": "POST" + `verb`: "POST" }, "updateCredentials": { "url": "/credentials/:strategy/:_id/_update", - "verb": "PUT" + `verb`: "PUT" }, "hasCredentials": { "url": "/credentials/:strategy/:_id/_exists", - "verb": "GET" + `verb`: "GET" }, "validateCredentials": { "url": "/credentials/:strategy/:_id/_validate", - "verb": "POST" + `verb`: "POST" }, "deleteCredentials": { "url": "/credentials/:strategy/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "getCredentials": { "url": "/credentials/:strategy/:_id", - "verb": "GET" + `verb`: "GET" }, "getCredentialsById": { "url": "/credentials/:strategy/:_id/_byId", - "verb": "GET" + `verb`: "GET" }, "getCredentialFields": { "url": "/credentials/:strategy/_fields", - "verb": "GET" + `verb`: "GET" }, "getAllCredentialFields": { "url": "/credentials/_fields", - "verb": "GET" + `verb`: "GET" } }, "collection": { "update": { "url": "/:index/:collection/", - "verb": "POST" + `verb`: "POST" }, "updateMapping": { "url": "/:index/:collection/_mapping", - "verb": "PUT" + `verb`: "PUT" }, "getMapping": { "url": "/:index/:collection/_mapping", - "verb": "GET" + `verb`: "GET" }, "getSpecifications": { "url": "/:index/:collection/_specifications", - "verb": "GET" + `verb`: "GET" }, "searchSpecifications": { "url": "/validations/_search", - "verb": "POST" + `verb`: "POST" }, "scrollSpecifications": { "url": "/validations/_scroll/:scrollId", - "verb": "GET" + `verb`: "GET" }, "updateSpecifications": { "url": "/:index/:collection/_specifications", - "verb": "PUT" + `verb`: "PUT" }, "deleteSpecifications": { "url": "/:index/:collection/_specifications", - "verb": "DELETE" + `verb`: "DELETE" }, "validateSpecifications": { "url": "/:index/:collection/_validateSpecifications", - "verb": "POST" + `verb`: "POST" }, "truncate": { "url": "/:index/:collection/_truncate", - "verb": "DELETE" + `verb`: "DELETE" }, "list": { "url": "/:index/_list", - "verb": "GET" + `verb`: "GET" }, "exists": { "url": "/:index/:collection/_exists", - "verb": "GET" + `verb`: "GET" }, "create": { "url": "/:index/:collection", - "verb": "PUT" + `verb`: "PUT" } }, "index": { "mDelete": { "url": "/_mdelete", - "verb": "DELETE" + `verb`: "DELETE" }, "create": { "url": "/:index/_create", - "verb": "POST" + `verb`: "POST" }, "delete": { "url": "/:index", - "verb": "DELETE" + `verb`: "DELETE" }, - "refresh": { + `refresh`: { "url": "/:index/_refresh", - "verb": "POST" + `verb`: "POST" }, "list": { "url": "/_list", - "verb": "GET" + `verb`: "GET" }, "refreshInternal": { "url": "/_refreshInternal", - "verb": "POST" + `verb`: "POST" }, "getAutoRefresh": { "url": "/:index/_autoRefresh", - "verb": "GET" + `verb`: "GET" }, "setAutoRefresh": { "url": "/:index/_autoRefresh", - "verb": "POST" + `verb`: "POST" }, "exists": { "url": "/:index/_exists", - "verb": "GET" + `verb`: "GET" } }, "ms": { "append": { "url": "/ms/_append/:_id", - "verb": "POST" + `verb`: "POST" }, "bitcount": { "url": "/ms/_bitcount/:_id", - "verb": "GET" + `verb`: "GET" }, "bitop": { "url": "/ms/_bitop/:_id", - "verb": "POST" + `verb`: "POST" }, "bitpos": { "url": "/ms/_bitpos/:_id", - "verb": "GET" + `verb`: "GET" }, "dbsize": { "url": "/ms/_dbsize", - "verb": "GET" + `verb`: "GET" }, "decrby": { "url": "/ms/_decrby/:_id", - "verb": "POST" + `verb`: "POST" }, "del": { "url": "/ms", - "verb": "DELETE" + `verb`: "DELETE" }, "expire": { "url": "/ms/_expire/:_id", - "verb": "POST" + `verb`: "POST" }, "expireat": { "url": "/ms/_expireat/:_id", - "verb": "POST" + `verb`: "POST" }, "flushdb": { "url": "/ms/_flushdb", - "verb": "POST" + `verb`: "POST" }, "geoadd": { "url": "/ms/_geoadd/:_id", - "verb": "POST" + `verb`: "POST" }, "geodist": { "url": "/ms/_geodist/:_id/:member1/:member2", - "verb": "GET" + `verb`: "GET" }, "geohash": { "url": "/ms/_geohash/:_id", - "verb": "GET" + `verb`: "GET" }, "georadius": { "url": "/ms/_georadius/:_id", - "verb": "GET" + `verb`: "GET" }, "georadiusbymember": { "url": "/ms/_georadiusbymember/:_id", - "verb": "GET" + `verb`: "GET" }, "getbit": { "url": "/ms/_getbit/:_id", - "verb": "GET" + `verb`: "GET" }, "getrange": { "url": "/ms/_getrange/:_id", - "verb": "GET" + `verb`: "GET" }, "hdel": { "url": "/ms/_hdel/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "hmget": { "url": "/ms/_hmget/:_id", - "verb": "GET" + `verb`: "GET" }, "hexists": { "url": "/ms/_hexists/:_id/:field", - "verb": "GET" + `verb`: "GET" }, "hincrby": { "url": "/ms/_hincrby/:_id", - "verb": "POST" + `verb`: "POST" }, "hmset": { "url": "/ms/_hmset/:_id", - "verb": "POST" + `verb`: "POST" }, "hset": { "url": "/ms/_hset/:_id", - "verb": "POST" + `verb`: "POST" }, "hstrlen": { "url": "/ms/_hstrlen/:_id/:field", - "verb": "GET" + `verb`: "GET" }, "keys": { "url": "/ms/_keys/:pattern", - "verb": "GET" + `verb`: "GET" }, "lindex": { "url": "/ms/_lindex/:_id/:idx", - "verb": "GET" + `verb`: "GET" }, "linsert": { "url": "/ms/_linsert/:_id", - "verb": "POST" + `verb`: "POST" }, "lpush": { "url": "/ms/_lpush/:_id", - "verb": "POST" + `verb`: "POST" }, "lrange": { "url": "/ms/_lrange/:_id", - "verb": "GET" + `verb`: "GET" }, "lrem": { "url": "/ms/_lrem/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "lset": { "url": "/ms/_lset/:_id", - "verb": "POST" + `verb`: "POST" }, "ltrim": { "url": "/ms/_ltrim/:_id", - "verb": "POST" + `verb`: "POST" }, "mexecute": { "url": "/ms/_mexecute", - "verb": "POST" + `verb`: "POST" }, "mget": { "url": "/ms/_mget", - "verb": "GET" + `verb`: "GET" }, "mset": { "url": "/ms/_mset", - "verb": "POST" + `verb`: "POST" }, "object": { "url": "/ms/_object/:_id", - "verb": "GET" + `verb`: "GET" }, "pexpire": { "url": "/ms/_pexpire/:_id", - "verb": "POST" + `verb`: "POST" }, "pexpireat": { "url": "/ms/_pexpireat/:_id", - "verb": "POST" + `verb`: "POST" }, "pfadd": { "url": "/ms/_pfadd/:_id", - "verb": "POST" + `verb`: "POST" }, "pfmerge": { "url": "/ms/_pfmerge/:_id", - "verb": "POST" + `verb`: "POST" }, "ping": { "url": "/ms/_ping", - "verb": "GET" + `verb`: "GET" }, "psetex": { "url": "/ms/_psetex/:_id", - "verb": "POST" + `verb`: "POST" }, "randomkey": { "url": "/ms/_randomkey", - "verb": "GET" + `verb`: "GET" }, "rename": { "url": "/ms/_rename/:_id", - "verb": "POST" + `verb`: "POST" }, "renamenx": { "url": "/ms/_renamenx/:_id", - "verb": "POST" + `verb`: "POST" }, "rpoplpush": { "url": "/ms/_rpoplpush", - "verb": "POST" + `verb`: "POST" }, "sadd": { "url": "/ms/_sadd/:_id", - "verb": "POST" + `verb`: "POST" }, "scan": { "url": "/ms/_scan", - "verb": "GET" + `verb`: "GET" }, "sdiff": { "url": "/ms/_sdiff/:_id", - "verb": "GET" + `verb`: "GET" }, "sdiffstore": { "url": "/ms/_sdiffstore/:_id", - "verb": "POST" + `verb`: "POST" }, "set": { "url": "/ms/_set/:_id", - "verb": "POST" + `verb`: "POST" }, "setex": { "url": "/ms/_setex/:_id", - "verb": "POST" + `verb`: "POST" }, "setnx": { "url": "/ms/_setnx/:_id", - "verb": "POST" + `verb`: "POST" }, "sinterstore": { "url": "/ms/_sinterstore", - "verb": "POST" + `verb`: "POST" }, "sismember": { "url": "/ms/_sismember/:_id/:member", - "verb": "GET" + `verb`: "GET" }, "smove": { "url": "/ms/_smove/:_id", - "verb": "POST" + `verb`: "POST" }, "sort": { "url": "/ms/_sort/:_id", - "verb": "POST" + `verb`: "POST" }, "spop": { "url": "/ms/_spop/:_id", - "verb": "POST" + `verb`: "POST" }, "srandmember": { "url": "/ms/_srandmember/:_id", - "verb": "GET" + `verb`: "GET" }, "srem": { "url": "/ms/_srem/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "sscan": { "url": "/ms/_sscan/:_id", - "verb": "GET" + `verb`: "GET" }, "sunion": { "url": "/ms/_sunion", - "verb": "GET" + `verb`: "GET" }, "sunionstore": { "url": "/ms/_sunionstore", - "verb": "POST" + `verb`: "POST" }, "time": { "url": "/ms/_time", - "verb": "GET" + `verb`: "GET" }, "touch": { "url": "/ms/_touch", - "verb": "POST" + `verb`: "POST" }, "zadd": { "url": "/ms/_zadd/:_id", - "verb": "POST" + `verb`: "POST" }, "zcount": { "url": "/ms/_zcount/:_id", - "verb": "GET" + `verb`: "GET" }, "zincrby": { "url": "/ms/_zincrby/:_id", - "verb": "POST" + `verb`: "POST" }, "zinterstore": { "url": "/ms/_zinterstore/:_id", - "verb": "POST" + `verb`: "POST" }, "zlexcount": { "url": "/ms/_zlexcount/:_id", - "verb": "GET" + `verb`: "GET" }, "zrange": { "url": "/ms/_zrange/:_id", - "verb": "GET" + `verb`: "GET" }, "zrangebylex": { "url": "/ms/_zrangebylex/:_id", - "verb": "GET" + `verb`: "GET" }, "zrangebyscore": { "url": "/ms/_zrangebyscore/:_id", - "verb": "GET" + `verb`: "GET" }, "zrem": { "url": "/ms/_zrem/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "zremrangebylex": { "url": "/ms/_zremrangebylex/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "zremrangebyrank": { "url": "/ms/_zremrangebyrank/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "zremrangebyscore": { "url": "/ms/_zremrangebyscore/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "zrevrangebylex": { "url": "/ms/_zrevrangebylex/:_id", - "verb": "GET" + `verb`: "GET" }, "zrevrangebyscore": { "url": "/ms/_zrevrangebyscore/:_id", - "verb": "GET" + `verb`: "GET" }, "zrevrank": { "url": "/ms/_zrevrank/:_id/:member", - "verb": "GET" + `verb`: "GET" }, "zunionstore": { "url": "/ms/_zunionstore/:_id", - "verb": "POST" + `verb`: "POST" }, "zcard": { "url": "/ms/_zcard/:_id", - "verb": "GET" + `verb`: "GET" }, "type": { "url": "/ms/_type/:_id", - "verb": "GET" + `verb`: "GET" }, "ttl": { "url": "/ms/_ttl/:_id", - "verb": "GET" + `verb`: "GET" }, "strlen": { "url": "/ms/_strlen/:_id", - "verb": "GET" + `verb`: "GET" }, "smembers": { "url": "/ms/_smembers/:_id", - "verb": "GET" + `verb`: "GET" }, "scard": { "url": "/ms/_scard/:_id", - "verb": "GET" + `verb`: "GET" }, "rpop": { "url": "/ms/_rpop/:_id", - "verb": "POST" + `verb`: "POST" }, "pttl": { "url": "/ms/_pttl/:_id", - "verb": "GET" + `verb`: "GET" }, "persist": { "url": "/ms/_persist/:_id", - "verb": "POST" + `verb`: "POST" }, "lpop": { "url": "/ms/_lpop/:_id", - "verb": "POST" + `verb`: "POST" }, "llen": { "url": "/ms/_llen/:_id", - "verb": "GET" + `verb`: "GET" }, "incr": { "url": "/ms/_incr/:_id", - "verb": "POST" + `verb`: "POST" }, "hvals": { "url": "/ms/_hvals/:_id", - "verb": "GET" + `verb`: "GET" }, "hlen": { "url": "/ms/_hlen/:_id", - "verb": "GET" + `verb`: "GET" }, "hkeys": { "url": "/ms/_hkeys/:_id", - "verb": "GET" + `verb`: "GET" }, "hgetall": { "url": "/ms/_hgetall/:_id", - "verb": "GET" + `verb`: "GET" }, "get": { "url": "/ms/:_id", - "verb": "GET" + `verb`: "GET" }, "decr": { "url": "/ms/_decr/:_id", - "verb": "POST" + `verb`: "POST" }, "rpushx": { "url": "/ms/_rpushx/:_id", - "verb": "POST" + `verb`: "POST" }, "lpushx": { "url": "/ms/_lpushx/:_id", - "verb": "POST" + `verb`: "POST" }, "getset": { "url": "/ms/_getset/:_id", - "verb": "POST" + `verb`: "POST" }, "sinter": { "url": "/ms/_sinter", - "verb": "GET" + `verb`: "GET" }, "pfcount": { "url": "/ms/_pfcount", - "verb": "GET" + `verb`: "GET" }, "incrbyfloat": { "url": "/ms/_incrbyfloat/:_id", - "verb": "POST" + `verb`: "POST" }, "incrby": { "url": "/ms/_incrby/:_id", - "verb": "POST" + `verb`: "POST" }, "geopos": { "url": "/ms/_geopos/:_id", - "verb": "GET" + `verb`: "GET" }, "hget": { "url": "/ms/_hget/:_id/:field", - "verb": "GET" + `verb`: "GET" }, "hsetnx": { "url": "/ms/_hsetnx/:_id", - "verb": "POST" + `verb`: "POST" }, "msetnx": { "url": "/ms/_msetnx", - "verb": "POST" + `verb`: "POST" }, "rpush": { "url": "/ms/_rpush/:_id", - "verb": "POST" + `verb`: "POST" }, "hincrbyfloat": { "url": "/ms/_hincrbyfloat/:_id", - "verb": "POST" + `verb`: "POST" }, "zrevrange": { "url": "/ms/_zrevrange/:_id", - "verb": "GET" + `verb`: "GET" }, "zrank": { "url": "/ms/_zrank/:_id/:member", - "verb": "GET" + `verb`: "GET" }, "zscore": { "url": "/ms/_zscore/:_id/:member", - "verb": "GET" + `verb`: "GET" }, "zscan": { "url": "/ms/_zscan/:_id", - "verb": "GET" + `verb`: "GET" }, "hscan": { "url": "/ms/_hscan/:_id", - "verb": "GET" + `verb`: "GET" }, "exists": { "url": "/ms/_exists", - "verb": "GET" + `verb`: "GET" } }, "memoryStorage": { "append": { "url": "/ms/_append/:_id", - "verb": "POST" + `verb`: "POST" }, "bitcount": { "url": "/ms/_bitcount/:_id", - "verb": "GET" + `verb`: "GET" }, "bitop": { "url": "/ms/_bitop/:_id", - "verb": "POST" + `verb`: "POST" }, "bitpos": { "url": "/ms/_bitpos/:_id", - "verb": "GET" + `verb`: "GET" }, "dbsize": { "url": "/ms/_dbsize", - "verb": "GET" + `verb`: "GET" }, "decrby": { "url": "/ms/_decrby/:_id", - "verb": "POST" + `verb`: "POST" }, "del": { "url": "/ms", - "verb": "DELETE" + `verb`: "DELETE" }, "expire": { "url": "/ms/_expire/:_id", - "verb": "POST" + `verb`: "POST" }, "expireat": { "url": "/ms/_expireat/:_id", - "verb": "POST" + `verb`: "POST" }, "flushdb": { "url": "/ms/_flushdb", - "verb": "POST" + `verb`: "POST" }, "geoadd": { "url": "/ms/_geoadd/:_id", - "verb": "POST" + `verb`: "POST" }, "geodist": { "url": "/ms/_geodist/:_id/:member1/:member2", - "verb": "GET" + `verb`: "GET" }, "geohash": { "url": "/ms/_geohash/:_id", - "verb": "GET" + `verb`: "GET" }, "georadius": { "url": "/ms/_georadius/:_id", - "verb": "GET" + `verb`: "GET" }, "georadiusbymember": { "url": "/ms/_georadiusbymember/:_id", - "verb": "GET" + `verb`: "GET" }, "getbit": { "url": "/ms/_getbit/:_id", - "verb": "GET" + `verb`: "GET" }, "getrange": { "url": "/ms/_getrange/:_id", - "verb": "GET" + `verb`: "GET" }, "hdel": { "url": "/ms/_hdel/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "hmget": { "url": "/ms/_hmget/:_id", - "verb": "GET" + `verb`: "GET" }, "hexists": { "url": "/ms/_hexists/:_id/:field", - "verb": "GET" + `verb`: "GET" }, "hincrby": { "url": "/ms/_hincrby/:_id", - "verb": "POST" + `verb`: "POST" }, "hmset": { "url": "/ms/_hmset/:_id", - "verb": "POST" + `verb`: "POST" }, "hset": { "url": "/ms/_hset/:_id", - "verb": "POST" + `verb`: "POST" }, "hstrlen": { "url": "/ms/_hstrlen/:_id/:field", - "verb": "GET" + `verb`: "GET" }, "keys": { "url": "/ms/_keys/:pattern", - "verb": "GET" + `verb`: "GET" }, "lindex": { "url": "/ms/_lindex/:_id/:idx", - "verb": "GET" + `verb`: "GET" }, "linsert": { "url": "/ms/_linsert/:_id", - "verb": "POST" + `verb`: "POST" }, "lpush": { "url": "/ms/_lpush/:_id", - "verb": "POST" + `verb`: "POST" }, "lrange": { "url": "/ms/_lrange/:_id", - "verb": "GET" + `verb`: "GET" }, "lrem": { "url": "/ms/_lrem/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "lset": { "url": "/ms/_lset/:_id", - "verb": "POST" + `verb`: "POST" }, "ltrim": { "url": "/ms/_ltrim/:_id", - "verb": "POST" + `verb`: "POST" }, "mget": { "url": "/ms/_mget", - "verb": "GET" + `verb`: "GET" }, "mset": { "url": "/ms/_mset", - "verb": "POST" + `verb`: "POST" }, "object": { "url": "/ms/_object/:_id", - "verb": "GET" + `verb`: "GET" }, "pexpire": { "url": "/ms/_pexpire/:_id", - "verb": "POST" + `verb`: "POST" }, "pexpireat": { "url": "/ms/_pexpireat/:_id", - "verb": "POST" + `verb`: "POST" }, "pfadd": { "url": "/ms/_pfadd/:_id", - "verb": "POST" + `verb`: "POST" }, "pfmerge": { "url": "/ms/_pfmerge/:_id", - "verb": "POST" + `verb`: "POST" }, "ping": { "url": "/ms/_ping", - "verb": "GET" + `verb`: "GET" }, "psetex": { "url": "/ms/_psetex/:_id", - "verb": "POST" + `verb`: "POST" }, "randomkey": { "url": "/ms/_randomkey", - "verb": "GET" + `verb`: "GET" }, "rename": { "url": "/ms/_rename/:_id", - "verb": "POST" + `verb`: "POST" }, "renamenx": { "url": "/ms/_renamenx/:_id", - "verb": "POST" + `verb`: "POST" }, "rpoplpush": { "url": "/ms/_rpoplpush", - "verb": "POST" + `verb`: "POST" }, "sadd": { "url": "/ms/_sadd/:_id", - "verb": "POST" + `verb`: "POST" }, "scan": { "url": "/ms/_scan", - "verb": "GET" + `verb`: "GET" }, "sdiff": { "url": "/ms/_sdiff/:_id", - "verb": "GET" + `verb`: "GET" }, "sdiffstore": { "url": "/ms/_sdiffstore/:_id", - "verb": "POST" + `verb`: "POST" }, "set": { "url": "/ms/_set/:_id", - "verb": "POST" + `verb`: "POST" }, "setex": { "url": "/ms/_setex/:_id", - "verb": "POST" + `verb`: "POST" }, "setnx": { "url": "/ms/_setnx/:_id", - "verb": "POST" + `verb`: "POST" }, "sinterstore": { "url": "/ms/_sinterstore", - "verb": "POST" + `verb`: "POST" }, "sismember": { "url": "/ms/_sismember/:_id/:member", - "verb": "GET" + `verb`: "GET" }, "smove": { "url": "/ms/_smove/:_id", - "verb": "POST" + `verb`: "POST" }, "sort": { "url": "/ms/_sort/:_id", - "verb": "POST" + `verb`: "POST" }, "spop": { "url": "/ms/_spop/:_id", - "verb": "POST" + `verb`: "POST" }, "srandmember": { "url": "/ms/_srandmember/:_id", - "verb": "GET" + `verb`: "GET" }, "srem": { "url": "/ms/_srem/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "sscan": { "url": "/ms/_sscan/:_id", - "verb": "GET" + `verb`: "GET" }, "sunion": { "url": "/ms/_sunion", - "verb": "GET" + `verb`: "GET" }, "sunionstore": { "url": "/ms/_sunionstore", - "verb": "POST" + `verb`: "POST" }, "time": { "url": "/ms/_time", - "verb": "GET" + `verb`: "GET" }, "touch": { "url": "/ms/_touch", - "verb": "POST" + `verb`: "POST" }, "zadd": { "url": "/ms/_zadd/:_id", - "verb": "POST" + `verb`: "POST" }, "zcount": { "url": "/ms/_zcount/:_id", - "verb": "GET" + `verb`: "GET" }, "zincrby": { "url": "/ms/_zincrby/:_id", - "verb": "POST" + `verb`: "POST" }, "zinterstore": { "url": "/ms/_zinterstore/:_id", - "verb": "POST" + `verb`: "POST" }, "zlexcount": { "url": "/ms/_zlexcount/:_id", - "verb": "GET" + `verb`: "GET" }, "zrange": { "url": "/ms/_zrange/:_id", - "verb": "GET" + `verb`: "GET" }, "zrangebylex": { "url": "/ms/_zrangebylex/:_id", - "verb": "GET" + `verb`: "GET" }, "zrangebyscore": { "url": "/ms/_zrangebyscore/:_id", - "verb": "GET" + `verb`: "GET" }, "zrem": { "url": "/ms/_zrem/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "zremrangebylex": { "url": "/ms/_zremrangebylex/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "zremrangebyrank": { "url": "/ms/_zremrangebyrank/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "zremrangebyscore": { "url": "/ms/_zremrangebyscore/:_id", - "verb": "DELETE" + `verb`: "DELETE" }, "zrevrangebylex": { "url": "/ms/_zrevrangebylex/:_id", - "verb": "GET" + `verb`: "GET" }, "zrevrangebyscore": { "url": "/ms/_zrevrangebyscore/:_id", - "verb": "GET" + `verb`: "GET" }, "zrevrank": { "url": "/ms/_zrevrank/:_id/:member", - "verb": "GET" + `verb`: "GET" }, "zunionstore": { "url": "/ms/_zunionstore/:_id", - "verb": "POST" + `verb`: "POST" }, "zcard": { "url": "/ms/_zcard/:_id", - "verb": "GET" + `verb`: "GET" }, "type": { "url": "/ms/_type/:_id", - "verb": "GET" + `verb`: "GET" }, "ttl": { "url": "/ms/_ttl/:_id", - "verb": "GET" + `verb`: "GET" }, "strlen": { "url": "/ms/_strlen/:_id", - "verb": "GET" + `verb`: "GET" }, "smembers": { "url": "/ms/_smembers/:_id", - "verb": "GET" + `verb`: "GET" }, "scard": { "url": "/ms/_scard/:_id", - "verb": "GET" + `verb`: "GET" }, "rpop": { "url": "/ms/_rpop/:_id", - "verb": "POST" + `verb`: "POST" }, "pttl": { "url": "/ms/_pttl/:_id", - "verb": "GET" + `verb`: "GET" }, "persist": { "url": "/ms/_persist/:_id", - "verb": "POST" + `verb`: "POST" }, "lpop": { "url": "/ms/_lpop/:_id", - "verb": "POST" + `verb`: "POST" }, "llen": { "url": "/ms/_llen/:_id", - "verb": "GET" + `verb`: "GET" }, "incr": { "url": "/ms/_incr/:_id", - "verb": "POST" + `verb`: "POST" }, "hvals": { "url": "/ms/_hvals/:_id", - "verb": "GET" + `verb`: "GET" }, "hlen": { "url": "/ms/_hlen/:_id", - "verb": "GET" + `verb`: "GET" }, "hkeys": { "url": "/ms/_hkeys/:_id", - "verb": "GET" + `verb`: "GET" }, "hgetall": { "url": "/ms/_hgetall/:_id", - "verb": "GET" + `verb`: "GET" }, "get": { "url": "/ms/:_id", - "verb": "GET" + `verb`: "GET" }, "decr": { "url": "/ms/_decr/:_id", - "verb": "POST" + `verb`: "POST" }, "rpushx": { "url": "/ms/_rpushx/:_id", - "verb": "POST" + `verb`: "POST" }, "lpushx": { "url": "/ms/_lpushx/:_id", - "verb": "POST" + `verb`: "POST" }, "getset": { "url": "/ms/_getset/:_id", - "verb": "POST" + `verb`: "POST" }, "sinter": { "url": "/ms/_sinter", - "verb": "GET" + `verb`: "GET" }, "pfcount": { "url": "/ms/_pfcount", - "verb": "GET" + `verb`: "GET" }, "incrbyfloat": { "url": "/ms/_incrbyfloat/:_id", - "verb": "POST" + `verb`: "POST" }, "incrby": { "url": "/ms/_incrby/:_id", - "verb": "POST" + `verb`: "POST" }, "geopos": { "url": "/ms/_geopos/:_id", - "verb": "GET" + `verb`: "GET" }, "hget": { "url": "/ms/_hget/:_id/:field", - "verb": "GET" + `verb`: "GET" }, "hsetnx": { "url": "/ms/_hsetnx/:_id", - "verb": "POST" + `verb`: "POST" }, "msetnx": { "url": "/ms/_msetnx", - "verb": "POST" + `verb`: "POST" }, "rpush": { "url": "/ms/_rpush/:_id", - "verb": "POST" + `verb`: "POST" }, "hincrbyfloat": { "url": "/ms/_hincrbyfloat/:_id", - "verb": "POST" + `verb`: "POST" }, "zrevrange": { "url": "/ms/_zrevrange/:_id", - "verb": "GET" + `verb`: "GET" }, "zrank": { "url": "/ms/_zrank/:_id/:member", - "verb": "GET" + `verb`: "GET" }, "zscore": { "url": "/ms/_zscore/:_id/:member", - "verb": "GET" + `verb`: "GET" }, "zscan": { "url": "/ms/_zscan/:_id", - "verb": "GET" + `verb`: "GET" }, "hscan": { "url": "/ms/_hscan/:_id", - "verb": "GET" + `verb`: "GET" }, "exists": { "url": "/ms/_exists", - "verb": "GET" + `verb`: "GET" } }, "realtime": { "list": { "url": "/_listSubscriptions", - "verb": "GET" + `verb`: "GET" }, "publish": { "url": "/:index/:collection/_publish", - "verb": "POST" + `verb`: "POST" } }, "server": { "getStats": { "url": "/_getStats", - "verb": "GET" + `verb`: "GET" }, "getLastStats": { "url": "/_getLastStats", - "verb": "GET" + `verb`: "GET" }, "getAllStats": { "url": "/_getAllStats", - "verb": "GET" + `verb`: "GET" }, "getConfig": { "url": "/_getConfig", - "verb": "GET" + `verb`: "GET" }, "adminExists": { "url": "/_adminExists", - "verb": "GET" + `verb`: "GET" }, "now": { "url": "/_now", - "verb": "GET" + `verb`: "GET" }, "healthCheck": { "url": "/_healthCheck", - "verb": "GET" + `verb`: "GET" }, "info": { "url": "/", - "verb": "GET" + `verb`: "GET" } }, "admin": { "resetKuzzleData": { "url": "/admin/_resetKuzzleData/", - "verb": "POST" + `verb`: "POST" }, "resetCache": { "url": "/admin/_resetCache", - "verb": "POST" + `verb`: "POST" }, "resetSecurity": { "url": "/admin/_resetSecurity/", - "verb": "POST" + `verb`: "POST" }, "resetDatabase": { "url": "/admin/_resetDatabase/", - "verb": "POST" + `verb`: "POST" }, "dump": { "url": "/admin/_dump", - "verb": "POST" + `verb`: "POST" }, "shutdown": { "url": "/admin/_shutdown/", - "verb": "POST" + `verb`: "POST" }, "loadFixtures": { "url": "/admin/_loadFixtures/", - "verb": "POST" + `verb`: "POST" }, "loadMappings": { "url": "/admin/_loadMappings/", - "verb": "POST" + `verb`: "POST" }, "loadSecurities": { "url": "/admin/_loadSecurities/", - "verb": "POST" + `verb`: "POST" } } } diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index d0804bccc..73428df57 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -145,7 +145,7 @@ export interface Document { /** * Document Content */ - _source: { + _source?: { [key: string]: JSONObject | any, /** * Kuzzle metadata From e9ce7286c7bf5aeb5e3325e617288ec4dd82ba14 Mon Sep 17 00:00:00 2001 From: Aschen Date: Thu, 16 Jul 2020 12:11:37 +0200 Subject: [PATCH 11/24] Update Kuzzle --- src/Kuzzle.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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)}`); } From 32946d78b3acb6bc171b50ed57bd081d77e1d892 Mon Sep 17 00:00:00 2001 From: Aschen Date: Thu, 16 Jul 2020 12:20:45 +0200 Subject: [PATCH 12/24] 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 c5331a0fd49972f7ac2a38991373a292a898f9f4 Mon Sep 17 00:00:00 2001 From: Aschen Date: Thu, 16 Jul 2020 12:25:03 +0200 Subject: [PATCH 13/24] Fix tests --- src/controllers/Document.ts | 5 +- src/core/searchResult/Document.ts | 2 +- src/core/searchResult/SearchResultBase.ts | 2 +- src/protocols/routes.json | 716 +++++++++++----------- 4 files changed, 363 insertions(+), 362 deletions(-) diff --git a/src/controllers/Document.ts b/src/controllers/Document.ts index b37eecd12..4dac72eea 100644 --- a/src/controllers/Document.ts +++ b/src/controllers/Document.ts @@ -1,5 +1,6 @@ import { BaseController } from './Base'; import { SearchResult } from '../core/searchResult/SearchResultBase'; +import { DocumentsSearchResult } from '../core/searchResult/Document'; import { JSONObject, Document } from '../utils/interfaces'; export class DocumentController extends BaseController { @@ -221,7 +222,7 @@ export class DocumentController extends BaseController { collection: string, _id: string, options: { queuable?: boolean, refresh?: string } = {} - ): Promise { + ): Promise { const request = { index, collection, @@ -701,7 +702,7 @@ export class DocumentController extends BaseController { retryOnConflict?: boolean, source?: boolean } = {} - ): Promise { + ): Promise { const request = { index, collection, diff --git a/src/core/searchResult/Document.ts b/src/core/searchResult/Document.ts index e9ffaa778..6a87c9d3a 100644 --- a/src/core/searchResult/Document.ts +++ b/src/core/searchResult/Document.ts @@ -1,6 +1,6 @@ import { SearchResultBase } from './SearchResultBase'; -class DocumentsSearchResult extends SearchResultBase { +export class DocumentsSearchResult extends SearchResultBase { /** * @param {Kuzzle} kuzzle * @param {object} query diff --git a/src/core/searchResult/SearchResultBase.ts b/src/core/searchResult/SearchResultBase.ts index 09006cbdd..9530c93d5 100644 --- a/src/core/searchResult/SearchResultBase.ts +++ b/src/core/searchResult/SearchResultBase.ts @@ -1,4 +1,4 @@ -import { JSONObject, KuzzleRequest } from "../../utils/interfaces"; +import { JSONObject, KuzzleRequest } from '../../utils/interfaces'; import { Kuzzle } from '../../Kuzzle'; export interface SearchResult { diff --git a/src/protocols/routes.json b/src/protocols/routes.json index 3cbc11f42..f07f9d939 100644 --- a/src/protocols/routes.json +++ b/src/protocols/routes.json @@ -1,1452 +1,1452 @@ { "auth": { "login": { - `verb`: "POST", + "verb": "POST", "url": "/_login/:strategy" }, "logout": { "url": "/_logout", - `verb`: "POST" + "verb": "POST" }, "getCurrentUser": { "url": "/users/_me", - `verb`: "GET" + "verb": "GET" }, "getMyRights": { "url": "/users/_me/_rights", - `verb`: "GET" + "verb": "GET" }, "checkToken": { "url": "/_checkToken", - `verb`: "POST" + "verb": "POST" }, "updateSelf": { "url": "/_updateSelf", - `verb`: "PUT" + "verb": "PUT" }, "getStrategies": { "url": "/strategies", - `verb`: "GET" + "verb": "GET" }, "createMyCredentials": { "url": "/credentials/:strategy/_me/_create", - `verb`: "POST" + "verb": "POST" }, "updateMyCredentials": { "url": "/credentials/:strategy/_me/_update", - `verb`: "PUT" + "verb": "PUT" }, "credentialsExist": { "url": "/credentials/:strategy/_me/_exists", - `verb`: "GET" + "verb": "GET" }, "validateMyCredentials": { "url": "/credentials/:strategy/_me/_validate", - `verb`: "POST" + "verb": "POST" }, "deleteMyCredentials": { "url": "/credentials/:strategy/_me", - `verb`: "DELETE" + "verb": "DELETE" }, "getMyCredentials": { "url": "/credentials/:strategy/_me", - `verb`: "GET" + "verb": "GET" }, "refreshToken": { "url": "/_refreshToken", - `verb`: "POST" + "verb": "POST" } }, "bulk": { "deleteByQuery": { "url": "/:index/:collection/_bulk/_query", - `verb`: "DELETE" + "verb": "DELETE" }, "import": { - `verb`: "POST", + "verb": "POST", "url": "/:index/:collection/_bulk" }, "write": { - `verb`: "POST", + "verb": "POST", "url": "/:index/:collection/_write" }, "mWrite": { - `verb`: "POST", + "verb": "POST", "url": "/:index/:collection/_mWrite" } }, "document": { "create": { - `verb`: "POST", + "verb": "POST", "url": "/:index/:collection/_create" }, "scroll": { "url": "/_scroll/:scrollId", - `verb`: "GET" + "verb": "GET" }, "exists": { "url": "/:index/:collection/:_id/_exists", - `verb`: "GET" + "verb": "GET" }, "get": { "url": "/:index/:collection/:_id", - `verb`: "GET" + "verb": "GET" }, "mGet": { "url": "/:index/:collection/_mGet", - `verb`: "POST" + "verb": "POST" }, "count": { "url": "/:index/:collection/_count", - `verb`: "POST" + "verb": "POST" }, "mCreate": { "url": "/:index/:collection/_mCreate", - `verb`: "POST" + "verb": "POST" }, "createOrReplace": { "url": "/:index/:collection/:_id", - `verb`: "PUT" + "verb": "PUT" }, "mCreateOrReplace": { "url": "/:index/:collection/_mCreateOrReplace", - `verb`: "PUT" + "verb": "PUT" }, "update": { "url": "/:index/:collection/:_id/_update", - `verb`: "PUT" + "verb": "PUT" }, "updateByQuery": { "url": "/:index/:collection/_query", - `verb`: "PUT" + "verb": "PUT" }, "mUpdate": { "url": "/:index/:collection/_mUpdate", - `verb`: "PUT" + "verb": "PUT" }, "replace": { "url": "/:index/:collection/:_id/_replace", - `verb`: "PUT" + "verb": "PUT" }, "mReplace": { "url": "/:index/:collection/_mReplace", - `verb`: "PUT" + "verb": "PUT" }, "delete": { "url": "/:index/:collection/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "mDelete": { "url": "/:index/:collection/_mDelete", - `verb`: "DELETE" + "verb": "DELETE" }, "deleteByQuery": { "url": "/:index/:collection/_query", - `verb`: "DELETE" + "verb": "DELETE" }, "validate": { "url": "/:index/:collection/_validate", - `verb`: "POST" + "verb": "POST" }, "search": { "url": "/:index/:collection/_search", - `verb`: "GET" + "verb": "GET" } }, "security": { "createFirstAdmin": { - `verb`: "POST", + "verb": "POST", "url": "/_createFirstAdmin" }, "createRestrictedUser": { - `verb`: "POST", + "verb": "POST", "url": "/users/_createRestricted" }, "createUser": { - `verb`: "POST", + "verb": "POST", "url": "/users/_create" }, "getRoleMapping": { "url": "/roles/_mapping", - `verb`: "GET" + "verb": "GET" }, "updateRoleMapping": { "url": "/roles/_mapping", - `verb`: "PUT" + "verb": "PUT" }, "getProfileMapping": { "url": "/profiles/_mapping", - `verb`: "GET" + "verb": "GET" }, "updateProfileMapping": { "url": "/profiles/_mapping", - `verb`: "PUT" + "verb": "PUT" }, "getUserMapping": { "url": "/users/_mapping", - `verb`: "GET" + "verb": "GET" }, "updateUserMapping": { "url": "/users/_mapping", - `verb`: "PUT" + "verb": "PUT" }, "getRole": { "url": "/roles/:_id", - `verb`: "GET" + "verb": "GET" }, "mGetRoles": { "url": "/roles/_mGet", - `verb`: "POST" + "verb": "POST" }, "searchRoles": { "url": "/roles/_search", - `verb`: "POST" + "verb": "POST" }, "createOrReplaceRole": { "url": "/roles/:_id", - `verb`: "PUT" + "verb": "PUT" }, "createRole": { "url": "/roles/:_id/_create", - `verb`: "POST" + "verb": "POST" }, "deleteRole": { "url": "/roles/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "getProfile": { "url": "/profiles/:_id", - `verb`: "GET" + "verb": "GET" }, "mGetProfiles": { "url": "/profiles/_mGet", - `verb`: "POST" + "verb": "POST" }, "createOrReplaceProfile": { "url": "/profiles/:_id", - `verb`: "PUT" + "verb": "PUT" }, "createProfile": { "url": "/profiles/:_id/_create", - `verb`: "POST" + "verb": "POST" }, "deleteProfile": { "url": "/profiles/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "searchProfiles": { "url": "/profiles/_search", - `verb`: "POST" + "verb": "POST" }, "getUser": { "url": "/users/:_id", - `verb`: "GET" + "verb": "GET" }, "getProfileRights": { "url": "/profiles/:_id/_rights", - `verb`: "GET" + "verb": "GET" }, "getUserRights": { "url": "/users/:_id/_rights", - `verb`: "GET" + "verb": "GET" }, "searchUsers": { "url": "/users/_search", - `verb`: "POST" + "verb": "POST" }, "deleteUser": { "url": "/users/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "updateUser": { "url": "/users/:_id/_update", - `verb`: "PUT" + "verb": "PUT" }, "replaceUser": { "url": "/users/:_id/_replace", - `verb`: "PUT" + "verb": "PUT" }, "updateProfile": { "url": "/profiles/:_id/_update", - `verb`: "PUT" + "verb": "PUT" }, "updateRole": { "url": "/roles/:_id/_update", - `verb`: "PUT" + "verb": "PUT" }, "mDeleteProfiles": { "url": "/profiles/_mDelete", - `verb`: "POST" + "verb": "POST" }, "mDeleteRoles": { "url": "/roles/_mDelete", - `verb`: "POST" + "verb": "POST" }, "mDeleteUsers": { "url": "/users/_mDelete", - `verb`: "POST" + "verb": "POST" }, "scrollUsers": { "url": "/users/_scroll/:scrollId", - `verb`: "GET" + "verb": "GET" }, "scrollProfiles": { "url": "/profiles/_scroll/:scrollId", - `verb`: "GET" + "verb": "GET" }, "createCredentials": { "url": "/credentials/:strategy/:_id/_create", - `verb`: "POST" + "verb": "POST" }, "updateCredentials": { "url": "/credentials/:strategy/:_id/_update", - `verb`: "PUT" + "verb": "PUT" }, "hasCredentials": { "url": "/credentials/:strategy/:_id/_exists", - `verb`: "GET" + "verb": "GET" }, "validateCredentials": { "url": "/credentials/:strategy/:_id/_validate", - `verb`: "POST" + "verb": "POST" }, "deleteCredentials": { "url": "/credentials/:strategy/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "getCredentials": { "url": "/credentials/:strategy/:_id", - `verb`: "GET" + "verb": "GET" }, "getCredentialsById": { "url": "/credentials/:strategy/:_id/_byId", - `verb`: "GET" + "verb": "GET" }, "getCredentialFields": { "url": "/credentials/:strategy/_fields", - `verb`: "GET" + "verb": "GET" }, "getAllCredentialFields": { "url": "/credentials/_fields", - `verb`: "GET" + "verb": "GET" } }, "collection": { "update": { "url": "/:index/:collection/", - `verb`: "POST" + "verb": "POST" }, "updateMapping": { "url": "/:index/:collection/_mapping", - `verb`: "PUT" + "verb": "PUT" }, "getMapping": { "url": "/:index/:collection/_mapping", - `verb`: "GET" + "verb": "GET" }, "getSpecifications": { "url": "/:index/:collection/_specifications", - `verb`: "GET" + "verb": "GET" }, "searchSpecifications": { "url": "/validations/_search", - `verb`: "POST" + "verb": "POST" }, "scrollSpecifications": { "url": "/validations/_scroll/:scrollId", - `verb`: "GET" + "verb": "GET" }, "updateSpecifications": { "url": "/:index/:collection/_specifications", - `verb`: "PUT" + "verb": "PUT" }, "deleteSpecifications": { "url": "/:index/:collection/_specifications", - `verb`: "DELETE" + "verb": "DELETE" }, "validateSpecifications": { "url": "/:index/:collection/_validateSpecifications", - `verb`: "POST" + "verb": "POST" }, "truncate": { "url": "/:index/:collection/_truncate", - `verb`: "DELETE" + "verb": "DELETE" }, "list": { "url": "/:index/_list", - `verb`: "GET" + "verb": "GET" }, "exists": { "url": "/:index/:collection/_exists", - `verb`: "GET" + "verb": "GET" }, "create": { "url": "/:index/:collection", - `verb`: "PUT" + "verb": "PUT" } }, "index": { "mDelete": { "url": "/_mdelete", - `verb`: "DELETE" + "verb": "DELETE" }, "create": { "url": "/:index/_create", - `verb`: "POST" + "verb": "POST" }, "delete": { "url": "/:index", - `verb`: "DELETE" + "verb": "DELETE" }, - `refresh`: { + "refresh": { "url": "/:index/_refresh", - `verb`: "POST" + "verb": "POST" }, "list": { "url": "/_list", - `verb`: "GET" + "verb": "GET" }, "refreshInternal": { "url": "/_refreshInternal", - `verb`: "POST" + "verb": "POST" }, "getAutoRefresh": { "url": "/:index/_autoRefresh", - `verb`: "GET" + "verb": "GET" }, "setAutoRefresh": { "url": "/:index/_autoRefresh", - `verb`: "POST" + "verb": "POST" }, "exists": { "url": "/:index/_exists", - `verb`: "GET" + "verb": "GET" } }, "ms": { "append": { "url": "/ms/_append/:_id", - `verb`: "POST" + "verb": "POST" }, "bitcount": { "url": "/ms/_bitcount/:_id", - `verb`: "GET" + "verb": "GET" }, "bitop": { "url": "/ms/_bitop/:_id", - `verb`: "POST" + "verb": "POST" }, "bitpos": { "url": "/ms/_bitpos/:_id", - `verb`: "GET" + "verb": "GET" }, "dbsize": { "url": "/ms/_dbsize", - `verb`: "GET" + "verb": "GET" }, "decrby": { "url": "/ms/_decrby/:_id", - `verb`: "POST" + "verb": "POST" }, "del": { "url": "/ms", - `verb`: "DELETE" + "verb": "DELETE" }, "expire": { "url": "/ms/_expire/:_id", - `verb`: "POST" + "verb": "POST" }, "expireat": { "url": "/ms/_expireat/:_id", - `verb`: "POST" + "verb": "POST" }, "flushdb": { "url": "/ms/_flushdb", - `verb`: "POST" + "verb": "POST" }, "geoadd": { "url": "/ms/_geoadd/:_id", - `verb`: "POST" + "verb": "POST" }, "geodist": { "url": "/ms/_geodist/:_id/:member1/:member2", - `verb`: "GET" + "verb": "GET" }, "geohash": { "url": "/ms/_geohash/:_id", - `verb`: "GET" + "verb": "GET" }, "georadius": { "url": "/ms/_georadius/:_id", - `verb`: "GET" + "verb": "GET" }, "georadiusbymember": { "url": "/ms/_georadiusbymember/:_id", - `verb`: "GET" + "verb": "GET" }, "getbit": { "url": "/ms/_getbit/:_id", - `verb`: "GET" + "verb": "GET" }, "getrange": { "url": "/ms/_getrange/:_id", - `verb`: "GET" + "verb": "GET" }, "hdel": { "url": "/ms/_hdel/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "hmget": { "url": "/ms/_hmget/:_id", - `verb`: "GET" + "verb": "GET" }, "hexists": { "url": "/ms/_hexists/:_id/:field", - `verb`: "GET" + "verb": "GET" }, "hincrby": { "url": "/ms/_hincrby/:_id", - `verb`: "POST" + "verb": "POST" }, "hmset": { "url": "/ms/_hmset/:_id", - `verb`: "POST" + "verb": "POST" }, "hset": { "url": "/ms/_hset/:_id", - `verb`: "POST" + "verb": "POST" }, "hstrlen": { "url": "/ms/_hstrlen/:_id/:field", - `verb`: "GET" + "verb": "GET" }, "keys": { "url": "/ms/_keys/:pattern", - `verb`: "GET" + "verb": "GET" }, "lindex": { "url": "/ms/_lindex/:_id/:idx", - `verb`: "GET" + "verb": "GET" }, "linsert": { "url": "/ms/_linsert/:_id", - `verb`: "POST" + "verb": "POST" }, "lpush": { "url": "/ms/_lpush/:_id", - `verb`: "POST" + "verb": "POST" }, "lrange": { "url": "/ms/_lrange/:_id", - `verb`: "GET" + "verb": "GET" }, "lrem": { "url": "/ms/_lrem/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "lset": { "url": "/ms/_lset/:_id", - `verb`: "POST" + "verb": "POST" }, "ltrim": { "url": "/ms/_ltrim/:_id", - `verb`: "POST" + "verb": "POST" }, "mexecute": { "url": "/ms/_mexecute", - `verb`: "POST" + "verb": "POST" }, "mget": { "url": "/ms/_mget", - `verb`: "GET" + "verb": "GET" }, "mset": { "url": "/ms/_mset", - `verb`: "POST" + "verb": "POST" }, "object": { "url": "/ms/_object/:_id", - `verb`: "GET" + "verb": "GET" }, "pexpire": { "url": "/ms/_pexpire/:_id", - `verb`: "POST" + "verb": "POST" }, "pexpireat": { "url": "/ms/_pexpireat/:_id", - `verb`: "POST" + "verb": "POST" }, "pfadd": { "url": "/ms/_pfadd/:_id", - `verb`: "POST" + "verb": "POST" }, "pfmerge": { "url": "/ms/_pfmerge/:_id", - `verb`: "POST" + "verb": "POST" }, "ping": { "url": "/ms/_ping", - `verb`: "GET" + "verb": "GET" }, "psetex": { "url": "/ms/_psetex/:_id", - `verb`: "POST" + "verb": "POST" }, "randomkey": { "url": "/ms/_randomkey", - `verb`: "GET" + "verb": "GET" }, "rename": { "url": "/ms/_rename/:_id", - `verb`: "POST" + "verb": "POST" }, "renamenx": { "url": "/ms/_renamenx/:_id", - `verb`: "POST" + "verb": "POST" }, "rpoplpush": { "url": "/ms/_rpoplpush", - `verb`: "POST" + "verb": "POST" }, "sadd": { "url": "/ms/_sadd/:_id", - `verb`: "POST" + "verb": "POST" }, "scan": { "url": "/ms/_scan", - `verb`: "GET" + "verb": "GET" }, "sdiff": { "url": "/ms/_sdiff/:_id", - `verb`: "GET" + "verb": "GET" }, "sdiffstore": { "url": "/ms/_sdiffstore/:_id", - `verb`: "POST" + "verb": "POST" }, "set": { "url": "/ms/_set/:_id", - `verb`: "POST" + "verb": "POST" }, "setex": { "url": "/ms/_setex/:_id", - `verb`: "POST" + "verb": "POST" }, "setnx": { "url": "/ms/_setnx/:_id", - `verb`: "POST" + "verb": "POST" }, "sinterstore": { "url": "/ms/_sinterstore", - `verb`: "POST" + "verb": "POST" }, "sismember": { "url": "/ms/_sismember/:_id/:member", - `verb`: "GET" + "verb": "GET" }, "smove": { "url": "/ms/_smove/:_id", - `verb`: "POST" + "verb": "POST" }, "sort": { "url": "/ms/_sort/:_id", - `verb`: "POST" + "verb": "POST" }, "spop": { "url": "/ms/_spop/:_id", - `verb`: "POST" + "verb": "POST" }, "srandmember": { "url": "/ms/_srandmember/:_id", - `verb`: "GET" + "verb": "GET" }, "srem": { "url": "/ms/_srem/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "sscan": { "url": "/ms/_sscan/:_id", - `verb`: "GET" + "verb": "GET" }, "sunion": { "url": "/ms/_sunion", - `verb`: "GET" + "verb": "GET" }, "sunionstore": { "url": "/ms/_sunionstore", - `verb`: "POST" + "verb": "POST" }, "time": { "url": "/ms/_time", - `verb`: "GET" + "verb": "GET" }, "touch": { "url": "/ms/_touch", - `verb`: "POST" + "verb": "POST" }, "zadd": { "url": "/ms/_zadd/:_id", - `verb`: "POST" + "verb": "POST" }, "zcount": { "url": "/ms/_zcount/:_id", - `verb`: "GET" + "verb": "GET" }, "zincrby": { "url": "/ms/_zincrby/:_id", - `verb`: "POST" + "verb": "POST" }, "zinterstore": { "url": "/ms/_zinterstore/:_id", - `verb`: "POST" + "verb": "POST" }, "zlexcount": { "url": "/ms/_zlexcount/:_id", - `verb`: "GET" + "verb": "GET" }, "zrange": { "url": "/ms/_zrange/:_id", - `verb`: "GET" + "verb": "GET" }, "zrangebylex": { "url": "/ms/_zrangebylex/:_id", - `verb`: "GET" + "verb": "GET" }, "zrangebyscore": { "url": "/ms/_zrangebyscore/:_id", - `verb`: "GET" + "verb": "GET" }, "zrem": { "url": "/ms/_zrem/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "zremrangebylex": { "url": "/ms/_zremrangebylex/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "zremrangebyrank": { "url": "/ms/_zremrangebyrank/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "zremrangebyscore": { "url": "/ms/_zremrangebyscore/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "zrevrangebylex": { "url": "/ms/_zrevrangebylex/:_id", - `verb`: "GET" + "verb": "GET" }, "zrevrangebyscore": { "url": "/ms/_zrevrangebyscore/:_id", - `verb`: "GET" + "verb": "GET" }, "zrevrank": { "url": "/ms/_zrevrank/:_id/:member", - `verb`: "GET" + "verb": "GET" }, "zunionstore": { "url": "/ms/_zunionstore/:_id", - `verb`: "POST" + "verb": "POST" }, "zcard": { "url": "/ms/_zcard/:_id", - `verb`: "GET" + "verb": "GET" }, "type": { "url": "/ms/_type/:_id", - `verb`: "GET" + "verb": "GET" }, "ttl": { "url": "/ms/_ttl/:_id", - `verb`: "GET" + "verb": "GET" }, "strlen": { "url": "/ms/_strlen/:_id", - `verb`: "GET" + "verb": "GET" }, "smembers": { "url": "/ms/_smembers/:_id", - `verb`: "GET" + "verb": "GET" }, "scard": { "url": "/ms/_scard/:_id", - `verb`: "GET" + "verb": "GET" }, "rpop": { "url": "/ms/_rpop/:_id", - `verb`: "POST" + "verb": "POST" }, "pttl": { "url": "/ms/_pttl/:_id", - `verb`: "GET" + "verb": "GET" }, "persist": { "url": "/ms/_persist/:_id", - `verb`: "POST" + "verb": "POST" }, "lpop": { "url": "/ms/_lpop/:_id", - `verb`: "POST" + "verb": "POST" }, "llen": { "url": "/ms/_llen/:_id", - `verb`: "GET" + "verb": "GET" }, "incr": { "url": "/ms/_incr/:_id", - `verb`: "POST" + "verb": "POST" }, "hvals": { "url": "/ms/_hvals/:_id", - `verb`: "GET" + "verb": "GET" }, "hlen": { "url": "/ms/_hlen/:_id", - `verb`: "GET" + "verb": "GET" }, "hkeys": { "url": "/ms/_hkeys/:_id", - `verb`: "GET" + "verb": "GET" }, "hgetall": { "url": "/ms/_hgetall/:_id", - `verb`: "GET" + "verb": "GET" }, "get": { "url": "/ms/:_id", - `verb`: "GET" + "verb": "GET" }, "decr": { "url": "/ms/_decr/:_id", - `verb`: "POST" + "verb": "POST" }, "rpushx": { "url": "/ms/_rpushx/:_id", - `verb`: "POST" + "verb": "POST" }, "lpushx": { "url": "/ms/_lpushx/:_id", - `verb`: "POST" + "verb": "POST" }, "getset": { "url": "/ms/_getset/:_id", - `verb`: "POST" + "verb": "POST" }, "sinter": { "url": "/ms/_sinter", - `verb`: "GET" + "verb": "GET" }, "pfcount": { "url": "/ms/_pfcount", - `verb`: "GET" + "verb": "GET" }, "incrbyfloat": { "url": "/ms/_incrbyfloat/:_id", - `verb`: "POST" + "verb": "POST" }, "incrby": { "url": "/ms/_incrby/:_id", - `verb`: "POST" + "verb": "POST" }, "geopos": { "url": "/ms/_geopos/:_id", - `verb`: "GET" + "verb": "GET" }, "hget": { "url": "/ms/_hget/:_id/:field", - `verb`: "GET" + "verb": "GET" }, "hsetnx": { "url": "/ms/_hsetnx/:_id", - `verb`: "POST" + "verb": "POST" }, "msetnx": { "url": "/ms/_msetnx", - `verb`: "POST" + "verb": "POST" }, "rpush": { "url": "/ms/_rpush/:_id", - `verb`: "POST" + "verb": "POST" }, "hincrbyfloat": { "url": "/ms/_hincrbyfloat/:_id", - `verb`: "POST" + "verb": "POST" }, "zrevrange": { "url": "/ms/_zrevrange/:_id", - `verb`: "GET" + "verb": "GET" }, "zrank": { "url": "/ms/_zrank/:_id/:member", - `verb`: "GET" + "verb": "GET" }, "zscore": { "url": "/ms/_zscore/:_id/:member", - `verb`: "GET" + "verb": "GET" }, "zscan": { "url": "/ms/_zscan/:_id", - `verb`: "GET" + "verb": "GET" }, "hscan": { "url": "/ms/_hscan/:_id", - `verb`: "GET" + "verb": "GET" }, "exists": { "url": "/ms/_exists", - `verb`: "GET" + "verb": "GET" } }, "memoryStorage": { "append": { "url": "/ms/_append/:_id", - `verb`: "POST" + "verb": "POST" }, "bitcount": { "url": "/ms/_bitcount/:_id", - `verb`: "GET" + "verb": "GET" }, "bitop": { "url": "/ms/_bitop/:_id", - `verb`: "POST" + "verb": "POST" }, "bitpos": { "url": "/ms/_bitpos/:_id", - `verb`: "GET" + "verb": "GET" }, "dbsize": { "url": "/ms/_dbsize", - `verb`: "GET" + "verb": "GET" }, "decrby": { "url": "/ms/_decrby/:_id", - `verb`: "POST" + "verb": "POST" }, "del": { "url": "/ms", - `verb`: "DELETE" + "verb": "DELETE" }, "expire": { "url": "/ms/_expire/:_id", - `verb`: "POST" + "verb": "POST" }, "expireat": { "url": "/ms/_expireat/:_id", - `verb`: "POST" + "verb": "POST" }, "flushdb": { "url": "/ms/_flushdb", - `verb`: "POST" + "verb": "POST" }, "geoadd": { "url": "/ms/_geoadd/:_id", - `verb`: "POST" + "verb": "POST" }, "geodist": { "url": "/ms/_geodist/:_id/:member1/:member2", - `verb`: "GET" + "verb": "GET" }, "geohash": { "url": "/ms/_geohash/:_id", - `verb`: "GET" + "verb": "GET" }, "georadius": { "url": "/ms/_georadius/:_id", - `verb`: "GET" + "verb": "GET" }, "georadiusbymember": { "url": "/ms/_georadiusbymember/:_id", - `verb`: "GET" + "verb": "GET" }, "getbit": { "url": "/ms/_getbit/:_id", - `verb`: "GET" + "verb": "GET" }, "getrange": { "url": "/ms/_getrange/:_id", - `verb`: "GET" + "verb": "GET" }, "hdel": { "url": "/ms/_hdel/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "hmget": { "url": "/ms/_hmget/:_id", - `verb`: "GET" + "verb": "GET" }, "hexists": { "url": "/ms/_hexists/:_id/:field", - `verb`: "GET" + "verb": "GET" }, "hincrby": { "url": "/ms/_hincrby/:_id", - `verb`: "POST" + "verb": "POST" }, "hmset": { "url": "/ms/_hmset/:_id", - `verb`: "POST" + "verb": "POST" }, "hset": { "url": "/ms/_hset/:_id", - `verb`: "POST" + "verb": "POST" }, "hstrlen": { "url": "/ms/_hstrlen/:_id/:field", - `verb`: "GET" + "verb": "GET" }, "keys": { "url": "/ms/_keys/:pattern", - `verb`: "GET" + "verb": "GET" }, "lindex": { "url": "/ms/_lindex/:_id/:idx", - `verb`: "GET" + "verb": "GET" }, "linsert": { "url": "/ms/_linsert/:_id", - `verb`: "POST" + "verb": "POST" }, "lpush": { "url": "/ms/_lpush/:_id", - `verb`: "POST" + "verb": "POST" }, "lrange": { "url": "/ms/_lrange/:_id", - `verb`: "GET" + "verb": "GET" }, "lrem": { "url": "/ms/_lrem/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "lset": { "url": "/ms/_lset/:_id", - `verb`: "POST" + "verb": "POST" }, "ltrim": { "url": "/ms/_ltrim/:_id", - `verb`: "POST" + "verb": "POST" }, "mget": { "url": "/ms/_mget", - `verb`: "GET" + "verb": "GET" }, "mset": { "url": "/ms/_mset", - `verb`: "POST" + "verb": "POST" }, "object": { "url": "/ms/_object/:_id", - `verb`: "GET" + "verb": "GET" }, "pexpire": { "url": "/ms/_pexpire/:_id", - `verb`: "POST" + "verb": "POST" }, "pexpireat": { "url": "/ms/_pexpireat/:_id", - `verb`: "POST" + "verb": "POST" }, "pfadd": { "url": "/ms/_pfadd/:_id", - `verb`: "POST" + "verb": "POST" }, "pfmerge": { "url": "/ms/_pfmerge/:_id", - `verb`: "POST" + "verb": "POST" }, "ping": { "url": "/ms/_ping", - `verb`: "GET" + "verb": "GET" }, "psetex": { "url": "/ms/_psetex/:_id", - `verb`: "POST" + "verb": "POST" }, "randomkey": { "url": "/ms/_randomkey", - `verb`: "GET" + "verb": "GET" }, "rename": { "url": "/ms/_rename/:_id", - `verb`: "POST" + "verb": "POST" }, "renamenx": { "url": "/ms/_renamenx/:_id", - `verb`: "POST" + "verb": "POST" }, "rpoplpush": { "url": "/ms/_rpoplpush", - `verb`: "POST" + "verb": "POST" }, "sadd": { "url": "/ms/_sadd/:_id", - `verb`: "POST" + "verb": "POST" }, "scan": { "url": "/ms/_scan", - `verb`: "GET" + "verb": "GET" }, "sdiff": { "url": "/ms/_sdiff/:_id", - `verb`: "GET" + "verb": "GET" }, "sdiffstore": { "url": "/ms/_sdiffstore/:_id", - `verb`: "POST" + "verb": "POST" }, "set": { "url": "/ms/_set/:_id", - `verb`: "POST" + "verb": "POST" }, "setex": { "url": "/ms/_setex/:_id", - `verb`: "POST" + "verb": "POST" }, "setnx": { "url": "/ms/_setnx/:_id", - `verb`: "POST" + "verb": "POST" }, "sinterstore": { "url": "/ms/_sinterstore", - `verb`: "POST" + "verb": "POST" }, "sismember": { "url": "/ms/_sismember/:_id/:member", - `verb`: "GET" + "verb": "GET" }, "smove": { "url": "/ms/_smove/:_id", - `verb`: "POST" + "verb": "POST" }, "sort": { "url": "/ms/_sort/:_id", - `verb`: "POST" + "verb": "POST" }, "spop": { "url": "/ms/_spop/:_id", - `verb`: "POST" + "verb": "POST" }, "srandmember": { "url": "/ms/_srandmember/:_id", - `verb`: "GET" + "verb": "GET" }, "srem": { "url": "/ms/_srem/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "sscan": { "url": "/ms/_sscan/:_id", - `verb`: "GET" + "verb": "GET" }, "sunion": { "url": "/ms/_sunion", - `verb`: "GET" + "verb": "GET" }, "sunionstore": { "url": "/ms/_sunionstore", - `verb`: "POST" + "verb": "POST" }, "time": { "url": "/ms/_time", - `verb`: "GET" + "verb": "GET" }, "touch": { "url": "/ms/_touch", - `verb`: "POST" + "verb": "POST" }, "zadd": { "url": "/ms/_zadd/:_id", - `verb`: "POST" + "verb": "POST" }, "zcount": { "url": "/ms/_zcount/:_id", - `verb`: "GET" + "verb": "GET" }, "zincrby": { "url": "/ms/_zincrby/:_id", - `verb`: "POST" + "verb": "POST" }, "zinterstore": { "url": "/ms/_zinterstore/:_id", - `verb`: "POST" + "verb": "POST" }, "zlexcount": { "url": "/ms/_zlexcount/:_id", - `verb`: "GET" + "verb": "GET" }, "zrange": { "url": "/ms/_zrange/:_id", - `verb`: "GET" + "verb": "GET" }, "zrangebylex": { "url": "/ms/_zrangebylex/:_id", - `verb`: "GET" + "verb": "GET" }, "zrangebyscore": { "url": "/ms/_zrangebyscore/:_id", - `verb`: "GET" + "verb": "GET" }, "zrem": { "url": "/ms/_zrem/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "zremrangebylex": { "url": "/ms/_zremrangebylex/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "zremrangebyrank": { "url": "/ms/_zremrangebyrank/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "zremrangebyscore": { "url": "/ms/_zremrangebyscore/:_id", - `verb`: "DELETE" + "verb": "DELETE" }, "zrevrangebylex": { "url": "/ms/_zrevrangebylex/:_id", - `verb`: "GET" + "verb": "GET" }, "zrevrangebyscore": { "url": "/ms/_zrevrangebyscore/:_id", - `verb`: "GET" + "verb": "GET" }, "zrevrank": { "url": "/ms/_zrevrank/:_id/:member", - `verb`: "GET" + "verb": "GET" }, "zunionstore": { "url": "/ms/_zunionstore/:_id", - `verb`: "POST" + "verb": "POST" }, "zcard": { "url": "/ms/_zcard/:_id", - `verb`: "GET" + "verb": "GET" }, "type": { "url": "/ms/_type/:_id", - `verb`: "GET" + "verb": "GET" }, "ttl": { "url": "/ms/_ttl/:_id", - `verb`: "GET" + "verb": "GET" }, "strlen": { "url": "/ms/_strlen/:_id", - `verb`: "GET" + "verb": "GET" }, "smembers": { "url": "/ms/_smembers/:_id", - `verb`: "GET" + "verb": "GET" }, "scard": { "url": "/ms/_scard/:_id", - `verb`: "GET" + "verb": "GET" }, "rpop": { "url": "/ms/_rpop/:_id", - `verb`: "POST" + "verb": "POST" }, "pttl": { "url": "/ms/_pttl/:_id", - `verb`: "GET" + "verb": "GET" }, "persist": { "url": "/ms/_persist/:_id", - `verb`: "POST" + "verb": "POST" }, "lpop": { "url": "/ms/_lpop/:_id", - `verb`: "POST" + "verb": "POST" }, "llen": { "url": "/ms/_llen/:_id", - `verb`: "GET" + "verb": "GET" }, "incr": { "url": "/ms/_incr/:_id", - `verb`: "POST" + "verb": "POST" }, "hvals": { "url": "/ms/_hvals/:_id", - `verb`: "GET" + "verb": "GET" }, "hlen": { "url": "/ms/_hlen/:_id", - `verb`: "GET" + "verb": "GET" }, "hkeys": { "url": "/ms/_hkeys/:_id", - `verb`: "GET" + "verb": "GET" }, "hgetall": { "url": "/ms/_hgetall/:_id", - `verb`: "GET" + "verb": "GET" }, "get": { "url": "/ms/:_id", - `verb`: "GET" + "verb": "GET" }, "decr": { "url": "/ms/_decr/:_id", - `verb`: "POST" + "verb": "POST" }, "rpushx": { "url": "/ms/_rpushx/:_id", - `verb`: "POST" + "verb": "POST" }, "lpushx": { "url": "/ms/_lpushx/:_id", - `verb`: "POST" + "verb": "POST" }, "getset": { "url": "/ms/_getset/:_id", - `verb`: "POST" + "verb": "POST" }, "sinter": { "url": "/ms/_sinter", - `verb`: "GET" + "verb": "GET" }, "pfcount": { "url": "/ms/_pfcount", - `verb`: "GET" + "verb": "GET" }, "incrbyfloat": { "url": "/ms/_incrbyfloat/:_id", - `verb`: "POST" + "verb": "POST" }, "incrby": { "url": "/ms/_incrby/:_id", - `verb`: "POST" + "verb": "POST" }, "geopos": { "url": "/ms/_geopos/:_id", - `verb`: "GET" + "verb": "GET" }, "hget": { "url": "/ms/_hget/:_id/:field", - `verb`: "GET" + "verb": "GET" }, "hsetnx": { "url": "/ms/_hsetnx/:_id", - `verb`: "POST" + "verb": "POST" }, "msetnx": { "url": "/ms/_msetnx", - `verb`: "POST" + "verb": "POST" }, "rpush": { "url": "/ms/_rpush/:_id", - `verb`: "POST" + "verb": "POST" }, "hincrbyfloat": { "url": "/ms/_hincrbyfloat/:_id", - `verb`: "POST" + "verb": "POST" }, "zrevrange": { "url": "/ms/_zrevrange/:_id", - `verb`: "GET" + "verb": "GET" }, "zrank": { "url": "/ms/_zrank/:_id/:member", - `verb`: "GET" + "verb": "GET" }, "zscore": { "url": "/ms/_zscore/:_id/:member", - `verb`: "GET" + "verb": "GET" }, "zscan": { "url": "/ms/_zscan/:_id", - `verb`: "GET" + "verb": "GET" }, "hscan": { "url": "/ms/_hscan/:_id", - `verb`: "GET" + "verb": "GET" }, "exists": { "url": "/ms/_exists", - `verb`: "GET" + "verb": "GET" } }, "realtime": { "list": { "url": "/_listSubscriptions", - `verb`: "GET" + "verb": "GET" }, "publish": { "url": "/:index/:collection/_publish", - `verb`: "POST" + "verb": "POST" } }, "server": { "getStats": { "url": "/_getStats", - `verb`: "GET" + "verb": "GET" }, "getLastStats": { "url": "/_getLastStats", - `verb`: "GET" + "verb": "GET" }, "getAllStats": { "url": "/_getAllStats", - `verb`: "GET" + "verb": "GET" }, "getConfig": { "url": "/_getConfig", - `verb`: "GET" + "verb": "GET" }, "adminExists": { "url": "/_adminExists", - `verb`: "GET" + "verb": "GET" }, "now": { "url": "/_now", - `verb`: "GET" + "verb": "GET" }, "healthCheck": { "url": "/_healthCheck", - `verb`: "GET" + "verb": "GET" }, "info": { "url": "/", - `verb`: "GET" + "verb": "GET" } }, "admin": { "resetKuzzleData": { "url": "/admin/_resetKuzzleData/", - `verb`: "POST" + "verb": "POST" }, "resetCache": { "url": "/admin/_resetCache", - `verb`: "POST" + "verb": "POST" }, "resetSecurity": { "url": "/admin/_resetSecurity/", - `verb`: "POST" + "verb": "POST" }, "resetDatabase": { "url": "/admin/_resetDatabase/", - `verb`: "POST" + "verb": "POST" }, "dump": { "url": "/admin/_dump", - `verb`: "POST" + "verb": "POST" }, "shutdown": { "url": "/admin/_shutdown/", - `verb`: "POST" + "verb": "POST" }, "loadFixtures": { "url": "/admin/_loadFixtures/", - `verb`: "POST" + "verb": "POST" }, "loadMappings": { "url": "/admin/_loadMappings/", - `verb`: "POST" + "verb": "POST" }, "loadSecurities": { "url": "/admin/_loadSecurities/", - `verb`: "POST" + "verb": "POST" } } } From 5bc582c515804975ac75bfe031d1804421c49dc0 Mon Sep 17 00:00:00 2001 From: Aschen Date: Thu, 16 Jul 2020 12:27:12 +0200 Subject: [PATCH 14/24] nit --- src/core/searchResult/SearchResultBase.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/searchResult/SearchResultBase.ts b/src/core/searchResult/SearchResultBase.ts index 9530c93d5..43885e60b 100644 --- a/src/core/searchResult/SearchResultBase.ts +++ b/src/core/searchResult/SearchResultBase.ts @@ -115,7 +115,7 @@ export class SearchResultBase implements SearchResult { // It resulting in having less fetched documents than the total and thus the SDK // try to fetch the next results page but it's empty if (! hit) { - return Promise.reject(new Error('Unable to retrieve all results from search: the sort combination must identify one item only. Add document `_id` to the sort.')); + return Promise.reject(new Error('Unable to retrieve all results from search: the sort combination must identify one item only. Add document "_id" to the sort.')); } request.body.search_after = []; From 2f62b54222422057787eafde04dbc76fb857134f Mon Sep 17 00:00:00 2001 From: Aschen Date: Thu, 16 Jul 2020 12:30:31 +0200 Subject: [PATCH 15/24] 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 5c73d6d46bfb6707e3cf624933f3c44fb16d070d Mon Sep 17 00:00:00 2001 From: Aschen Date: Fri, 17 Jul 2020 07:22:59 +0200 Subject: [PATCH 16/24] Add templated SearchResultBase --- src/controllers/Document.ts | 4 ++-- src/core/searchResult/Document.ts | 3 ++- src/core/searchResult/SearchResultBase.ts | 12 ++++++------ src/utils/interfaces.ts | 20 +++++++++++++++++--- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/controllers/Document.ts b/src/controllers/Document.ts index 4dac72eea..2ce615832 100644 --- a/src/controllers/Document.ts +++ b/src/controllers/Document.ts @@ -1,7 +1,7 @@ import { BaseController } from './Base'; import { SearchResult } from '../core/searchResult/SearchResultBase'; import { DocumentsSearchResult } from '../core/searchResult/Document'; -import { JSONObject, Document } from '../utils/interfaces'; +import { JSONObject, Document, DocumentHit } from '../utils/interfaces'; export class DocumentController extends BaseController { constructor (kuzzle) { @@ -637,7 +637,7 @@ export class DocumentController extends BaseController { scroll?: string; verb?: string; } = {} - ): Promise { + ): Promise> { return this._search(index, collection, query, options) .then(({ response, request, opts }) => ( new DocumentsSearchResult(this.kuzzle, request, opts, response.result) diff --git a/src/core/searchResult/Document.ts b/src/core/searchResult/Document.ts index 6a87c9d3a..9902d6864 100644 --- a/src/core/searchResult/Document.ts +++ b/src/core/searchResult/Document.ts @@ -1,6 +1,7 @@ import { SearchResultBase } from './SearchResultBase'; +import { DocumentHit } from '../../utils/interfaces'; -export class DocumentsSearchResult extends SearchResultBase { +export class DocumentsSearchResult extends SearchResultBase { /** * @param {Kuzzle} kuzzle * @param {object} query diff --git a/src/core/searchResult/SearchResultBase.ts b/src/core/searchResult/SearchResultBase.ts index 43885e60b..a6c2da445 100644 --- a/src/core/searchResult/SearchResultBase.ts +++ b/src/core/searchResult/SearchResultBase.ts @@ -1,7 +1,7 @@ import { JSONObject, KuzzleRequest } from '../../utils/interfaces'; import { Kuzzle } from '../../Kuzzle'; -export interface SearchResult { +export interface SearchResult { /** * Search aggregations */ @@ -10,7 +10,7 @@ export interface SearchResult { /** * Page results */ - hits: Array; + hits: Array; /** * Total number of items that can be retrieved @@ -35,10 +35,10 @@ export interface SearchResult { * * @returns A SearchResult or null if no more pages */ - next (): Promise; + next (): Promise | null>; } -export class SearchResultBase implements SearchResult { +export class SearchResultBase implements SearchResult { protected _searchAction: string; protected _scrollAction: string; protected _controller: string; @@ -49,7 +49,7 @@ export class SearchResultBase implements SearchResult { public aggregations?: JSONObject; - public hits: Array; + public hits: Array; public total: number; @@ -91,7 +91,7 @@ export class SearchResultBase implements SearchResult { this.total = response.total || 0; } - next (): Promise { + next (): Promise | null> { if (this.fetched >= this.total) { return Promise.resolve(null); } diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index 73428df57..6062231a3 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -132,12 +132,16 @@ export interface ApiKey { /** * Kuzzle document + * + * @property _id + * @property _version + * @property _source */ export interface Document { /** * Document unique ID */ - _id: string; + _id?: string; /** * Document Version (generated by Elasticsearch) */ @@ -151,7 +155,7 @@ export interface Document { * Kuzzle metadata * @see https://docs.kuzzle.io/core/2/guides/essentials/document-metadata/ */ - _kuzzle_info: { + _kuzzle_info?: { /** * Kuid of the user who created the document */ @@ -170,4 +174,14 @@ export interface Document { updatedAt: number | null } }; -} \ No newline at end of file +} + +/** + * Document retrieved from a search + */ +export interface DocumentHit extends Document { + /** + * Relevance score + */ + _score: number; +} From 7c1f8d8a6b775678a2cf94ff06340f88dda0ac32 Mon Sep 17 00:00:00 2001 From: Aschen Date: Tue, 21 Jul 2020 07:52:30 +0200 Subject: [PATCH 17/24] Add realtime --- .eslintignore | 1 + .gitignore | 1 + .../controllers/auth/create-api-key/index.md | 2 +- doc/7/controllers/realtime/count/index.md | 2 +- .../security/create-api-key/index.md | 2 +- .../realtime-notifications/index.md | 4 +- index.ts | 22 +- src/controllers/Auth.ts | 2 +- src/controllers/Realtime.js | 320 +++++++++++------- src/controllers/Realtime.ts | 259 ++++++++++++++ src/utils/interfaces.ts | 135 +++++++- test/controllers/realtime.test.js | 34 +- 12 files changed, 633 insertions(+), 151 deletions(-) create mode 100644 src/controllers/Realtime.ts diff --git a/.eslintignore b/.eslintignore index d849ec6cc..644e46fdc 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,6 +5,7 @@ src/**/*.js.map src/Kuzzle.js src/controllers/Auth.js src/controllers/Document.js +src/controllers/Realtime.js src/controllers/Base.js src/core/security/User.js src/core/security/Profile.js diff --git a/.gitignore b/.gitignore index 1a1af6994..6aac492ce 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ index.js src/Kuzzle.js src/controllers/Auth.js src/controllers/Document.js +src/controllers/Realtime.js src/controllers/Base.js src/core/security/User.js src/core/security/Profile.js diff --git a/doc/7/controllers/auth/create-api-key/index.md b/doc/7/controllers/auth/create-api-key/index.md index d7a2ca918..f0f301b9f 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
| Expiration date in UNIX micro-timestamp format (`-1` if the token never expires) | +| `expiresAt` |
number
| Expiration date in Epoch-millis 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/realtime/count/index.md b/doc/7/controllers/realtime/count/index.md index d8beddf88..dd5c460ed 100644 --- a/doc/7/controllers/realtime/count/index.md +++ b/doc/7/controllers/realtime/count/index.md @@ -32,7 +32,7 @@ Additional query options ## Resolves -Resolves to a number represensting active connections using the same provided subscription room. +Resolves to a number representing active connections using the same provided subscription room. ## Usage diff --git a/doc/7/controllers/security/create-api-key/index.md b/doc/7/controllers/security/create-api-key/index.md index 35202ecb1..ae4722cb0 100644 --- a/doc/7/controllers/security/create-api-key/index.md +++ b/doc/7/controllers/security/create-api-key/index.md @@ -57,7 +57,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
| Aexpiration date in Epoch-millis 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/essentials/realtime-notifications/index.md b/doc/7/essentials/realtime-notifications/index.md index cdc976ce7..86583db0d 100644 --- a/doc/7/essentials/realtime-notifications/index.md +++ b/doc/7/essentials/realtime-notifications/index.md @@ -23,7 +23,7 @@ These notifications represent [documents changes & messages](/core/2/api/essenti | `protocol` |
string
| Network protocol used to modify the document | | `result` |
object
| Notification content | | `room` |
string
| Subscription channel identifier. Can be used to link a notification to its corresponding subscription | -| `scope` |
string
| `in`: document enters (or stays) in the scope
string | `in`: document enters (or stays) in the scope
`out`: document leaves the scope | | `timestamp` |
number
| Timestamp of the event, in Epoch-millis format | | `type` |
string
| `document`: Notification type | | `volatile` |
object
| Request [volatile data](/core/2/api/essentials/volatile-data) | @@ -50,7 +50,7 @@ These notifications represent [user events](/core/2/api/essentials/notifications | `room` |
string
| Subscription channel identifier. Can be used to link a notification to its corresponding subscription | | `timestamp` |
number
| Timestamp of the event, in Epoch-millis format | | `type` |
string
| `user`: Notification type | -| `user` |
string
| `in`: a new user has subscribed to the same filters
string | `in`: a new user has subscribed to the same filters
`out`: a user cancelled a shared subscription | | `volatile` |
object
| Request [volatile data](/core/2/api/essentials/volatile-data) | The `result` object is the notification content, and it has the following structure: diff --git a/index.ts b/index.ts index 2fbee87ec..174002ded 100644 --- a/index.ts +++ b/index.ts @@ -8,6 +8,20 @@ if (typeof window !== 'undefined' && typeof BUILT === 'undefined') { 'Learn more at https://github.com/kuzzleio/sdk-javascript/tree/master#browser'); } +export * from './src/Kuzzle' +export * from './src/protocols/Http'; +export * from './src/protocols/WebSocket'; +export * from './src/controllers/Base'; +export * from './src/protocols/abstract/Base'; +export * from './src/core/KuzzleEventEmitter'; + +export * from './src/core/searchResult/SearchResultBase'; +export * from './src/core/searchResult/Document'; +export * from './src/core/searchResult/Profile'; +export * from './src/core/searchResult/Role'; +export * from './src/core/searchResult/Specifications'; +export * from './src/core/searchResult/User'; + import { Kuzzle } from './src/Kuzzle'; import { Http, WebSocket } from './src/protocols'; import * as BaseController from './src/controllers/Base'; @@ -21,7 +35,7 @@ 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 = { +module.exports = { Kuzzle, Http, WebSocket, @@ -34,8 +48,4 @@ const exported = { RoleSearchResult, SpecificationSearchResult, UserSearchResult -} - -export default exported; - -module.exports = exported; \ No newline at end of file +}; \ No newline at end of file diff --git a/src/controllers/Auth.ts b/src/controllers/Auth.ts index e9bd3c30d..7cd56f216 100644 --- a/src/controllers/Auth.ts +++ b/src/controllers/Auth.ts @@ -500,7 +500,7 @@ export class AuthController extends BaseController { */ _id: string; /** - * Expiration date in UNIX micro-timestamp format (-1 if the token never expires) + * Expiration date in Epoch-millis format (-1 if the token never expires) */ expiresAt: number; /** diff --git a/src/controllers/Realtime.js b/src/controllers/Realtime.js index e68478ba2..07d89d661 100644 --- a/src/controllers/Realtime.js +++ b/src/controllers/Realtime.js @@ -1,128 +1,206 @@ -const { BaseController } = require('./Base'); -const Room = require('../core/Room'); - - -class RealtimeController extends BaseController { - /** - * @param {Kuzzle} kuzzle - */ - constructor (kuzzle) { - super(kuzzle, 'realtime'); - - this.subscriptions = new Map(); - this.subscriptionsOff = new Map(); - - this.kuzzle.on('tokenExpired', () => this.tokenExpired()); - } - - count (roomId, options = {}) { - return this.query({ - action: 'count', - body: {roomId} - }, options) - .then(response => response.result.count); - } - - publish (index, collection, message, options = {}) { - const request = { - index, - collection, - body: message, - action: 'publish', - _id: options._id - }; - - return this.query(request, options) - .then(response => response.result.published); - } - - subscribe (index, collection, filters, callback, options = {}) { - const room = new Room(this, index, collection, filters, callback, options); - - return room.subscribe() - .then(() => { - if (!this.subscriptions.has(room.id)) { - this.subscriptions.set(room.id, []); - } - this.subscriptions.get(room.id).push(room); - return room.id; - }); - } - - unsubscribe (roomId, options = {}) { - const request = { - action: 'unsubscribe', - body: { roomId } - }; - - return this.query(request, options) - .then(response => { - const rooms = this.subscriptions.get(roomId); - - if (rooms) { - for (const room of rooms) { - room.removeListeners(); - } - - this.subscriptions.delete(roomId); - } - - return response.result; - }); - } - - // called on network error or disconnection - disconnected () { - for (const roomId of this.subscriptions.keys()) { - for (const room of this.subscriptions.get(roomId)) { - room.removeListeners(); - - if (room.autoResubscribe) { - if (!this.subscriptionsOff.has(roomId)) { - this.subscriptionsOff.set(roomId, []); - } - this.subscriptionsOff.get(roomId).push(room); +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.RealtimeController = exports.UserOption = exports.ScopeOption = void 0; +const Base_1 = require("./Base"); +const Room_1 = require("../core/Room"); +/** + * Enum for `scope` option of realtime.subscribe method + */ +var ScopeOption; +(function (ScopeOption) { + /** + * Receive all document notifications + */ + ScopeOption["all"] = "all"; + /** + * Receive notifications when document enter or stay in the scope + */ + ScopeOption["in"] = "in"; + /** + * Receive notification when document exit the scope + */ + ScopeOption["out"] = "out"; + /** + * Do not receive document notifications + */ + ScopeOption["none"] = "none"; +})(ScopeOption = exports.ScopeOption || (exports.ScopeOption = {})); +/** + * Enum for `user` option of realtime.subscribe method + */ +var UserOption; +(function (UserOption) { + /** + * Receive all user notifications + */ + UserOption["all"] = "all"; + /** + * Receive notification when users join the room + */ + UserOption["in"] = "in"; + /** + * Receive notifications when users leave the room + */ + UserOption["out"] = "out"; + /** + * Do not receive user notifications + */ + UserOption["none"] = "none"; +})(UserOption = exports.UserOption || (exports.UserOption = {})); +class RealtimeController extends Base_1.BaseController { + constructor(kuzzle) { + super(kuzzle, 'realtime'); + this._subscriptions = new Map(); + this._subscriptionsOff = new Map(); + this.kuzzle.on('tokenExpired', () => this.tokenExpired()); + } + /** + * Returns the number of other connections sharing the same subscription. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/realtime/count/ + * + * @param roomId Subscription room ID + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns A number represensting active connections using the same provided subscription room. + */ + count(roomId, options = {}) { + return this.query({ + action: 'count', + body: { roomId } + }, options) + .then(response => response.result.count); + } + /** + * Sends a real-time message to Kuzzle. + * + * The message will be dispatched to all clients with subscriptions + * matching the index, the collection and the message content. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/realtime/count/ + * + * @param index Index name + * @param collection Collection name + * @param message Message to send (will be put in `_source` property of the notification) + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `_id` Additional unique ID (will be put in the `_id` property of the notification) + */ + publish(index, collection, message, options = {}) { + const request = { + index, + collection, + body: message, + action: 'publish', + _id: options._id + }; + return this.query(request, options) + .then(response => response.result.published); + } + /** + * Subscribes by providing a set of filters: messages, document changes + * and, optionally, user events matching the provided filters will generate + * real-time notifications. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/realtime/subscribe/ + * @see https://docs.kuzzle.io/sdk/js/7/essentials/realtime-notifications/ + * + * @param index Index name + * @param collection Collection name + * @param filters Optional subscription filters (@see https://docs.kuzzle.io/core/2/guides/cookbooks/realtime-api) + * @param callback Callback function to handle notifications + * + * @returns A string containing the room ID + */ + subscribe(index, collection, filters, callback, options = {}) { + const room = new Room_1.Room(this, index, collection, filters, callback, options); + return room.subscribe() + .then(() => { + if (!this._subscriptions.has(room.id)) { + this._subscriptions.set(room.id, []); + } + this._subscriptions.get(room.id).push(room); + return room.id; + }); + } + /** + * Removes a subscription + * + * @param roomId Subscription room ID + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + */ + unsubscribe(roomId, options = {}) { + const request = { + action: 'unsubscribe', + body: { roomId } + }; + return this.query(request, options) + .then(response => { + const rooms = this._subscriptions.get(roomId); + if (rooms) { + for (const room of rooms) { + room.removeListeners(); + } + this._subscriptions.delete(roomId); + } + return response.result; + }); + } + /** + * Internal method. + * + * Should be called on network error or disconnection + */ + disconnected() { + for (const roomId of this._subscriptions.keys()) { + for (const room of this._subscriptions.get(roomId)) { + room.removeListeners(); + if (room.autoResubscribe) { + if (!this._subscriptionsOff.has(roomId)) { + this._subscriptionsOff.set(roomId, []); + } + this._subscriptionsOff.get(roomId).push(room); + } + } + this._subscriptions.delete(roomId); } - } - - this.subscriptions.delete(roomId); } - } - - /** - * Called on kuzzle reconnection. - * Resubscribe to eligible disabled rooms. - */ - reconnected () { - for (const roomId of this.subscriptionsOff.keys()) { - for (const room of this.subscriptionsOff.get(roomId)) { - if (!this.subscriptions.has(roomId)) { - this.subscriptions.set(roomId, []); + /** + * Internal method. + * + * Called on kuzzle reconnection. + * Resubscribe to eligible disabled rooms. + */ + reconnected() { + for (const roomId of this._subscriptionsOff.keys()) { + for (const room of this._subscriptionsOff.get(roomId)) { + if (!this._subscriptions.has(roomId)) { + this._subscriptions.set(roomId, []); + } + this._subscriptions.get(roomId).push(room); + room.subscribe() + .catch(() => this.kuzzle.emit('discarded', { request: room.request })); + } + this._subscriptionsOff.delete(roomId); } - this.subscriptions.get(roomId).push(room); - - room.subscribe() - .catch(() => this.kuzzle.emit('discarded', {request: room.request})); - } - - this.subscriptionsOff.delete(roomId); } - } - - /** - * Removes all subscriptions. - */ - tokenExpired() { - for (const roomId of this.subscriptions.keys()) { - for (const room of this.subscriptions.get(roomId)) { - room.removeListeners(); - } + /** + * Internal method. + * + * Removes all subscriptions. + */ + tokenExpired() { + for (const roomId of this._subscriptions.keys()) { + for (const room of this._subscriptions.get(roomId)) { + room.removeListeners(); + } + } + this._subscriptions = new Map(); + this._subscriptionsOff = new Map(); } - - this.subscriptions = new Map(); - this.subscriptionsOff = new Map(); - } - } - +exports.RealtimeController = RealtimeController; module.exports = { RealtimeController }; +//# sourceMappingURL=Realtime.js.map \ No newline at end of file diff --git a/src/controllers/Realtime.ts b/src/controllers/Realtime.ts new file mode 100644 index 000000000..8f40e0555 --- /dev/null +++ b/src/controllers/Realtime.ts @@ -0,0 +1,259 @@ +import { BaseController } from './Base'; +import { Room } from '../core/Room'; +import { JSONObject } from '../utils/interfaces'; + +/** + * Enum for `scope` option of realtime.subscribe method + */ +export enum ScopeOption { + /** + * Receive all document notifications + */ + all = 'all', + /** + * Receive notifications when document enter or stay in the scope + */ + in = 'in', + /** + * Receive notification when document exit the scope + */ + out = 'out', + /** + * Do not receive document notifications + */ + none = 'none' +} + +/** + * Enum for `user` option of realtime.subscribe method + */ +export enum UserOption { + /** + * Receive all user notifications + */ + all = 'all', + /** + * Receive notification when users join the room + */ + in = 'in', + /** + * Receive notifications when users leave the room + */ + out = 'out', + /** + * Do not receive user notifications + */ + none = 'none' +} + +export class RealtimeController extends BaseController { + private _subscriptions: Map>; + private _subscriptionsOff: Map>; + + constructor (kuzzle) { + super(kuzzle, 'realtime'); + + this._subscriptions = new Map(); + this._subscriptionsOff = new Map(); + + this.kuzzle.on('tokenExpired', () => this.tokenExpired()); + } + + /** + * Returns the number of other connections sharing the same subscription. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/realtime/count/ + * + * @param roomId Subscription room ID + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * + * @returns A number represensting active connections using the same provided subscription room. + */ + count (roomId: string, options: { queuable?: boolean } = {}): Promise { + return this.query({ + action: 'count', + body: { roomId } + }, options) + .then(response => response.result.count); + } + + /** + * Sends a real-time message to Kuzzle. + * + * The message will be dispatched to all clients with subscriptions + * matching the index, the collection and the message content. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/realtime/count/ + * + * @param index Index name + * @param collection Collection name + * @param message Message to send (will be put in `_source` property of the notification) + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + * - `_id` Additional unique ID (will be put in the `_id` property of the notification) + */ + publish ( + index: string, + collection: string, + message: JSONObject, + options: { queuable?: boolean, _id?: string } = {} + ): Promise { + const request = { + index, + collection, + body: message, + action: 'publish', + _id: options._id + }; + + return this.query(request, options) + .then(response => response.result.published); + } + + /** + * Subscribes by providing a set of filters: messages, document changes + * and, optionally, user events matching the provided filters will generate + * real-time notifications. + * + * @see https://docs.kuzzle.io/sdk/js/7/controllers/realtime/subscribe/ + * @see https://docs.kuzzle.io/sdk/js/7/essentials/realtime-notifications/ + * + * @param index Index name + * @param collection Collection name + * @param filters Optional subscription filters (@see https://docs.kuzzle.io/core/2/guides/cookbooks/realtime-api) + * @param callback Callback function to handle notifications + * + * @returns A string containing the room ID + */ + subscribe ( + index: string, + collection: string, + filters: JSONObject, + callback: (Notification) => void | Promise, + options: { + /** + * Subscribe to document entering or leaving the scope. (default: 'all') + */ + scope?: ScopeOption; + /** + * Subscribe to users entering or leaving the room. (default: 'none') + */ + users?: UserOption; + /** + * Subscribe to notifications fired by our own queries. (default: true) + */ + subscribeToSelf?: boolean; + /** + * Subscription information sent alongside notifications + */ + volatile?: JSONObject; + } = {} + ): Promise { + const room = new Room(this, index, collection, filters, callback, options); + + return room.subscribe() + .then(() => { + if (!this._subscriptions.has(room.id)) { + this._subscriptions.set(room.id, []); + } + this._subscriptions.get(room.id).push(room); + return room.id; + }); + } + + /** + * Removes a subscription + * + * @param roomId Subscription room ID + * @param options Additional options + * - `queuable` If true, queues the request during downtime, until connected to Kuzzle again + */ + unsubscribe ( + roomId: string, + options: { queuable?: boolean } = {} + ): Promise { + const request = { + action: 'unsubscribe', + body: { roomId } + }; + + return this.query(request, options) + .then(response => { + const rooms = this._subscriptions.get(roomId); + + if (rooms) { + for (const room of rooms) { + room.removeListeners(); + } + + this._subscriptions.delete(roomId); + } + + return response.result; + }); + } + + /** + * Internal method. + * + * Should be called on network error or disconnection + */ + disconnected () { + for (const roomId of this._subscriptions.keys()) { + for (const room of this._subscriptions.get(roomId)) { + room.removeListeners(); + + if (room.autoResubscribe) { + if (!this._subscriptionsOff.has(roomId)) { + this._subscriptionsOff.set(roomId, []); + } + this._subscriptionsOff.get(roomId).push(room); + } + } + + this._subscriptions.delete(roomId); + } + } + + /** + * Internal method. + * + * Called on kuzzle reconnection. + * Resubscribe to eligible disabled rooms. + */ + reconnected () { + for (const roomId of this._subscriptionsOff.keys()) { + for (const room of this._subscriptionsOff.get(roomId)) { + if (!this._subscriptions.has(roomId)) { + this._subscriptions.set(roomId, []); + } + this._subscriptions.get(roomId).push(room); + + room.subscribe() + .catch(() => this.kuzzle.emit('discarded', {request: room.request})); + } + + this._subscriptionsOff.delete(roomId); + } + } + + /** + * Internal method. + * + * Removes all subscriptions. + */ + tokenExpired() { + for (const roomId of this._subscriptions.keys()) { + for (const room of this._subscriptions.get(roomId)) { + room.removeListeners(); + } + } + + this._subscriptions = new Map(); + this._subscriptionsOff = new Map(); + } + +} + +module.exports = { RealtimeController }; diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index 6062231a3..07db9ae85 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -112,7 +112,7 @@ export interface ApiKey { */ userId: string; /** - * Expiration date in UNIX micro-timestamp format (-1 if the token never expires) + * Expiration date in Epoch-millis format (-1 if the token never expires) */ expiresAt: number; /** @@ -185,3 +185,136 @@ export interface DocumentHit extends Document { */ _score: number; } + +/** + * Enum for notification types + */ +export enum ENotificationType { + document = 'document', + user = 'user', + TokenExpired = 'TokenExpired' +} + +/** + * Real-time notifications sent by Kuzzle. + * + */ +export interface Notification { + /** + * Notification type + */ + type: ENotificationType; +} + +export interface BaseNotification extends Notification { + /** + * Controller that triggered the notification + */ + controller: string; + /** + * Action that triggered the notification + */ + action: string; + /** + * Index name + */ + index: string; + /** + * Collection name + */ + collection: string; + /** + * Network protocol used to trigger the notification + */ + protocol: string; + /** + * Subscription channel identifier. + * Can be used to link a notification to its corresponding subscription + */ + room: string; + /** + * Timestamp of the event, in Epoch-millis format + */ + timestamp: number; + /** + * Request volatile data + * @see https://docs.kuzzle.io/core/2/guides/essentials/volatile-data/ + */ + volatile: JSONObject; +} + +/** + * State of the document regarding the scope + */ +export enum EDocumentScope { + /** + * Document enters or stays in the scope + */ + in = 'in', + /** + * Document exit the scope + */ + out = 'out' +} + +/** + * Notification triggered by a document change. + * (create, update, delete) + */ +export interface DocumentNotification extends BaseNotification { + /** + * Updated document that triggered the notification + */ + result: Document; + /** + * State of the document regarding the scope (`in` or `out`) + */ + scope: EDocumentScope; + + type: ENotificationType.document; +} + +/** + * Tells wether an user leave or join the subscription room + */ +export enum EUserScope { + /** + * User enters the subscription room + */ + in = 'in', + /** + * User leaves the subscription room + */ + out = 'out' +} + +/** + * Notification triggered by an user joining or leaving a subscription room + */ +export interface UserNotification extends BaseNotification { + /** + * Tell wether an user leave or join the subscription room (`in` or `out`) + */ + user: EUserScope; + + /** + * Contains the actual number of users in the subscription room + */ + result: { + /** + * Updated users count sharing the same subscription room + */ + count: number; + } + + type: ENotificationType.user; +} + +export interface ServerNotification extends BaseNotification { + /** + * Server message explaining why this notifications has been triggered + */ + message: string; + + type: ENotificationType.TokenExpired; +} \ No newline at end of file diff --git a/test/controllers/realtime.test.js b/test/controllers/realtime.test.js index c264a24cb..4aefd8e2b 100644 --- a/test/controllers/realtime.test.js +++ b/test/controllers/realtime.test.js @@ -139,17 +139,17 @@ describe('Realtime Controller', () => { body = {foo: 'bar'}, cb = sinon.stub(); - kuzzle.realtime.subscriptions = new Map(); + kuzzle.realtime._subscriptions = new Map(); return kuzzle.realtime.subscribe('index', 'collection', body, cb, options) .then(() => { - const subscriptions = kuzzle.realtime.subscriptions.get(roomId); + const subscriptions = kuzzle.realtime._subscriptions.get(roomId); should(subscriptions).be.an.Array(); should(subscriptions.length).be.exactly(1); should(subscriptions[0]).be.exactly(room); return kuzzle.realtime.subscribe('index', 'collection', body, cb, options); }).then(() => { - const subscriptions = kuzzle.realtime.subscriptions.get(roomId); + const subscriptions = kuzzle.realtime._subscriptions.get(roomId); should(subscriptions).be.an.Array(); should(subscriptions.length).be.exactly(2); @@ -175,8 +175,8 @@ describe('Realtime Controller', () => { room1.removeListeners.reset(); room2.removeListeners.reset(); - kuzzle.realtime.subscriptions.set(roomId, [room1, room2]); - kuzzle.realtime.subscriptions.set('foo', [room3]); + kuzzle.realtime._subscriptions.set(roomId, [room1, room2]); + kuzzle.realtime._subscriptions.set('foo', [room3]); kuzzle.query.resolves({result: roomId}); }); @@ -193,12 +193,12 @@ describe('Realtime Controller', () => { it('should delete rooms from local storage', () => { return kuzzle.realtime.unsubscribe(roomId) .then(() => { - should(kuzzle.realtime.subscriptions.get(roomId)).be.undefined(); + should(kuzzle.realtime._subscriptions.get(roomId)).be.undefined(); // Check we do not remove other registered rooms: - should(kuzzle.realtime.subscriptions.get('foo')).be.an.Array(); - should(kuzzle.realtime.subscriptions.get('foo').length).be.equal(1); - should(kuzzle.realtime.subscriptions.get('foo')[0]).be.equal(room3); + should(kuzzle.realtime._subscriptions.get('foo')).be.an.Array(); + should(kuzzle.realtime._subscriptions.get('foo').length).be.equal(1); + should(kuzzle.realtime._subscriptions.get('foo')[0]).be.equal(room3); }); }); @@ -233,15 +233,15 @@ describe('Realtime Controller', () => { removeListeners: sinon.stub() }; - kuzzle.realtime.subscriptions = new Map([ + kuzzle.realtime._subscriptions = new Map([ ['foo', [roomA, roomB]], ['bar', [roomC]] ]); kuzzle.realtime.disconnected(); - should(kuzzle.realtime.subscriptions).be.empty(); - should(kuzzle.realtime.subscriptionsOff).eql(new Map([ + should(kuzzle.realtime._subscriptions).be.empty(); + should(kuzzle.realtime._subscriptionsOff).eql(new Map([ ['foo', [roomA]] ])); for (const room of [roomA, roomB, roomC]) { @@ -256,15 +256,15 @@ describe('Realtime Controller', () => { const roomB = { subscribe: sinon.stub().resolves() }; const roomC = { subscribe: sinon.stub().resolves() }; - kuzzle.realtime.subscriptionsOff = new Map([ + kuzzle.realtime._subscriptionsOff = new Map([ ['foo', [roomA, roomB]], ['bar', [roomC]] ]); kuzzle.realtime.reconnected(); - should(kuzzle.realtime.subscriptionsOff).be.empty(); - should(kuzzle.realtime.subscriptions).eql(new Map([ + should(kuzzle.realtime._subscriptionsOff).be.empty(); + should(kuzzle.realtime._subscriptions).eql(new Map([ ['foo', [roomA, roomB]], ['bar', [roomC]] ])); @@ -283,12 +283,12 @@ describe('Realtime Controller', () => { kuzzle.jwt = 'foobar'; for (let i = 0; i < 10; i++) { - kuzzle.realtime.subscriptions.set(uuidv4(), [{removeListeners: stub}]); + kuzzle.realtime._subscriptions.set(uuidv4(), [{removeListeners: stub}]); } kuzzle.realtime.tokenExpired(); - should(kuzzle.realtime.subscriptions).be.empty(); + should(kuzzle.realtime._subscriptions).be.empty(); should(stub.callCount).be.eql(10); }); }); From 0a8c377f8f514c6e32459661d27e298cbd4116ff Mon Sep 17 00:00:00 2001 From: Aschen Date: Wed, 22 Jul 2020 06:07:28 +0200 Subject: [PATCH 18/24] Realtime ok --- index.ts | 2 +- src/utils/interfaces.ts | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/index.ts b/index.ts index a79185ae0..1b8bbd4d1 100644 --- a/index.ts +++ b/index.ts @@ -34,7 +34,7 @@ const exported = { RoleSearchResult, SpecificationSearchResult, UserSearchResult -} +}; export default exported; diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index 49140e6fb..0062ee9c7 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -112,11 +112,7 @@ export interface ApiKey { */ userId: string; /** -<<<<<<< HEAD * Expiration date in Epoch-millis format (-1 if the token never expires) -======= - * Expiration date in UNIX micro-timestamp format (-1 if the token never expires) ->>>>>>> 7-dev */ expiresAt: number; /** @@ -189,7 +185,6 @@ export interface DocumentHit extends Document { */ _score: number; } -<<<<<<< HEAD /** * Enum for notification types @@ -323,5 +318,3 @@ export interface ServerNotification extends BaseNotification { type: ENotificationType.TokenExpired; } -======= ->>>>>>> 7-dev From f8d2f99b03d6197111a897ab7161266e656afec8 Mon Sep 17 00:00:00 2001 From: Aschen Date: Fri, 31 Jul 2020 11:40:06 +0200 Subject: [PATCH 19/24] Fix unit and functional tests --- doc/7/controllers/auth/create-api-key/index.md | 4 ---- .../m-delete-profiles/snippets/m-delete-profiles.test.yml | 2 +- index.ts | 5 +++-- src/controllers/Realtime.ts | 2 +- src/core/Room.js | 2 ++ 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/doc/7/controllers/auth/create-api-key/index.md b/doc/7/controllers/auth/create-api-key/index.md index 8146e0dbb..f0f301b9f 100644 --- a/doc/7/controllers/auth/create-api-key/index.md +++ b/doc/7/controllers/auth/create-api-key/index.md @@ -56,11 +56,7 @@ The API key content has the following properties: | Name | Type | Description | | --------- | ----------------- | ---------------- | | `userId` |
string
| User kuid | -<<<<<<< HEAD | `expiresAt` |
number
| Expiration date in Epoch-millis format (`-1` if the token never expires) | -======= -| `expiresAt` |
number
| Expiration date in UNIX micro-timestamp format (`-1` if the token never expires) | ->>>>>>> 7-dev | `ttl` |
number
| Original TTL | | `description` |
string
| API key description | | `token` |
string
| Authentication token associated with this API key | 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 6fbf0d363..0c7f7e2de 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: '^\[ ''profile\d'', ''profile\d'', ''profile\d'', ''profile\d', ''profile\d'' \]$' +expected: '^\[ ''profile\d'', ''profile\d'', ''profile\d'', ''profile\d'', ''profile\d'' \]$' diff --git a/index.ts b/index.ts index 3e1bc7d24..9e46e1d98 100644 --- a/index.ts +++ b/index.ts @@ -18,7 +18,7 @@ import { SearchResultBase } from './src/core/searchResult/SearchResultBase'; import { DocumentSearchResult } from './src/core/searchResult/Document'; import { ProfileSearchResult } from './src/core/searchResult/Profile'; import { RoleSearchResult } from './src/core/searchResult/Role'; -import { SpecificationSearchResult } from './src/core/searchResult/Specifications'; +import { SpecificationsSearchResult } from './src/core/searchResult/Specifications'; import { UserSearchResult } from './src/core/searchResult/User'; const exported = { @@ -32,10 +32,11 @@ const exported = { DocumentSearchResult, ProfileSearchResult, RoleSearchResult, - SpecificationSearchResult, + SpecificationsSearchResult, UserSearchResult }; export default exported; + module.exports = exported; diff --git a/src/controllers/Realtime.ts b/src/controllers/Realtime.ts index 8f40e0555..07e0ad3ca 100644 --- a/src/controllers/Realtime.ts +++ b/src/controllers/Realtime.ts @@ -1,5 +1,5 @@ import { BaseController } from './Base'; -import { Room } from '../core/Room'; +import * as Room from '../core/Room'; import { JSONObject } from '../utils/interfaces'; /** diff --git a/src/core/Room.js b/src/core/Room.js index ddf0c4af4..25fd7a6ed 100644 --- a/src/core/Room.js +++ b/src/core/Room.js @@ -1,3 +1,5 @@ +'use strict'; + class Room { /** From 294ca42a9c58ce5399ef7ef2e07d18e7b7df6663 Mon Sep 17 00:00:00 2001 From: Aschen Date: Fri, 31 Jul 2020 11:56:09 +0200 Subject: [PATCH 20/24] Fix func test --- .../security/m-delete-roles/snippets/m-delete-roles.test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/7/controllers/security/m-delete-roles/snippets/m-delete-roles.test.yml b/doc/7/controllers/security/m-delete-roles/snippets/m-delete-roles.test.yml index 5578d3834..9926cee32 100644 --- a/doc/7/controllers/security/m-delete-roles/snippets/m-delete-roles.test.yml +++ b/doc/7/controllers/security/m-delete-roles/snippets/m-delete-roles.test.yml @@ -14,4 +14,4 @@ hooks: }' kuzzle:7512/roles/role${i}/_create done template: default -expected: '^\[ ''role1'', ''role2'', ''role3'', ''role4'', ''role5'' \]$' +expected: '^\[ ''role5'', ''role4'', ''role3'', ''role2'', ''role1'' \]$' From bb9cbf23cf9b2c951e105e1e39daa43e32390a43 Mon Sep 17 00:00:00 2001 From: Aschen Date: Fri, 31 Jul 2020 12:42:05 +0200 Subject: [PATCH 21/24] I hate ci dev --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index aa2ac4cef..393c9a5e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -78,7 +78,7 @@ jobs: - npm install - npm run build script: - - npm run doc-testing + - travis_retry npm run doc-testing - stage: Tests name: Dead link check From fb69d92d425d64a41add2e02578e296507f8ae7f Mon Sep 17 00:00:00 2001 From: Aschen Date: Fri, 31 Jul 2020 13:38:25 +0200 Subject: [PATCH 22/24] Fix func test --- .../security/m-delete-roles/snippets/m-delete-roles.test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/7/controllers/security/m-delete-roles/snippets/m-delete-roles.test.yml b/doc/7/controllers/security/m-delete-roles/snippets/m-delete-roles.test.yml index 9926cee32..1fb4967fa 100644 --- a/doc/7/controllers/security/m-delete-roles/snippets/m-delete-roles.test.yml +++ b/doc/7/controllers/security/m-delete-roles/snippets/m-delete-roles.test.yml @@ -14,4 +14,4 @@ hooks: }' kuzzle:7512/roles/role${i}/_create done template: default -expected: '^\[ ''role5'', ''role4'', ''role3'', ''role2'', ''role1'' \]$' +expected: '^\[ ''role\d'', ''role\d'', ''role\d'', ''role\d'', ''role\d'' \]$' From 29acc2907967d62e07f15bd7819ddc66d098d1cb Mon Sep 17 00:00:00 2001 From: Aschen Date: Mon, 10 Aug 2020 10:31:50 +0200 Subject: [PATCH 23/24] fix test --- .../security/m-delete-users/snippets/m-delete-users.test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/7/controllers/security/m-delete-users/snippets/m-delete-users.test.yml b/doc/7/controllers/security/m-delete-users/snippets/m-delete-users.test.yml index be9ebea3d..ef0acb81b 100644 --- a/doc/7/controllers/security/m-delete-users/snippets/m-delete-users.test.yml +++ b/doc/7/controllers/security/m-delete-users/snippets/m-delete-users.test.yml @@ -16,4 +16,4 @@ hooks: }' kuzzle:7512/users/user${i}/_create done template: default -expected: '^\[ ''user1'', ''user2'', ''user3'' \]$' +expected: '^\[ ''user\d'', ''user\d'', ''user\d'' \]$' From 6e4427167447fb933f4c086c12e60bace054433f Mon Sep 17 00:00:00 2001 From: Aschen Date: Wed, 12 Aug 2020 12:23:33 +0200 Subject: [PATCH 24/24] Rename realtime event handler methods --- src/Kuzzle.ts | 7 ---- src/controllers/Realtime.ts | 34 +++++++++---------- src/utils/interfaces.ts | 6 ++-- test/controllers/realtime.test.js | 55 +++++++++++++++++++++++++------ test/kuzzle/connect.test.js | 12 +++---- 5 files changed, 71 insertions(+), 43 deletions(-) diff --git a/src/Kuzzle.ts b/src/Kuzzle.ts index 6d25a3fde..126aea4b0 100644 --- a/src/Kuzzle.ts +++ b/src/Kuzzle.ts @@ -361,15 +361,10 @@ export class Kuzzle extends KuzzleEventEmitter { if (this.autoQueue) { this.startQueuing(); } - - this.realtime.disconnected(); - this.emit('networkError', error); }); this.protocol.addListener('disconnect', () => { - this.realtime.disconnected(); - this.emit('disconnected'); }); @@ -382,8 +377,6 @@ export class Kuzzle extends KuzzleEventEmitter { this.playQueue(); } - this.realtime.reconnected(); - if (this.auth.authenticationToken) { return this.auth.checkToken() .then(res => { diff --git a/src/controllers/Realtime.ts b/src/controllers/Realtime.ts index 07e0ad3ca..007addf64 100644 --- a/src/controllers/Realtime.ts +++ b/src/controllers/Realtime.ts @@ -1,5 +1,5 @@ import { BaseController } from './Base'; -import * as Room from '../core/Room'; +import Room from '../core/Room'; import { JSONObject } from '../utils/interfaces'; /** @@ -56,7 +56,10 @@ export class RealtimeController extends BaseController { this._subscriptions = new Map(); this._subscriptionsOff = new Map(); - this.kuzzle.on('tokenExpired', () => this.tokenExpired()); + this.kuzzle.on('tokenExpired', () => this.removeSubscriptions()); + this.kuzzle.on('disconnected', () => this.saveSubscriptions()); + this.kuzzle.on('networkError', () => this.saveSubscriptions()); + this.kuzzle.on('reconnected', () => this.resubscribe()); } /** @@ -123,6 +126,11 @@ export class RealtimeController extends BaseController { * @param collection Collection name * @param filters Optional subscription filters (@see https://docs.kuzzle.io/core/2/guides/cookbooks/realtime-api) * @param callback Callback function to handle notifications + * @param options Additional options + * - `scope` Subscribe to document entering or leaving the scope. (default: 'all') + * - `users` Subscribe to users entering or leaving the room. (default: 'none') + * - `subscribeToSelf` Subscribe to notifications fired by our own queries. (default: true) + * - `volatile` Subscription information sent alongside notifications * * @returns A string containing the room ID */ @@ -195,11 +203,9 @@ export class RealtimeController extends BaseController { } /** - * Internal method. - * - * Should be called on network error or disconnection + * Called when kuzzle is disconnected */ - disconnected () { + private saveSubscriptions () { for (const roomId of this._subscriptions.keys()) { for (const room of this._subscriptions.get(roomId)) { room.removeListeners(); @@ -217,12 +223,9 @@ export class RealtimeController extends BaseController { } /** - * Internal method. - * - * Called on kuzzle reconnection. - * Resubscribe to eligible disabled rooms. + * Called on kuzzle reconnection */ - reconnected () { + private resubscribe () { for (const roomId of this._subscriptionsOff.keys()) { for (const room of this._subscriptionsOff.get(roomId)) { if (!this._subscriptions.has(roomId)) { @@ -231,7 +234,7 @@ export class RealtimeController extends BaseController { this._subscriptions.get(roomId).push(room); room.subscribe() - .catch(() => this.kuzzle.emit('discarded', {request: room.request})); + .catch(() => this.kuzzle.emit('discarded', { request: room.request })); } this._subscriptionsOff.delete(roomId); @@ -239,11 +242,9 @@ export class RealtimeController extends BaseController { } /** - * Internal method. - * - * Removes all subscriptions. + * Called when a token expire */ - tokenExpired() { + private removeSubscriptions() { for (const roomId of this._subscriptions.keys()) { for (const room of this._subscriptions.get(roomId)) { room.removeListeners(); @@ -253,7 +254,6 @@ export class RealtimeController extends BaseController { this._subscriptions = new Map(); this._subscriptionsOff = new Map(); } - } module.exports = { RealtimeController }; diff --git a/src/utils/interfaces.ts b/src/utils/interfaces.ts index 80ce87e4a..39e444a86 100644 --- a/src/utils/interfaces.ts +++ b/src/utils/interfaces.ts @@ -199,7 +199,6 @@ export interface DocumentHit extends Document { } /** -<<<<<<< HEAD * Enum for notification types */ export enum ENotificationType { @@ -330,7 +329,9 @@ export interface ServerNotification extends BaseNotification { message: string; type: ENotificationType.TokenExpired; -======= +} + +/** * HTTP routes definition format * @example * { @@ -365,5 +366,4 @@ export interface HttpRoutes { url: string } } ->>>>>>> 7-dev } diff --git a/test/controllers/realtime.test.js b/test/controllers/realtime.test.js index 4aefd8e2b..698ecd767 100644 --- a/test/controllers/realtime.test.js +++ b/test/controllers/realtime.test.js @@ -26,17 +26,52 @@ describe('Realtime Controller', () => { }); describe('on: tokenExpired', () => { - it('should call tokenExpired() method', () => { - kuzzle.realtime.tokenExpired = sinon.stub(); + it('should call removeSubscriptions() method', () => { + kuzzle.realtime.removeSubscriptions = sinon.stub(); kuzzle.emit('tokenExpired'); process.nextTick(() => { - should(kuzzle.realtime.tokenExpired).be.called(); + should(kuzzle.realtime.removeSubscriptions).be.called(); }); }); }); + describe('on: disconnected', () => { + it('should call saveSubscriptions() method', () => { + kuzzle.realtime.saveSubscriptions = sinon.stub(); + + kuzzle.emit('disconnected'); + + process.nextTick(() => { + should(kuzzle.realtime.saveSubscriptions).be.called(); + }); + }); + }); + + describe('on: networkError', () => { + it('should call saveSubscriptions() method', () => { + kuzzle.realtime.saveSubscriptions = sinon.stub(); + + kuzzle.emit('networkError'); + + process.nextTick(() => { + should(kuzzle.realtime.saveSubscriptions).be.called(); + }); + }); + }); + + describe('on: reconnected', () => { + it('should call resubscribe() method', () => { + kuzzle.realtime.resubscribe = sinon.stub(); + + kuzzle.emit('reconnected'); + + process.nextTick(() => { + should(kuzzle.realtime.resubscribe).be.called(); + }); + }); + }); describe('#count', () => { it('should call realtime/count query with the roomId and return a Promise which resolves a number', () => { @@ -218,7 +253,7 @@ describe('Realtime Controller', () => { }); }); - describe('#disconnected', () => { + describe('#saveSubscriptions', () => { it('should disable current subscriptions', () => { const roomA = { autoResubscribe: true, @@ -238,7 +273,7 @@ describe('Realtime Controller', () => { ['bar', [roomC]] ]); - kuzzle.realtime.disconnected(); + kuzzle.realtime.saveSubscriptions(); should(kuzzle.realtime._subscriptions).be.empty(); should(kuzzle.realtime._subscriptionsOff).eql(new Map([ @@ -250,7 +285,7 @@ describe('Realtime Controller', () => { }); }); - describe('#reconnected', () => { + describe('#resubscribe', () => { it('should resubmit pending subcriptions', () => { const roomA = { subscribe: sinon.stub().resolves() }; const roomB = { subscribe: sinon.stub().resolves() }; @@ -261,7 +296,7 @@ describe('Realtime Controller', () => { ['bar', [roomC]] ]); - kuzzle.realtime.reconnected(); + kuzzle.realtime.resubscribe(); should(kuzzle.realtime._subscriptionsOff).be.empty(); should(kuzzle.realtime._subscriptions).eql(new Map([ @@ -276,8 +311,8 @@ describe('Realtime Controller', () => { }); }); - describe('#tokenExpired', () => { - it('should clear all subscriptions and emit a "tokenExpired" event', () => { + describe('#removeSubscriptions', () => { + it('should clear all subscriptions', () => { const stub = sinon.stub(); kuzzle.jwt = 'foobar'; @@ -286,7 +321,7 @@ describe('Realtime Controller', () => { kuzzle.realtime._subscriptions.set(uuidv4(), [{removeListeners: stub}]); } - kuzzle.realtime.tokenExpired(); + kuzzle.realtime.removeSubscriptions(); should(kuzzle.realtime._subscriptions).be.empty(); should(stub.callCount).be.eql(10); diff --git a/test/kuzzle/connect.test.js b/test/kuzzle/connect.test.js index 63c784b55..a151ffbd5 100644 --- a/test/kuzzle/connect.test.js +++ b/test/kuzzle/connect.test.js @@ -53,7 +53,7 @@ describe('Kuzzle connect', () => { kuzzle = new Kuzzle(protocols.nowhere), eventStub = sinon.stub(); - kuzzle.realtime.disconnected = sinon.stub(); + kuzzle.realtime.saveSubscriptions = sinon.stub(); kuzzle.addListener('networkError', eventStub); @@ -62,7 +62,7 @@ describe('Kuzzle connect', () => { throw new Error('should not happen'); }) .catch(() => { - should(kuzzle.realtime.disconnected).be.calledOnce(); + should(kuzzle.realtime.saveSubscriptions).be.calledOnce(); should(eventStub).be.calledOnce(); }); }); @@ -85,13 +85,13 @@ describe('Kuzzle connect', () => { kuzzle = new Kuzzle(protocols.somewhereagain), eventStub = sinon.stub(); - kuzzle.realtime.reconnected = sinon.stub(); + kuzzle.realtime.resubscribe = sinon.stub(); kuzzle.addListener('reconnected', eventStub); return kuzzle.connect() .then(() => { - should(kuzzle.realtime.reconnected).be.calledOnce(); + should(kuzzle.realtime.resubscribe).be.calledOnce(); should(eventStub).be.calledOnce(); }); }); @@ -137,14 +137,14 @@ describe('Kuzzle connect', () => { kuzzle = new Kuzzle(protocols.somewhere), eventStub = sinon.stub(); - kuzzle.realtime.disconnected = sinon.stub(); + kuzzle.realtime.saveSubscriptions = sinon.stub(); kuzzle.addListener('disconnected', eventStub); return kuzzle.connect() .then(() => kuzzle.protocol.disconnect()) .then(() => { - should(kuzzle.realtime.disconnected).be.calledOnce(); + should(kuzzle.realtime.saveSubscriptions).be.calledOnce(); should(eventStub).be.calledOnce(); }); });