зеркало из https://github.com/microsoft/etcd3.git
chore: update dependencies, modernize build, drop node 8 support
This commit is contained in:
Родитель
0ffcec9676
Коммит
92f7e69ef6
|
@ -1,11 +0,0 @@
|
|||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
end_of_line = lf
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -0,0 +1,28 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
module.exports = {
|
||||
ignorePatterns: ['**/*.d.ts', '**/test/*.*', '**/*.js'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
extends: ['plugin:@typescript-eslint/recommended'],
|
||||
plugins: ['header'],
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018, // Allows for the parsing of modern ECMAScript features
|
||||
sourceType: 'module', // Allows for the use of imports
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-use-before-define': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'header/header': [
|
||||
'error',
|
||||
'block',
|
||||
'---------------------------------------------------------\n * Copyright (C) Microsoft Corporation. All rights reserved.\n *--------------------------------------------------------',
|
||||
],
|
||||
|
||||
// todo: clean these:
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
},
|
||||
};
|
|
@ -36,4 +36,3 @@ npm-debug.log
|
|||
/lib
|
||||
/.nyc_output
|
||||
/*.etcd/
|
||||
/.vscode
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
/*
|
||||
!/lib
|
||||
/lib/test
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "pwa-node",
|
||||
"request": "launch",
|
||||
"name": "Run Tests",
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"program": "${workspaceFolder}/node_modules/mocha/bin/mocha",
|
||||
"preLaunchTask": "tsc: build - tsconfig.json",
|
||||
"args": [
|
||||
"--timeout",
|
||||
"60000",
|
||||
"--require",
|
||||
"source-map-support/register",
|
||||
"--require",
|
||||
"./lib/test/_setup.js",
|
||||
"\"lib/test/**/*.test.js\""
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"search.exclude": {
|
||||
"**/lib": true,
|
||||
"**/.nyc_output": true,
|
||||
"**/docs": true,
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
*
|
||||
* Usage:
|
||||
*
|
||||
* > node bin/generate-methods proto/rpc.proto > src/methods.ts
|
||||
* > node bin/generate-methods proto/rpc.proto > src/rpc.ts
|
||||
*
|
||||
* protobufjs does have a TypeScript generator but its output isn't very useful
|
||||
* for grpc, much less this client. Rather than reprocessing it, let's just
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
export class <%= name %>Client {
|
||||
constructor(private client: ICallable<any>) {}
|
||||
constructor(private client: ICallable<unknown>) {}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
return this.client
|
||||
.getConnection('<%= service %>')
|
||||
.then(({ resource, client, metadata }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stream = (<any> client).<%= _.lowerFirst(name) %>(metadata, options);
|
||||
stream.on('error', () => this.client.markFailed(resource));
|
||||
return stream;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
return this.client
|
||||
.getConnection('<%= service %>')
|
||||
.then(({ resource, client, metadata }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stream = (<any> client).<%= _.lowerFirst(name) %>(metadata, options, <%= req.empty ? '{}' : 'req' %>);
|
||||
stream.on('error', () => this.client.markFailed(resource));
|
||||
return stream;
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
// AUTOGENERATED CODE, DO NOT EDIT
|
||||
// tslint:disable
|
||||
|
||||
import * as grpc from 'grpc';
|
||||
import { StatusMessage } from './grpcTypes';
|
||||
|
||||
export interface ICallable<T> {
|
||||
exec(
|
||||
exec<T>(
|
||||
service: keyof typeof Services,
|
||||
method: string,
|
||||
params: object,
|
||||
params: unknown,
|
||||
options?: grpc.CallOptions,
|
||||
): Promise<any>;
|
||||
): Promise<T>;
|
||||
|
||||
getConnection(
|
||||
service: keyof typeof Services,
|
||||
|
@ -21,7 +25,7 @@ export interface ICallable<T> {
|
|||
export interface IResponseStream<T> {
|
||||
on(event: 'data', fn: (item: T) => void): this;
|
||||
on(event: 'end', fn: () => void): this;
|
||||
on(event: 'status', fn: (status: grpc.StatusMessage) => void): this;
|
||||
on(event: 'status', fn: (status: StatusMessage) => void): this;
|
||||
on(event: 'error', fn: (err: Error) => void): this;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
## 1.0.0 TBA
|
||||
|
||||
- **breaking**: Node < 10 is no longer supported
|
||||
- **breaking**: `bignumber.js`, used to handle 64-bit numbers returned from etcd, updated from 5.x to 9.0.0
|
||||
- **breaking**: TypeScript is updated to 3.9, and the types of some function signatures have been narrowed
|
||||
|
||||
## 0.2.13 2019-07-03
|
||||
|
||||
- **bug**: fixed comparisons breaking in STM when using namespaces (see [#90](https://github.com/mixer/etcd3/issues/90))
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
version: '3.6'
|
||||
services:
|
||||
etcd:
|
||||
build:
|
||||
context: .
|
||||
ports:
|
||||
- 2379:2379
|
||||
- 2380:2380
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
68
package.json
68
package.json
|
@ -6,18 +6,19 @@
|
|||
"typings": "lib/src/index.d.ts",
|
||||
"scripts": {
|
||||
"test": "npm-run-all --parallel test:lint test:unit",
|
||||
"test:unit": "mocha",
|
||||
"test:unit": "mocha --require source-map-support/register --require ./lib/test/_setup.js \"lib/test/**/*.test.js\" --timeout 10000 --exit",
|
||||
"test:cover": "nyc mocha",
|
||||
"test:lint": "tslint --project tsconfig.json \"{src,test}/**/*.ts\"",
|
||||
"test:lint": "eslint \"src/**/*.ts\"",
|
||||
"watch": "tsc --watch",
|
||||
"build:proto": "node ./bin/update-proto ./proto && node bin/generate-methods.js ./proto/rpc.proto > src/rpc.ts && npm run fmt:ts",
|
||||
"build:doc": "rm -rf docs && typedoc --gitRevision `git describe --abbrev=0 --tags` --exclude \"**/test/*\" --excludePrivate --out ./docs ./src/index.ts && node bin/tame-typedoc",
|
||||
"build:ts": "tsc && cp -R proto lib",
|
||||
"fmt": "prettier --write \"{src,test}/**/*.{ts,js}\" && npm run -s test:lint -- --fix",
|
||||
"build:doc": "rimraf docs && typedoc --gitRevision `git describe --abbrev=0 --tags` --exclude \"**/test/*\" --excludePrivate --out ./docs ./src/index.ts && node bin/tame-typedoc",
|
||||
"build:ts": "tsc",
|
||||
"fmt": "prettier --write \"src/**/*.{ts,js}\" && npm run -s test:lint -- --fix",
|
||||
"prepare": "npm run -s build:ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/mixer/etcd3.git"
|
||||
"url": "git+https://github.com/microsoft/etcd3.git"
|
||||
},
|
||||
"nyc": {
|
||||
"include": [
|
||||
|
@ -47,42 +48,47 @@
|
|||
"author": "Connor Peet <connor@peet.io>",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/mixer/etcd3/issues"
|
||||
"url": "https://github.com/microsoft/etcd3/issues"
|
||||
},
|
||||
"homepage": "https://github.com/mixer/etcd3#readme",
|
||||
"homepage": "https://github.com/microsoft/etcd3#readme",
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.1.7",
|
||||
"@types/chai-as-promised": "^7.1.0",
|
||||
"@types/chai-subset": "^1.3.2",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "^7.0.12",
|
||||
"@types/sinon": "^7.0.13",
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/chai-as-promised": "^7.1.2",
|
||||
"@types/chai-subset": "^1.3.3",
|
||||
"@types/mocha": "^7.0.2",
|
||||
"@types/node": "^14.0.13",
|
||||
"@types/sinon": "^9.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^3.3.0",
|
||||
"@typescript-eslint/parser": "^3.3.0",
|
||||
"chai": "^4.2.0",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-subset": "^1.5.0",
|
||||
"change-case": "^3.1.0",
|
||||
"lodash": "^4.17.11",
|
||||
"mocha": "^6.1.4",
|
||||
"chai-subset": "^1.6.0",
|
||||
"change-case": "^4.1.1",
|
||||
"eslint": "^7.2.0",
|
||||
"eslint-plugin-header": "^3.0.0",
|
||||
"lodash": "^4.17.15",
|
||||
"mocha": "^8.0.1",
|
||||
"ncp": "^2.0.0",
|
||||
"node-fetch": "^2.6.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"nyc": "^14.1.1",
|
||||
"prettier": "^1.18.2",
|
||||
"protobufjs": "^6.8.8",
|
||||
"sinon": "^7.3.2",
|
||||
"ts-node": "^8.3.0",
|
||||
"tslint": "^5.18.0",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"typedoc": "^0.14.2",
|
||||
"typescript": "^3.5.2"
|
||||
"nyc": "^15.1.0",
|
||||
"prettier": "^2.0.5",
|
||||
"protobufjs": "^6.9.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"sinon": "^9.0.2",
|
||||
"ts-node": "^8.10.2",
|
||||
"typedoc": "^0.17.7",
|
||||
"typescript": "^3.9.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.5.1",
|
||||
"bignumber.js": "^5.0.0",
|
||||
"grpc": "^1.21.1"
|
||||
"@grpc/proto-loader": "^0.5.4",
|
||||
"bignumber.js": "^9.0.0",
|
||||
"grpc": "^1.24.3"
|
||||
},
|
||||
"prettier": {
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100
|
||||
"printWidth": 100,
|
||||
"arrowParens": "avoid"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,6 @@ $ docker-compose down
|
|||
|
||||
### Contributing
|
||||
|
||||
Running tests for this module requires running an etcd3 server locally. The tests try to use the default port initially, and you can configure this by setting the `ETCD_ADDR` environment variable, like `export ETCD_ADDR=localhost:12345`.# Contributing
|
||||
Running tests for this module requires running an etcd3 server locally. The tests try to use the default port initially, and you can configure this by setting the `ETCD_ADDR` environment variable, like `export ETCD_ADDR=localhost:12345`.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import * as grpc from 'grpc';
|
||||
|
||||
import { Range } from './range';
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
export interface IBackoffStrategy {
|
||||
/**
|
||||
* getDelay returns the amount of delay of the current backoff.
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { IBackoffStrategy } from './backoff';
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import * as grpc from 'grpc';
|
||||
|
||||
import { Rangable, Range } from './range';
|
||||
|
@ -140,7 +143,7 @@ export class SingleRangeBuilder extends RangeBuilder<string | null> {
|
|||
* Runs the built request and parses the returned key as JSON,
|
||||
* or returns `null` if it isn't found.
|
||||
*/
|
||||
public json(): Promise<object> {
|
||||
public json(): Promise<unknown> {
|
||||
return this.string().then(JSON.parse);
|
||||
}
|
||||
|
||||
|
@ -148,7 +151,7 @@ export class SingleRangeBuilder extends RangeBuilder<string | null> {
|
|||
* Runs the built request and returns the value of the returned key as a
|
||||
* string, or `null` if it isn't found.
|
||||
*/
|
||||
public string(encoding: string = 'utf8'): Promise<string | null> {
|
||||
public string(encoding: BufferEncoding = 'utf8'): Promise<string | null> {
|
||||
return this.exec().then(res =>
|
||||
res.kvs.length === 0 ? null : res.kvs[0].value.toString(encoding),
|
||||
);
|
||||
|
@ -249,7 +252,7 @@ export class MultiRangeBuilder extends RangeBuilder<{ [key: string]: string }> {
|
|||
/**
|
||||
* Keys returns an array of keys matching the query.
|
||||
*/
|
||||
public keys(encoding: string = 'utf8'): Promise<string[]> {
|
||||
public keys(encoding: BufferEncoding = 'utf8'): Promise<string[]> {
|
||||
this.request.keys_only = true;
|
||||
return this.exec().then(res => {
|
||||
return res.kvs.map(kv => kv.key.toString(encoding));
|
||||
|
@ -269,7 +272,7 @@ export class MultiRangeBuilder extends RangeBuilder<{ [key: string]: string }> {
|
|||
/**
|
||||
* Runs the built request and parses the returned keys as JSON.
|
||||
*/
|
||||
public json(): Promise<{ [key: string]: object }> {
|
||||
public json(): Promise<{ [key: string]: unknown }> {
|
||||
return this.mapValues(buf => JSON.parse(buf.toString()));
|
||||
}
|
||||
|
||||
|
@ -277,7 +280,7 @@ export class MultiRangeBuilder extends RangeBuilder<{ [key: string]: string }> {
|
|||
* Runs the built request and returns the value of the returned key as a
|
||||
* string, or `null` if it isn't found.
|
||||
*/
|
||||
public strings(encoding: string = 'utf8'): Promise<{ [key: string]: string }> {
|
||||
public strings(encoding: BufferEncoding = 'utf8'): Promise<{ [key: string]: string }> {
|
||||
return this.mapValues(buf => buf.toString(encoding));
|
||||
}
|
||||
|
||||
|
@ -560,9 +563,9 @@ export class PutBuilder extends PromiseWrap<RPC.IPutResponse> {
|
|||
*/
|
||||
export class ComparatorBuilder {
|
||||
private request: {
|
||||
compare: Array<Promise<RPC.ICompare>>;
|
||||
success: Array<Promise<RPC.IRequestOp>>;
|
||||
failure: Array<Promise<RPC.IRequestOp>>;
|
||||
compare: Promise<RPC.ICompare>[];
|
||||
success: Promise<RPC.IRequestOp>[];
|
||||
failure: Promise<RPC.IRequestOp>[];
|
||||
} = { compare: [], success: [], failure: [] };
|
||||
private callOptions: grpc.CallOptions | undefined;
|
||||
|
||||
|
@ -607,7 +610,7 @@ export class ComparatorBuilder {
|
|||
* Adds one or more consequent clauses to be executed if the comparison
|
||||
* is truthy.
|
||||
*/
|
||||
public then(...clauses: Array<RPC.IRequestOp | IOperation>): this {
|
||||
public then(...clauses: (RPC.IRequestOp | IOperation)[]): this {
|
||||
this.request.success = this.mapOperations(clauses);
|
||||
return this;
|
||||
}
|
||||
|
@ -616,7 +619,7 @@ export class ComparatorBuilder {
|
|||
* Adds one or more consequent clauses to be executed if the comparison
|
||||
* is falsey.
|
||||
*/
|
||||
public else(...clauses: Array<RPC.IRequestOp | IOperation>): this {
|
||||
public else(...clauses: (RPC.IRequestOp | IOperation)[]): this {
|
||||
this.request.failure = this.mapOperations(clauses);
|
||||
return this;
|
||||
}
|
||||
|
@ -638,7 +641,7 @@ export class ComparatorBuilder {
|
|||
/**
|
||||
* Low-level method to add
|
||||
*/
|
||||
public mapOperations(ops: Array<RPC.IRequestOp | IOperation>) {
|
||||
public mapOperations(ops: (RPC.IRequestOp | IOperation)[]): Promise<RPC.IRequestOp>[] {
|
||||
return ops.map(op => {
|
||||
if (typeof (op as IOperation).op === 'function') {
|
||||
return (op as IOperation).op();
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { loadSync } from '@grpc/proto-loader';
|
||||
import * as grpc from 'grpc';
|
||||
|
||||
|
@ -44,7 +47,7 @@ function runServiceCall(
|
|||
metadata: grpc.Metadata,
|
||||
options: grpc.CallOptions | undefined,
|
||||
method: string,
|
||||
payload: object,
|
||||
payload: unknown,
|
||||
): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
(client as any)[method](payload, metadata, options, (err: Error | null, res: any) => {
|
||||
|
@ -213,12 +216,12 @@ export class ConnectionPool implements ICallable<Host> {
|
|||
/**
|
||||
* @override
|
||||
*/
|
||||
public exec(
|
||||
public exec<T>(
|
||||
serviceName: keyof typeof Services,
|
||||
method: string,
|
||||
payload: object,
|
||||
payload: unknown,
|
||||
options?: grpc.CallOptions,
|
||||
): Promise<any> {
|
||||
): Promise<T> {
|
||||
if (this.mockImpl) {
|
||||
return this.mockImpl.exec(serviceName, method, payload);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/**
|
||||
* Thrown when an internal assertion fails.
|
||||
*/
|
||||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
export class ClientRuntimeError extends Error {
|
||||
constructor(message: string) {
|
||||
super(`${message} Please report this error at https://github.com/mixer/etcd3`);
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import * as grpc from 'grpc';
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { Role, User } from './auth';
|
||||
import { ConnectionPool } from './connection-pool';
|
||||
import { Namespace } from './namespace';
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { EventEmitter } from 'events';
|
||||
import * as grpc from 'grpc';
|
||||
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import * as grpc from 'grpc';
|
||||
|
||||
import { ComparatorBuilder, PutBuilder } from './builder';
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import * as grpc from 'grpc';
|
||||
|
||||
import * as Builder from './builder';
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { ChannelOptions } from './grpcTypes';
|
||||
|
||||
import { IBackoffStrategy } from './backoff/backoff';
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { emptyKey, endRangeForPrefix, toBuffer, zeroKey } from './util';
|
||||
|
||||
function compare(a: Buffer, b: Buffer) {
|
||||
|
|
64
src/rpc.ts
64
src/rpc.ts
|
@ -1,16 +1,19 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
|
||||
// AUTOGENERATED CODE, DO NOT EDIT
|
||||
// tslint:disable
|
||||
|
||||
import * as grpc from 'grpc';
|
||||
import { StatusMessage } from './grpcTypes';
|
||||
|
||||
export interface ICallable<T> {
|
||||
exec(
|
||||
exec<T>(
|
||||
service: keyof typeof Services,
|
||||
method: string,
|
||||
params: object,
|
||||
params: unknown,
|
||||
options?: grpc.CallOptions,
|
||||
): Promise<any>;
|
||||
): Promise<T>;
|
||||
|
||||
getConnection(
|
||||
service: keyof typeof Services,
|
||||
|
@ -34,7 +37,7 @@ export interface IRequestStream<T> {
|
|||
|
||||
export interface IDuplexStream<T, R> extends IRequestStream<T>, IResponseStream<R> {}
|
||||
export class KVClient {
|
||||
constructor(private client: ICallable<any>) {}
|
||||
constructor(private client: ICallable<unknown>) {}
|
||||
/**
|
||||
* Range gets the keys in the range from the key-value store.
|
||||
*/
|
||||
|
@ -83,7 +86,7 @@ export class KVClient {
|
|||
}
|
||||
|
||||
export class WatchClient {
|
||||
constructor(private client: ICallable<any>) {}
|
||||
constructor(private client: ICallable<unknown>) {}
|
||||
/**
|
||||
* Watch watches for events happening or that have happened. Both input and output
|
||||
* are streams; the input stream is for creating and canceling watchers and the output
|
||||
|
@ -93,6 +96,7 @@ export class WatchClient {
|
|||
*/
|
||||
public watch(options?: grpc.CallOptions): Promise<IDuplexStream<IWatchRequest, IWatchResponse>> {
|
||||
return this.client.getConnection('Watch').then(({ resource, client, metadata }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stream = (<any>client).watch(metadata, options);
|
||||
stream.on('error', () => this.client.markFailed(resource));
|
||||
return stream;
|
||||
|
@ -101,7 +105,7 @@ export class WatchClient {
|
|||
}
|
||||
|
||||
export class LeaseClient {
|
||||
constructor(private client: ICallable<any>) {}
|
||||
constructor(private client: ICallable<unknown>) {}
|
||||
/**
|
||||
* LeaseGrant creates a lease which expires if the server does not receive a keepAlive
|
||||
* within a given time to live period. All keys attached to the lease will be expired and
|
||||
|
@ -130,6 +134,7 @@ export class LeaseClient {
|
|||
options?: grpc.CallOptions,
|
||||
): Promise<IDuplexStream<ILeaseKeepAliveRequest, ILeaseKeepAliveResponse>> {
|
||||
return this.client.getConnection('Lease').then(({ resource, client, metadata }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stream = (<any>client).leaseKeepAlive(metadata, options);
|
||||
stream.on('error', () => this.client.markFailed(resource));
|
||||
return stream;
|
||||
|
@ -153,7 +158,7 @@ export class LeaseClient {
|
|||
}
|
||||
|
||||
export class ClusterClient {
|
||||
constructor(private client: ICallable<any>) {}
|
||||
constructor(private client: ICallable<unknown>) {}
|
||||
/**
|
||||
* MemberAdd adds a member into the cluster.
|
||||
*/
|
||||
|
@ -190,7 +195,7 @@ export class ClusterClient {
|
|||
}
|
||||
|
||||
export class MaintenanceClient {
|
||||
constructor(private client: ICallable<any>) {}
|
||||
constructor(private client: ICallable<unknown>) {}
|
||||
/**
|
||||
* Alarm activates, deactivates, and queries alarms regarding cluster health.
|
||||
*/
|
||||
|
@ -228,6 +233,7 @@ export class MaintenanceClient {
|
|||
*/
|
||||
public snapshot(options?: grpc.CallOptions): Promise<IResponseStream<ISnapshotResponse>> {
|
||||
return this.client.getConnection('Maintenance').then(({ resource, client, metadata }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const stream = (<any>client).snapshot(metadata, options, {});
|
||||
stream.on('error', () => this.client.markFailed(resource));
|
||||
return stream;
|
||||
|
@ -245,7 +251,7 @@ export class MaintenanceClient {
|
|||
}
|
||||
|
||||
export class AuthClient {
|
||||
constructor(private client: ICallable<any>) {}
|
||||
constructor(private client: ICallable<unknown>) {}
|
||||
/**
|
||||
* AuthEnable enables authentication.
|
||||
*/
|
||||
|
@ -1167,6 +1173,25 @@ export interface IAuthRoleGrantPermissionResponse {
|
|||
export interface IAuthRoleRevokePermissionResponse {
|
||||
header: IResponseHeader;
|
||||
}
|
||||
export interface IUser {
|
||||
name?: Buffer;
|
||||
password?: Buffer;
|
||||
roles?: string[];
|
||||
}
|
||||
export enum Permission {
|
||||
Read = 0,
|
||||
Write = 1,
|
||||
Readwrite = 2,
|
||||
}
|
||||
export interface IPermission {
|
||||
permType: keyof typeof Permission;
|
||||
key: Buffer;
|
||||
range_end: Buffer;
|
||||
}
|
||||
export interface IRole {
|
||||
name?: Buffer;
|
||||
keyPermission?: IPermission[];
|
||||
}
|
||||
export interface IKeyValue {
|
||||
/**
|
||||
* key is the first key for the range. If range_end is not given, the request only looks up key.
|
||||
|
@ -1205,25 +1230,6 @@ export interface IEvent {
|
|||
*/
|
||||
prev_kv: IKeyValue;
|
||||
}
|
||||
export interface IUser {
|
||||
name?: Buffer;
|
||||
password?: Buffer;
|
||||
roles?: string[];
|
||||
}
|
||||
export enum Permission {
|
||||
Read = 0,
|
||||
Write = 1,
|
||||
Readwrite = 2,
|
||||
}
|
||||
export interface IPermission {
|
||||
permType: keyof typeof Permission;
|
||||
key: Buffer;
|
||||
range_end: Buffer;
|
||||
}
|
||||
export interface IRole {
|
||||
name?: Buffer;
|
||||
keyPermission?: IPermission[];
|
||||
}
|
||||
export const Services = {
|
||||
KV: KVClient,
|
||||
Watch: WatchClient,
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { IBackoffStrategy } from './backoff/backoff';
|
||||
import { delay, minBy, sample } from './util';
|
||||
|
||||
|
@ -23,7 +26,7 @@ export class SharedPool<T> {
|
|||
// tests simpler.
|
||||
private static deterministicInsertion: false;
|
||||
|
||||
private resources: Array<IResourceRecord<T>> = [];
|
||||
private resources: IResourceRecord<T>[] = [];
|
||||
private contentionCount = 0;
|
||||
|
||||
public constructor(private strategy: IBackoffStrategy) {}
|
||||
|
|
12
src/stm.ts
12
src/stm.ts
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import BigNumber from 'bignumber.js';
|
||||
import * as grpc from 'grpc';
|
||||
|
||||
|
@ -91,7 +94,7 @@ function keyValueToResponse(key: string | Buffer, value?: Buffer): RPC.IRangeRes
|
|||
*/
|
||||
class ReadSet {
|
||||
private readonly reads: { [key: string]: Promise<RPC.IRangeResponse> } = Object.create(null);
|
||||
private readonly completedReads: Array<{ key: Buffer; res: RPC.IRangeResponse }> = [];
|
||||
private readonly completedReads: { key: Buffer; res: RPC.IRangeResponse }[] = [];
|
||||
private earliestMod = new BigNumber(Infinity);
|
||||
|
||||
/**
|
||||
|
@ -329,7 +332,7 @@ class BasicTransaction {
|
|||
]);
|
||||
}
|
||||
|
||||
protected assertNoOption<T>(req: string, obj: T, keys: Array<keyof T>) {
|
||||
protected assertNoOption<T>(req: string, obj: T, keys: (keyof T)[]) {
|
||||
keys.forEach(key => {
|
||||
if (obj[key] !== undefined) {
|
||||
throw new Error(`"${key}" is not supported in ${req} requests within STM transactions`);
|
||||
|
@ -490,10 +493,7 @@ export class SoftwareTransaction {
|
|||
const cmp = new Builder.ComparatorBuilder(this.rawKV, NSApplicator.default);
|
||||
switch (this.options.isolation) {
|
||||
case Isolation.SerializableSnapshot:
|
||||
const earliestMod = this.tx.readSet
|
||||
.earliestModRevision()
|
||||
.add(1)
|
||||
.toString();
|
||||
const earliestMod = this.tx.readSet.earliestModRevision().plus(1).toString();
|
||||
this.tx.writeSet.addNotChangedChecks(cmp, earliestMod);
|
||||
this.tx.readSet.addCurrentChecks(cmp);
|
||||
break;
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import * as chai from 'chai';
|
||||
|
||||
import { SharedPool } from '../shared-pool';
|
||||
|
||||
chai.use(require('chai-subset')); // tslint:disable-line
|
||||
chai.use(require('chai-as-promised')); // tslint:disable-line
|
||||
|
||||
(SharedPool as any).deterministicInsertion = true;
|
|
@ -1,7 +1,10 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { IBackoffStrategy } from '../src/backoff/backoff';
|
||||
import { ExponentialBackoff } from '../src/backoff/exponential';
|
||||
import { IBackoffStrategy } from '../backoff/backoff';
|
||||
import { ExponentialBackoff } from '../backoff/exponential';
|
||||
|
||||
describe('backoff strategies', () => {
|
||||
describe('exponential strategy', () => {
|
|
@ -1,7 +1,10 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
|
||||
import { Etcd3 } from '../src';
|
||||
import { Etcd3 } from '..';
|
||||
import { createTestClientAndKeys, tearDownTestClient } from './util';
|
||||
|
||||
describe('client', () => {
|
||||
|
@ -12,7 +15,7 @@ describe('client', () => {
|
|||
|
||||
it('allows mocking', async () => {
|
||||
const mock = client.mock({
|
||||
exec: sinon.stub(),
|
||||
exec: sinon.stub() as any,
|
||||
getConnection: sinon.stub(),
|
||||
});
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { GRPCConnectFailedError, IOptions, KVClient } from '../src';
|
||||
import { ConnectionPool } from '../src/connection-pool';
|
||||
import { GRPCConnectFailedError, IOptions, KVClient } from '..';
|
||||
import { ConnectionPool } from '../connection-pool';
|
||||
import { getHost, getOptions } from './util';
|
||||
|
||||
function getOptionsWithBadHost(options: Partial<IOptions> = {}): IOptions {
|
|
@ -1,4 +1,4 @@
|
|||
from quay.io/coreos/etcd:v3.2.13
|
||||
FROM quay.io/coreos/etcd:v3.2.13
|
||||
|
||||
COPY test/certs/certs/etcd0.localhost.crt test/certs/private/etcd0.localhost.key /root/
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
version: '3.6'
|
||||
services:
|
||||
etcd3_2:
|
||||
build:
|
||||
context: ../../../
|
||||
dockerfile: test/containers/3.2/Dockerfile
|
||||
ports:
|
||||
- 2379:2379
|
||||
- 2380:2380
|
|
@ -1,6 +1,9 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { Etcd3 } from '../src';
|
||||
import { Etcd3 } from '..';
|
||||
import { createTestClientAndKeys, tearDownTestClient } from './util';
|
||||
|
||||
describe('crud', () => {
|
||||
|
@ -27,12 +30,7 @@ describe('crud', () => {
|
|||
});
|
||||
|
||||
it('queries prefixes', async () => {
|
||||
expect(
|
||||
await client
|
||||
.getAll()
|
||||
.prefix('fo')
|
||||
.strings(),
|
||||
).to.deep.equal({
|
||||
expect(await client.getAll().prefix('fo').strings()).to.deep.equal({
|
||||
foo1: 'bar1',
|
||||
foo2: 'bar2',
|
||||
foo3: '{"value":"bar3"}',
|
||||
|
@ -55,21 +53,11 @@ describe('crud', () => {
|
|||
|
||||
it('sorts', async () => {
|
||||
expect(
|
||||
await client
|
||||
.getAll()
|
||||
.prefix('foo')
|
||||
.sort('Key', 'Ascend')
|
||||
.limit(2)
|
||||
.keys(),
|
||||
await client.getAll().prefix('foo').sort('Key', 'Ascend').limit(2).keys(),
|
||||
).to.deep.equal(['foo1', 'foo2']);
|
||||
|
||||
expect(
|
||||
await client
|
||||
.getAll()
|
||||
.prefix('foo')
|
||||
.sort('Key', 'Descend')
|
||||
.limit(2)
|
||||
.keys(),
|
||||
await client.getAll().prefix('foo').sort('Key', 'Descend').limit(2).keys(),
|
||||
).to.deep.equal(['foo3', 'foo2']);
|
||||
});
|
||||
});
|
||||
|
@ -86,12 +74,7 @@ describe('crud', () => {
|
|||
});
|
||||
|
||||
it('gets previous', async () => {
|
||||
expect(
|
||||
await client
|
||||
.delete()
|
||||
.key('foo1')
|
||||
.getPrevious(),
|
||||
).to.containSubset([
|
||||
expect(await client.delete().key('foo1').getPrevious()).to.containSubset([
|
||||
{
|
||||
key: new Buffer('foo1'),
|
||||
value: new Buffer('bar1'),
|
||||
|
@ -113,12 +96,7 @@ describe('crud', () => {
|
|||
});
|
||||
|
||||
it('includes previous values', async () => {
|
||||
expect(
|
||||
await client
|
||||
.put('foo1')
|
||||
.value('updated')
|
||||
.getPrevious(),
|
||||
).to.containSubset({
|
||||
expect(await client.put('foo1').value('updated').getPrevious()).to.containSubset({
|
||||
key: new Buffer('foo1'),
|
||||
value: new Buffer('bar1'),
|
||||
});
|
|
@ -1,8 +1,11 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
|
||||
import { Etcd3, EtcdLeaseInvalidError, GRPCConnectFailedError, Lease } from '../src';
|
||||
import { onceEvent } from '../src/util';
|
||||
import { Etcd3, EtcdLeaseInvalidError, GRPCConnectFailedError, Lease } from '..';
|
||||
import { onceEvent } from '../util';
|
||||
import { createTestClientAndKeys, getOptions, proxy, tearDownTestClient } from './util';
|
||||
|
||||
describe('lease()', () => {
|
|
@ -1,6 +1,9 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { Etcd3, EtcdLockFailedError } from '../src';
|
||||
import { Etcd3, EtcdLockFailedError } from '..';
|
||||
import { createTestClientAndKeys, tearDownTestClient } from './util';
|
||||
|
||||
describe('lock()', () => {
|
||||
|
@ -37,10 +40,7 @@ describe('lock()', () => {
|
|||
});
|
||||
|
||||
it('allows setting lock TTL before acquiring', async () => {
|
||||
const lock = await client
|
||||
.lock('resource')
|
||||
.ttl(10)
|
||||
.acquire();
|
||||
const lock = await client.lock('resource').ttl(10).acquire();
|
||||
await lock.release();
|
||||
});
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { Etcd3, Namespace } from '../src';
|
||||
import { Etcd3, Namespace } from '..';
|
||||
import { createTestClientAndKeys, tearDownTestClient } from './util';
|
||||
|
||||
describe('namespacing', () => {
|
||||
|
@ -54,10 +57,7 @@ describe('namespacing', () => {
|
|||
|
||||
it('runs a simple if', async () => {
|
||||
await ns.put('foo1').value('potatoes');
|
||||
await ns
|
||||
.if('foo1', 'Value', '==', 'potatoes')
|
||||
.then(ns.put('foo1').value('tomatoes'))
|
||||
.commit();
|
||||
await ns.if('foo1', 'Value', '==', 'potatoes').then(ns.put('foo1').value('tomatoes')).commit();
|
||||
|
||||
await assertEqualInNamespace('foo1', 'tomatoes');
|
||||
});
|
|
@ -1,5 +1,8 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
import { Range } from '../src/range';
|
||||
import { Range } from '../range';
|
||||
|
||||
describe('Range', () => {
|
||||
describe('prefix', () => {
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
import * as grpc from 'grpc';
|
||||
|
||||
|
@ -11,7 +14,7 @@ import {
|
|||
EtcdUserExistsError,
|
||||
EtcdUserNotFoundError,
|
||||
Role,
|
||||
} from '../src';
|
||||
} from '..';
|
||||
import { createTestClientAndKeys, expectReject, getOptions, tearDownTestClient } from './util';
|
||||
|
||||
function wipeAll(things: Promise<Array<{ delete(): any }>>) {
|
||||
|
@ -186,13 +189,7 @@ describe('roles and auth', () => {
|
|||
}),
|
||||
);
|
||||
|
||||
await expectReject(
|
||||
authedClient
|
||||
.put('wut')
|
||||
.value('bar')
|
||||
.exec(),
|
||||
EtcdPermissionDeniedError,
|
||||
);
|
||||
await expectReject(authedClient.put('wut').value('bar').exec(), EtcdPermissionDeniedError);
|
||||
|
||||
authedClient.close();
|
||||
});
|
||||
|
@ -208,10 +205,7 @@ describe('roles and auth', () => {
|
|||
);
|
||||
|
||||
await expectReject(
|
||||
authedClient
|
||||
.put('foo')
|
||||
.value('bar')
|
||||
.exec(),
|
||||
authedClient.put('foo').value('bar').exec(),
|
||||
EtcdAuthenticationFailedError,
|
||||
);
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
|
||||
import { ExponentialBackoff } from '../src/backoff/exponential';
|
||||
import { SharedPool } from '../src/shared-pool';
|
||||
import { ExponentialBackoff } from '../backoff/exponential';
|
||||
import { SharedPool } from '../shared-pool';
|
||||
|
||||
describe('shared pool', () => {
|
||||
let pool: SharedPool<number>;
|
||||
|
@ -24,7 +27,7 @@ describe('shared pool', () => {
|
|||
|
||||
afterEach(() => clock.restore());
|
||||
|
||||
async function getAll(count: number = 3): Promise<number[]> {
|
||||
async function getAll(count = 3): Promise<number[]> {
|
||||
const output: number[] = [];
|
||||
for (let i = 0; i < count; i += 1) {
|
||||
clock.tick(1);
|
|
@ -1,7 +1,10 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
import { Isolation, SoftwareTransaction } from '../src/stm';
|
||||
import { Isolation, SoftwareTransaction } from '../stm';
|
||||
|
||||
import { Etcd3, Namespace, STMConflictError } from '../src';
|
||||
import { Etcd3, Namespace, STMConflictError } from '..';
|
||||
import { createTestClient, createTestKeys, tearDownTestClient } from './util';
|
||||
|
||||
describe('stm()', () => {
|
||||
|
@ -34,7 +37,7 @@ describe('stm()', () => {
|
|||
const expectRetry = async (
|
||||
isolation: Isolation,
|
||||
fn: (tx: SoftwareTransaction, tries: number) => Promise<any>,
|
||||
retries: number = 2,
|
||||
retries = 2,
|
||||
) => {
|
||||
let tries = 0;
|
||||
await ns.stm({ isolation }).transact(async tx => fn(tx, ++tries));
|
|
@ -1,6 +1,9 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { Etcd3 } from '../src';
|
||||
import { Etcd3 } from '..';
|
||||
import { createTestClientAndKeys, tearDownTestClient } from './util';
|
||||
|
||||
describe('transactions', () => {
|
||||
|
@ -10,10 +13,7 @@ describe('transactions', () => {
|
|||
afterEach(async () => await tearDownTestClient(client));
|
||||
|
||||
it('runs a simple if', async () => {
|
||||
await client
|
||||
.if('foo1', 'Value', '==', 'bar1')
|
||||
.then(client.put('foo1').value('bar2'))
|
||||
.commit();
|
||||
await client.if('foo1', 'Value', '==', 'bar1').then(client.put('foo1').value('bar2')).commit();
|
||||
|
||||
expect(await client.get('foo1').string()).to.equal('bar2');
|
||||
});
|
|
@ -1,12 +1,18 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { expect } from 'chai';
|
||||
import * as fs from 'fs';
|
||||
import * as tls from 'tls';
|
||||
|
||||
import { Etcd3, IOptions, Namespace } from '../src';
|
||||
import { Etcd3, IOptions, Namespace } from '..';
|
||||
import { AddressInfo } from 'net';
|
||||
import { resolve } from 'path';
|
||||
|
||||
const rootCertificate = fs.readFileSync(`${__dirname}/certs/certs/ca.crt`);
|
||||
const tlsCert = fs.readFileSync(`${__dirname}/certs/certs/etcd0.localhost.crt`);
|
||||
const tlsKey = fs.readFileSync(`${__dirname}/certs/private/etcd0.localhost.key`);
|
||||
const rootPath = resolve(__dirname, '..', '..');
|
||||
const rootCertificate = fs.readFileSync(`${rootPath}/src/test/certs/certs/ca.crt`);
|
||||
const tlsCert = fs.readFileSync(`${rootPath}/src/test/certs/certs/etcd0.localhost.crt`);
|
||||
const tlsKey = fs.readFileSync(`${rootPath}/src/test/certs/private/etcd0.localhost.key`);
|
||||
const etcdSourceAddress = process.env.ETCD_ADDR || '127.0.0.1:2379';
|
||||
const [etcdSourceHost, etcdSourcePort] = etcdSourceAddress.split(':');
|
||||
|
||||
|
@ -34,7 +40,7 @@ export class Proxy {
|
|||
|
||||
this.server.listen(0, '127.0.0.1');
|
||||
this.server.on('listening', () => {
|
||||
const addr = this.server.address();
|
||||
const addr = this.server.address() as AddressInfo;
|
||||
this.host = addr.address;
|
||||
this.port = addr.port;
|
||||
this.isActive = true;
|
||||
|
@ -82,7 +88,7 @@ export class Proxy {
|
|||
let serverConnected = false;
|
||||
const serverBuffer: Buffer[] = [];
|
||||
const serverCnx = tls.connect(
|
||||
etcdSourcePort,
|
||||
Number(etcdSourcePort),
|
||||
etcdSourceHost,
|
||||
{
|
||||
secureContext: tls.createSecureContext({ ca: rootCertificate }),
|
|
@ -1,8 +1,11 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { Etcd3, IKeyValue, IWatchResponse, Watcher } from '../src';
|
||||
import { onceEvent } from '../src/util';
|
||||
import { Etcd3, IKeyValue, IWatchResponse, Watcher } from '..';
|
||||
import { onceEvent } from '../util';
|
||||
import { createTestClientAndKeys, getOptions, proxy, tearDownTestClient } from './util';
|
||||
|
||||
describe('watch()', () => {
|
||||
|
@ -57,10 +60,7 @@ describe('watch()', () => {
|
|||
await proxy.activate();
|
||||
const proxiedClient = await createTestClientAndKeys();
|
||||
|
||||
const watcher = await proxiedClient
|
||||
.watch()
|
||||
.key('foo1')
|
||||
.create();
|
||||
const watcher = await proxiedClient.watch().key('foo1').create();
|
||||
|
||||
proxy.pause();
|
||||
await onceEvent(watcher, 'disconnected');
|
||||
|
@ -78,16 +78,13 @@ describe('watch()', () => {
|
|||
await proxy.activate();
|
||||
const proxiedClient = await createTestClientAndKeys();
|
||||
|
||||
const watcher = await proxiedClient
|
||||
.watch()
|
||||
.key('foo1')
|
||||
.create();
|
||||
const watcher = await proxiedClient.watch().key('foo1').create();
|
||||
|
||||
await Promise.all([
|
||||
client.put('foo1').value('update 1'),
|
||||
onceEvent(watcher, 'data').then((res: IWatchResponse) => {
|
||||
expect(watcher.request.start_revision).to.equal(
|
||||
new BigNumber(res.header.revision).add(1).toString(),
|
||||
new BigNumber(res.header.revision).plus(1).toString(),
|
||||
);
|
||||
}),
|
||||
]);
|
||||
|
@ -108,10 +105,7 @@ describe('watch()', () => {
|
|||
await proxy.activate();
|
||||
const proxiedClient = await createTestClientAndKeys();
|
||||
|
||||
const watcher = await proxiedClient
|
||||
.watch()
|
||||
.key('foo1')
|
||||
.create();
|
||||
const watcher = await proxiedClient.watch().key('foo1').create();
|
||||
proxy.pause();
|
||||
await onceEvent(watcher, 'disconnected');
|
||||
const actualRevision = Number(watcher.request.start_revision);
|
||||
|
@ -124,23 +118,14 @@ describe('watch()', () => {
|
|||
|
||||
describe('subscription', () => {
|
||||
it('subscribes before the connection is established', async () => {
|
||||
const watcher = await client
|
||||
.watch()
|
||||
.key('foo1')
|
||||
.create();
|
||||
const watcher = await client.watch().key('foo1').create();
|
||||
await expectWatching(watcher, 'foo1');
|
||||
expect(getWatchers()).to.deep.equal([watcher]);
|
||||
});
|
||||
|
||||
it('subscribes while the connection is still being established', async () => {
|
||||
const watcher1 = client
|
||||
.watch()
|
||||
.key('foo1')
|
||||
.create();
|
||||
const watcher2 = client
|
||||
.watch()
|
||||
.key('bar')
|
||||
.create();
|
||||
const watcher1 = client.watch().key('foo1').create();
|
||||
const watcher2 = client.watch().key('bar').create();
|
||||
|
||||
const watchers = await Promise.all([
|
||||
watcher1.then(w => expectWatching(w, 'foo1')),
|
||||
|
@ -151,14 +136,8 @@ describe('watch()', () => {
|
|||
});
|
||||
|
||||
it('subscribes in series', async () => {
|
||||
const watcher1 = client
|
||||
.watch()
|
||||
.key('foo1')
|
||||
.watcher();
|
||||
const watcher2 = client
|
||||
.watch()
|
||||
.key('bar')
|
||||
.watcher();
|
||||
const watcher1 = client.watch().key('foo1').watcher();
|
||||
const watcher2 = client.watch().key('bar').watcher();
|
||||
const events: string[] = [];
|
||||
|
||||
watcher1.on('connecting', () => events.push('connecting1'));
|
||||
|
@ -172,31 +151,19 @@ describe('watch()', () => {
|
|||
});
|
||||
|
||||
it('subscribes after the connection is fully established', async () => {
|
||||
const watcher1 = await client
|
||||
.watch()
|
||||
.key('foo1')
|
||||
.create();
|
||||
const watcher1 = await client.watch().key('foo1').create();
|
||||
await expectWatching(watcher1, 'foo1');
|
||||
const watcher2 = await client
|
||||
.watch()
|
||||
.key('bar')
|
||||
.create();
|
||||
const watcher2 = await client.watch().key('bar').create();
|
||||
await expectWatching(watcher2, 'bar');
|
||||
expect(getWatchers()).to.deep.equal([watcher1, watcher2]);
|
||||
});
|
||||
|
||||
it('allows successive resubscription (issue #51)', async () => {
|
||||
const watcher1 = await client
|
||||
.watch()
|
||||
.key('foo1')
|
||||
.create();
|
||||
const watcher1 = await client.watch().key('foo1').create();
|
||||
await expectWatching(watcher1, 'foo1');
|
||||
await watcher1.cancel();
|
||||
|
||||
const watcher2 = await client
|
||||
.watch()
|
||||
.key('foo1')
|
||||
.create();
|
||||
const watcher2 = await client.watch().key('foo1').create();
|
||||
await expectWatching(watcher2, 'foo1');
|
||||
await watcher2.cancel();
|
||||
});
|
||||
|
@ -204,10 +171,7 @@ describe('watch()', () => {
|
|||
|
||||
describe('unsubscribing', () => {
|
||||
it('unsubscribes while the connection is established', async () => {
|
||||
const watcher = await client
|
||||
.watch()
|
||||
.key('foo1')
|
||||
.create();
|
||||
const watcher = await client.watch().key('foo1').create();
|
||||
await watcher.cancel();
|
||||
await expectNotWatching(watcher, 'foo1');
|
||||
expect(getWatchers()).to.deep.equal([]);
|
||||
|
@ -217,10 +181,7 @@ describe('watch()', () => {
|
|||
await proxy.activate();
|
||||
const proxiedClient = await createTestClientAndKeys();
|
||||
|
||||
const watcher = await proxiedClient
|
||||
.watch()
|
||||
.key('foo1')
|
||||
.create();
|
||||
const watcher = await proxiedClient.watch().key('foo1').create();
|
||||
proxy.pause();
|
||||
await watcher.cancel();
|
||||
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
import { ClientRuntimeError } from './errors';
|
||||
|
@ -155,7 +158,7 @@ export function forOwn<T>(
|
|||
obj: T,
|
||||
iterator: <K extends keyof T>(value: T[K], key: K) => void,
|
||||
): void {
|
||||
const keys = Object.keys(obj) as Array<keyof T>;
|
||||
const keys = Object.keys(obj) as (keyof T)[];
|
||||
for (const key of keys) {
|
||||
iterator(obj[key], key);
|
||||
}
|
||||
|
@ -167,7 +170,7 @@ export function forOwn<T>(
|
|||
*/
|
||||
export function onceEvent(emitter: EventEmitter, ...events: string[]): Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const teardown: Array<() => void> = [];
|
||||
const teardown: (() => void)[] = [];
|
||||
|
||||
const handler = (data: any, event: string) => {
|
||||
teardown.forEach(t => t());
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
/*---------------------------------------------------------
|
||||
* Copyright (C) Microsoft Corporation. All rights reserved.
|
||||
*--------------------------------------------------------*/
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
|
@ -381,7 +384,7 @@ export class WatchBuilder {
|
|||
/**
|
||||
* ignore omits certain operation kinds from the watch stream.
|
||||
*/
|
||||
public ignore(...operations: Array<keyof typeof operationNames>): this {
|
||||
public ignore(...operations: (keyof typeof operationNames)[]): this {
|
||||
this.request.filters = operations.map(op => operationNames[op]);
|
||||
return this;
|
||||
}
|
||||
|
@ -531,6 +534,6 @@ export class Watcher extends EventEmitter {
|
|||
* Updates the current revision based on the revision in the watch header.
|
||||
*/
|
||||
private updateRevision(req: RPC.IWatchResponse) {
|
||||
this.request.start_revision = new BigNumber(req.header.revision).add(1).toString();
|
||||
this.request.start_revision = new BigNumber(req.header.revision).plus(1).toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import * as chai from 'chai';
|
||||
|
||||
import { SharedPool } from '../src/shared-pool';
|
||||
|
||||
chai.use(require('chai-subset')); // tslint:disable-line
|
||||
chai.use(require('chai-as-promised')); // tslint:disable-line
|
||||
|
||||
(SharedPool as any).deterministicInsertion = true;
|
|
@ -1,8 +0,0 @@
|
|||
--watch-extensions ts
|
||||
--require source-map-support/register
|
||||
--require ts-node/register
|
||||
--require test/_setup.ts
|
||||
--timeout 20000
|
||||
--recursive
|
||||
--exit
|
||||
test/**/*.test.ts
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
|
@ -9,15 +10,9 @@
|
|||
"strictNullChecks": true,
|
||||
"experimentalDecorators": true,
|
||||
"module": "commonjs",
|
||||
"target": "es6",
|
||||
"target": "ES2018",
|
||||
"outDir": "lib",
|
||||
"lib": [
|
||||
"es5",
|
||||
"es6"
|
||||
],
|
||||
"typeRoots": [
|
||||
"node_modules/@types"
|
||||
],
|
||||
"lib": ["ES2018"],
|
||||
"types": [
|
||||
"chai-as-promised",
|
||||
"chai-subset",
|
||||
|
@ -25,14 +20,6 @@
|
|||
"node"
|
||||
],
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": [
|
||||
"src/types/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
"lib"
|
||||
]
|
||||
"include": ["src"],
|
||||
}
|
||||
|
|
10
tslint.json
10
tslint.json
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/tslint",
|
||||
"extends": ["tslint:recommended", "tslint-config-prettier"],
|
||||
"rules": {
|
||||
"object-literal-sort-keys": false,
|
||||
"unified-signatures": false,
|
||||
"max-classes-per-file": false,
|
||||
"variable-name": false
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче