Skip to content

Commit

Permalink
feat: kv list keys (#223)
Browse files Browse the repository at this point in the history
* implement keys method on kv stores

* fix: don't log grpc error metadata

* chore: update nitric version
---------

Co-authored-by: Jye Cusch <[email protected]>
  • Loading branch information
davemooreuws and jyecusch committed Mar 4, 2024
1 parent ac48c7f commit 8b607a2
Show file tree
Hide file tree
Showing 15 changed files with 2,451 additions and 44 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@nitric/sdk",
"description": "Nitric NodeJS client sdk",
"nitric": "v1.0.0",
"nitric": "v1.1.0",
"author": "Nitric <https://github.com/nitrictech>",
"repository": "https://github.com/nitrictech/node-sdk",
"main": "lib/index.js",
Expand Down
3 changes: 1 addition & 2 deletions src/api/errors/provider-error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ export class NitricProviderError extends Error {
Nitric Provider Error: ${grpcError.name}
Code: ${grpcError.code}
Message: ${grpcError.message}
Details: ${details}
Metadata: ${JSON.stringify(grpcError.metadata)}`;
Details: ${details}`;
super(message);
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/api/keyvalue/v1/keyvalue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { SERVICE_BIND } from '../../../constants';
import { KeyValueClient } from '@nitric/proto/keyvalue/v1/keyvalue_grpc_pb';
import { KvStoreClient } from '@nitric/proto/kvstore/v1/kvstore_grpc_pb';
import * as grpc from '@grpc/grpc-js';
import { StoreRef, ValueStructure } from './store';

Expand All @@ -23,10 +23,10 @@ import { StoreRef, ValueStructure } from './store';
* Used to create references to key/value stores.
*/
export class KeyValue {
private kvClient: KeyValueClient;
private kvClient: KvStoreClient;

constructor() {
this.kvClient = new KeyValueClient(
this.kvClient = new KvStoreClient(
SERVICE_BIND,
grpc.ChannelCredentials.createInsecure()
);
Expand Down
89 changes: 66 additions & 23 deletions src/api/keyvalue/v1/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import {
KeyValueDeleteRequest,
KeyValueGetRequest,
KeyValueGetResponse,
KeyValueSetRequest,
KvStoreDeleteKeyRequest,
KvStoreGetValueRequest,
KvStoreGetValueResponse,
KvStoreScanKeysRequest,
KvStoreScanKeysResponse,
KvStoreSetValueRequest,
Store,
ValueRef,
} from '@nitric/proto/keyvalue/v1/keyvalue_pb';
import { KeyValueClient } from '@nitric/proto/keyvalue/v1/keyvalue_grpc_pb';
} from '@nitric/proto/kvstore/v1/kvstore_pb';
import { KvStoreClient } from '@nitric/proto/kvstore/v1/kvstore_grpc_pb';
import { fromGrpcError } from '../../errors';
import { Struct } from 'google-protobuf/google/protobuf/struct_pb';
import { Transform } from 'stream';
import { ServiceError } from '@grpc/grpc-js';

export type ValueStructure = Record<string, any>;

Expand All @@ -30,10 +35,10 @@ export type ValueStructure = Record<string, any>;
* Provides a KeyValue API StoreRef class.
*/
export class StoreRef<T extends ValueStructure> {
private kvClient: KeyValueClient;
private kvClient: KvStoreClient;
public readonly name: string;

constructor(kvClient: KeyValueClient, name: string) {
constructor(kvClient: KvStoreClient, name: string) {
this.kvClient = kvClient;
this.name = name;
}
Expand All @@ -46,25 +51,28 @@ export class StoreRef<T extends ValueStructure> {
* @returns the value or null if not found
*/
public async get(key: string): Promise<T> {
const request = new KeyValueGetRequest();
const request = new KvStoreGetValueRequest();
const ref = new ValueRef();
ref.setStore(this.name);
ref.setKey(key);
request.setRef(ref);

return new Promise<T>((resolve, reject) => {
this.kvClient.get(request, (error, response: KeyValueGetResponse) => {
if (error) {
reject(fromGrpcError(error));
} else if (response.hasValue()) {
const value = response.getValue();
const content = value.getContent().toJavaScript() as T;
this.kvClient.getValue(
request,
(error, response: KvStoreGetValueResponse) => {
if (error) {
reject(fromGrpcError(error));
} else if (response.hasValue()) {
const value = response.getValue();
const content = value.getContent().toJavaScript() as T;

resolve(content);
} else {
resolve(null);
resolve(content);
} else {
resolve(null);
}
}
});
);
});
}

Expand All @@ -77,7 +85,7 @@ export class StoreRef<T extends ValueStructure> {
* @returns void
*/
public async set(key: string, value: T): Promise<void> {
const request = new KeyValueSetRequest();
const request = new KvStoreSetValueRequest();
const ref = new ValueRef();
ref.setStore(this.name);
ref.setKey(key);
Expand All @@ -86,7 +94,7 @@ export class StoreRef<T extends ValueStructure> {
request.setContent(content);

return new Promise<void>((resolve, reject) => {
this.kvClient.set(request, (error) => {
this.kvClient.setValue(request, (error) => {
if (error) {
reject(fromGrpcError(error));
} else {
Expand All @@ -104,14 +112,14 @@ export class StoreRef<T extends ValueStructure> {
* @returns void
*/
public async delete(key: string): Promise<void> {
const request = new KeyValueDeleteRequest();
const request = new KvStoreDeleteKeyRequest();
const ref = new ValueRef();
ref.setStore(this.name);
ref.setKey(key);
request.setRef(ref);

return new Promise<void>((resolve, reject) => {
this.kvClient.delete(request, (error) => {
this.kvClient.deleteKey(request, (error) => {
if (error) {
reject(fromGrpcError(error));
} else {
Expand All @@ -120,4 +128,39 @@ export class StoreRef<T extends ValueStructure> {
});
});
}

/**
* Return an async iterable of keys in the store
*
* @param prefix The prefix to filter keys by, if not provided all keys will be returned
* @returns an async iterable of keys
*/
public keys(prefix = ''): AsyncIterable<string> {
const store = new Store();
store.setName(this.name);
const request = new KvStoreScanKeysRequest();
request.setStore(store);
request.setPrefix(prefix);

const respStream = this.kvClient.scanKeys(request);

const transform = new Transform({
objectMode: true,
transform(result: KvStoreScanKeysResponse, _, callback) {
callback(null, result.getKey());
},
});

respStream.on('error', (e) => {
transform.destroy(fromGrpcError(e as ServiceError));
});
respStream.pipe(transform);

const iterator = transform[Symbol.asyncIterator]();
return {
[Symbol.asyncIterator]() {
return iterator;
},
} as any;
}
}
2 changes: 1 addition & 1 deletion src/context/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class WebsocketNotificationContext<T> extends BaseContext<
message ? message.getBody() : '',
request.getSocketName(),
request.getWebsocketEventCase(),
request.getConnectionid(),
request.getConnectionId(),
query
);

Expand Down
2 changes: 1 addition & 1 deletion src/gen/nitric/proto/deployments/v1/deployments_pb.js
Original file line number Diff line number Diff line change
Expand Up @@ -1627,7 +1627,7 @@ proto.nitric.proto.deployments.v1.UpResult.prototype.setSuccess = function(value


/**
* optional string Text = 2;
* optional string text = 2;
* @return {string}
*/
proto.nitric.proto.deployments.v1.UpResult.prototype.getText = function() {
Expand Down
38 changes: 38 additions & 0 deletions src/gen/nitric/proto/kvstore/v1/kvstore_grpc_pb.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// GENERATED CODE -- DO NOT EDIT!

// package: nitric.proto.kvstore.v1
// file: nitric/proto/kvstore/v1/kvstore.proto

import * as nitric_proto_kvstore_v1_kvstore_pb from "../../../../nitric/proto/kvstore/v1/kvstore_pb";
import * as grpc from "@grpc/grpc-js";

interface IKvStoreService extends grpc.ServiceDefinition<grpc.UntypedServiceImplementation> {
getValue: grpc.MethodDefinition<nitric_proto_kvstore_v1_kvstore_pb.KvStoreGetValueRequest, nitric_proto_kvstore_v1_kvstore_pb.KvStoreGetValueResponse>;
setValue: grpc.MethodDefinition<nitric_proto_kvstore_v1_kvstore_pb.KvStoreSetValueRequest, nitric_proto_kvstore_v1_kvstore_pb.KvStoreSetValueResponse>;
deleteKey: grpc.MethodDefinition<nitric_proto_kvstore_v1_kvstore_pb.KvStoreDeleteKeyRequest, nitric_proto_kvstore_v1_kvstore_pb.KvStoreDeleteKeyResponse>;
scanKeys: grpc.MethodDefinition<nitric_proto_kvstore_v1_kvstore_pb.KvStoreScanKeysRequest, nitric_proto_kvstore_v1_kvstore_pb.KvStoreScanKeysResponse>;
}

export const KvStoreService: IKvStoreService;

export interface IKvStoreServer extends grpc.UntypedServiceImplementation {
getValue: grpc.handleUnaryCall<nitric_proto_kvstore_v1_kvstore_pb.KvStoreGetValueRequest, nitric_proto_kvstore_v1_kvstore_pb.KvStoreGetValueResponse>;
setValue: grpc.handleUnaryCall<nitric_proto_kvstore_v1_kvstore_pb.KvStoreSetValueRequest, nitric_proto_kvstore_v1_kvstore_pb.KvStoreSetValueResponse>;
deleteKey: grpc.handleUnaryCall<nitric_proto_kvstore_v1_kvstore_pb.KvStoreDeleteKeyRequest, nitric_proto_kvstore_v1_kvstore_pb.KvStoreDeleteKeyResponse>;
scanKeys: grpc.handleServerStreamingCall<nitric_proto_kvstore_v1_kvstore_pb.KvStoreScanKeysRequest, nitric_proto_kvstore_v1_kvstore_pb.KvStoreScanKeysResponse>;
}

export class KvStoreClient extends grpc.Client {
constructor(address: string, credentials: grpc.ChannelCredentials, options?: object);
getValue(argument: nitric_proto_kvstore_v1_kvstore_pb.KvStoreGetValueRequest, callback: grpc.requestCallback<nitric_proto_kvstore_v1_kvstore_pb.KvStoreGetValueResponse>): grpc.ClientUnaryCall;
getValue(argument: nitric_proto_kvstore_v1_kvstore_pb.KvStoreGetValueRequest, metadataOrOptions: grpc.Metadata | grpc.CallOptions | null, callback: grpc.requestCallback<nitric_proto_kvstore_v1_kvstore_pb.KvStoreGetValueResponse>): grpc.ClientUnaryCall;
getValue(argument: nitric_proto_kvstore_v1_kvstore_pb.KvStoreGetValueRequest, metadata: grpc.Metadata | null, options: grpc.CallOptions | null, callback: grpc.requestCallback<nitric_proto_kvstore_v1_kvstore_pb.KvStoreGetValueResponse>): grpc.ClientUnaryCall;
setValue(argument: nitric_proto_kvstore_v1_kvstore_pb.KvStoreSetValueRequest, callback: grpc.requestCallback<nitric_proto_kvstore_v1_kvstore_pb.KvStoreSetValueResponse>): grpc.ClientUnaryCall;
setValue(argument: nitric_proto_kvstore_v1_kvstore_pb.KvStoreSetValueRequest, metadataOrOptions: grpc.Metadata | grpc.CallOptions | null, callback: grpc.requestCallback<nitric_proto_kvstore_v1_kvstore_pb.KvStoreSetValueResponse>): grpc.ClientUnaryCall;
setValue(argument: nitric_proto_kvstore_v1_kvstore_pb.KvStoreSetValueRequest, metadata: grpc.Metadata | null, options: grpc.CallOptions | null, callback: grpc.requestCallback<nitric_proto_kvstore_v1_kvstore_pb.KvStoreSetValueResponse>): grpc.ClientUnaryCall;
deleteKey(argument: nitric_proto_kvstore_v1_kvstore_pb.KvStoreDeleteKeyRequest, callback: grpc.requestCallback<nitric_proto_kvstore_v1_kvstore_pb.KvStoreDeleteKeyResponse>): grpc.ClientUnaryCall;
deleteKey(argument: nitric_proto_kvstore_v1_kvstore_pb.KvStoreDeleteKeyRequest, metadataOrOptions: grpc.Metadata | grpc.CallOptions | null, callback: grpc.requestCallback<nitric_proto_kvstore_v1_kvstore_pb.KvStoreDeleteKeyResponse>): grpc.ClientUnaryCall;
deleteKey(argument: nitric_proto_kvstore_v1_kvstore_pb.KvStoreDeleteKeyRequest, metadata: grpc.Metadata | null, options: grpc.CallOptions | null, callback: grpc.requestCallback<nitric_proto_kvstore_v1_kvstore_pb.KvStoreDeleteKeyResponse>): grpc.ClientUnaryCall;
scanKeys(argument: nitric_proto_kvstore_v1_kvstore_pb.KvStoreScanKeysRequest, metadataOrOptions?: grpc.Metadata | grpc.CallOptions | null): grpc.ClientReadableStream<nitric_proto_kvstore_v1_kvstore_pb.KvStoreScanKeysResponse>;
scanKeys(argument: nitric_proto_kvstore_v1_kvstore_pb.KvStoreScanKeysRequest, metadata?: grpc.Metadata | null, options?: grpc.CallOptions | null): grpc.ClientReadableStream<nitric_proto_kvstore_v1_kvstore_pb.KvStoreScanKeysResponse>;
}
Loading

0 comments on commit 8b607a2

Please sign in to comment.