Skip to content

Commit

Permalink
feat: allow passing a set of default CallOptions in new Etcd3()
Browse files Browse the repository at this point in the history
  • Loading branch information
connor4312 committed Sep 19, 2020
1 parent 66b1769 commit 5f973eb
Show file tree
Hide file tree
Showing 21 changed files with 1,328 additions and 742 deletions.
14 changes: 5 additions & 9 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,13 @@
"type": "pwa-node",
"request": "launch",
"name": "Run Tests",
"trace": true,
"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\""
"args": ["--timeout", "10000", "--require", "./lib/test/_setup.js", "lib/test/**/*.test.js"],
"outFiles": [
"${workspaceFolder}/**/*.js",
"!**/node_modules/**"
]
}
]
Expand Down
10 changes: 6 additions & 4 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
{
"typescript.tsdk": "node_modules\\typescript\\lib",
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"search.exclude": {
"**/lib": true,
"**/.nyc_output": true,
"**/docs": true,
"**/docs": true
},
"files.exclude": {
"**/.nyc_output": true,
},
"debug.node.autoAttach": "off"
"**/.nyc_output": true
}
}
15 changes: 15 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "typescript",
"tsconfig": "tsconfig.json",
"option": "watch",
"problemMatcher": [
"$tsc-watch"
],
"group": "build",
"label": "tsc: watch - tsconfig.json"
}
]
}
37 changes: 34 additions & 3 deletions bin/generate-methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
* create the output ourselves since it's pretty simple (~100 lines of code).
*/

const prettier = require('prettier');
const pbjs = require('protobufjs');
const fs = require('fs');
const _ = require('lodash');
Expand Down Expand Up @@ -66,14 +67,26 @@ class MessageCollection {

const messages = new MessageCollection();

let result = '';

function emit(string) {
if (string) {
process.stdout.write(string.replace(/\n\n+/g, '\n\n'));
result += string;
}

return emit;
}

function writeOut() {
fs.writeFileSync(
`${__dirname}/../src/rpc.ts`,
prettier.format(result, {
...require('../package.json').prettier,
parser: 'typescript',
}),
);
}

function template(name, params) {
if (!templates[name]) {
templates[name] = _.template(fs.readFileSync(`${__dirname}/template/${name}.tmpl`, 'utf8'));
Expand All @@ -89,7 +102,7 @@ function template(name, params) {
emit(
templates[name](params)
.replace(/^\-\- *\n/gm, '')
.replace(/^\-\-/gm, '')
.replace(/^\-\-/gm, ''),
);
}

Expand Down Expand Up @@ -169,7 +182,7 @@ function getCommentPrefixing(substring, from = 0, indentation = 1) {
}

function generateMethodCalls(node, name) {
services[name] = `${name}Client`;
const service = (services[name] = { cls: `${name}Client`, methods: new Map() });
template('class-header', { name });

_.forOwn(node.methods, (method, mname) => {
Expand All @@ -183,8 +196,12 @@ function generateMethodCalls(node, name) {
res,
responseTsType: res.empty ? 'void' : formatType(method.responseType),
service: name,
responseStream: method.responseStream,
requestStream: method.requestStream,
};

service.methods.set(method, params);

if (method.responseStream && !method.requestStream) {
template('response-stream-method', params);
} else if (method.responseStream && method.requestStream) {
Expand All @@ -199,6 +216,18 @@ function generateMethodCalls(node, name) {
emit('}\n\n');
}

function generateCallContext() {
emit('export type CallContext = \n');

for (const service of Object.values(services)) {
for (const params of service.methods.values()) {
template('call-context', params);
}
}

emit(';\n');
}

function generateInterface(node, name) {
const message = messages.find(name);
template('interface', { name, node, message });
Expand Down Expand Up @@ -276,6 +305,7 @@ function codeGen(ast) {
});

template('service-map', { services });
generateCallContext();
}

new pbjs.Root()
Expand All @@ -284,5 +314,6 @@ new pbjs.Root()
prepareForGeneration(ast.nested);
template('rpc-prefix');
codeGen(ast.nested);
writeOut();
})
.catch(err => console.error(err.stack));
1 change: 1 addition & 0 deletions bin/template/call-context.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
| { service: '<%= service %>', method: '<%= _.lowerFirst(name) %>', isStream: <%= !!(responseStream || requestStream) %>, <%= req.empty || requestStream ? '' : `params: ${requestTsType}` %> }
2 changes: 1 addition & 1 deletion bin/template/class-header.tmpl
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export class <%= name %>Client {
constructor(private client: ICallable<unknown>) {}
constructor(private readonly client: ICallable<unknown>) {}
8 changes: 7 additions & 1 deletion bin/template/duplex-stream-method.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,13 @@
public <%= _.lowerFirst(name) %>(options?: grpc.CallOptions): Promise<IDuplexStream<<%= requestTsType %>, <%= responseTsType %>>> {
return this.client.withConnection('<%= service %>', ({ resource, client, metadata }) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const stream = (<any> client).<%= _.lowerFirst(name) %>(metadata, options);
const resolved = resolveCallOptions(options, this.client.callOptionsFactory, {
service: '<%= service %>',
method: '<%= _.lowerFirst(name) %>',
isStream: true,
});

const stream = (<any> client).<%= _.lowerFirst(name) %>(metadata, resolved);
stream.on('error', (err: Error) => stream.writable && this.client.markFailed(resource, err));
return stream;
});
Expand Down
7 changes: 6 additions & 1 deletion bin/template/response-stream-method.tmpl
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
--<%= getCommentPrefixing(`rpc ${name}(`) %>
public <%= _.lowerFirst(name) %>(<%= req.empty ? '' : `req: ${requestTsType}, ` %>options?: grpc.CallOptions): Promise<IResponseStream<<%= responseTsType %>>> {
return this.client.withConnection('<%= service %>', ({ resource, client, metadata }) => {
const resolved = resolveCallOptions(options, this.client.callOptionsFactory, {
service: '<%= service %>',
method: '<%= _.lowerFirst(name) %>', <%= req.empty ? '' : 'params: req, ' %> isStream: true,
});

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const stream = (<any> client).<%= _.lowerFirst(name) %>(metadata, options, <%= req.empty ? '{}' : 'req' %>);
const stream = (<any> client).<%= _.lowerFirst(name) %>(metadata, resolved, <%= req.empty ? '{}' : 'req' %>);
stream.on('error', (err: Error) => this.client.markFailed(resource, err));
return stream;
});
Expand Down
4 changes: 4 additions & 0 deletions bin/template/rpc-prefix.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
/* eslint-disable @typescript-eslint/no-empty-interface */

import * as grpc from '@grpc/grpc-js';
import type { CallOptionsFactory } from './options';
import { resolveCallOptions } from './util';

export interface ICallable<T> {
exec<T>(
Expand All @@ -22,6 +24,8 @@ export interface ICallable<T> {
): Promise<R>;

markFailed(resource: T, error: Error): void;

readonly callOptionsFactory: CallOptionsFactory | undefined;
}

export interface IResponseStream<T> {
Expand Down
2 changes: 1 addition & 1 deletion bin/template/service-map.tmpl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export const Services = {
--<% _.forOwn(services, (clientName, serviceName) => { %>
<%= serviceName %>: <%= clientName %>,
<%= serviceName %>: <%= clientName.cls %>,
--<% }) %>
};
26 changes: 26 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,32 @@

- **fix:** update version of cockatiel to fix incompatible TypeScript types (see [#128](https://github.com/microsoft/etcd3/issues/128))
- **fix:** don't include the deadline in inherited lease call options (see [#131](https://github.com/microsoft/etcd3/issues/131))
- **feat:** allow passing a set of default CallOptions in new Etcd3() (see [#133](https://github.com/microsoft/etcd3/issues/133))

When constructing `Etcd3`, you can now pass `defaultCallOptions`. This can be an object, or a function which will be called for each etcd method call and should return an object. As a function, it will be called with a context object, which looks like:

```js
{
service: 'KV', // etcd service name
method: 'range', // etcd method name
isStream: false, // whether the call create a stream
params: { ... }, // arguments given to the call
}
```

For example, this will set a 10 second timeout on all calls which are not streams:

```js
const etcd3 = new Etcd3({
defaultCallOptions: context => context.isStream ? {} : Date.now() + 10000,
});
```

The default options are shallow merged with any call-specific options. For example this will always result in a 5 second timeout, regardless of what the `defaultCallOptions` contains:

```js
etcd3.get('foo').options({ deadline: Date.now() + 5000 })
```

## 1.0.1 2020-06-21

Expand Down
Loading

0 comments on commit 5f973eb

Please sign in to comment.