Skip to content

Commit c694ae5

Browse files
authored
feat: add gapic getAppProfilesStream method (#771)
1 parent 8a2a74e commit c694ae5

File tree

5 files changed

+248
-63
lines changed

5 files changed

+248
-63
lines changed

src/index.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,10 @@ export class Bigtable {
750750
});
751751
};
752752

753-
const gapicStreamingMethods = {listTablesStream: true};
753+
const gapicStreamingMethods = {
754+
listTablesStream: true,
755+
listAppProfilesStream: true,
756+
};
754757

755758
if (isStreamMode) {
756759
stream = streamEvents(new PassThrough({objectMode: true}));

src/instance.ts

+59
Original file line numberDiff line numberDiff line change
@@ -686,6 +686,65 @@ Please use the format 'my-instance' or '${bigtable.projectName}/instances/my-ins
686686
);
687687
}
688688

689+
/**
690+
* Get {@link AppProfile} objects for all the App Profiles in your
691+
* Cloud Bigtable instance as a readable object stream.
692+
*
693+
* @param {object} [gaxOptions] Request configuration options, outlined here:
694+
* https://googleapis.github.io/gax-nodejs/CallSettings.html.
695+
* {@link Instance#getAppProfiles} for a complete list of options.
696+
* @returns {stream}
697+
*
698+
* @example
699+
* const {Bigtable} = require('@google-cloud/bigtable');
700+
* const bigtable = new Bigtable();
701+
* const instance = bigtable.instance('my-instance');
702+
*
703+
* instance.getAppProfilesStream()
704+
* .on('error', console.error)
705+
* .on('data', function(appProfile) {
706+
* // appProfile is a AppProfile object.
707+
* })
708+
* .on('end', () => {
709+
* // All appProfiles retrieved.
710+
* });
711+
*
712+
* //-
713+
* // If you anticipate many results, you can end a stream early to prevent
714+
* // unnecessary processing and API requests.
715+
* //-
716+
* instance.getAppProfilesStream()
717+
* .on('data', function(appProfile) {
718+
* this.end();
719+
* });
720+
*/
721+
getAppProfilesStream(gaxOptions?: CallOptions): NodeJS.ReadableStream {
722+
const reqOpts = {
723+
parent: this.name,
724+
};
725+
726+
// eslint-disable-next-line @typescript-eslint/no-this-alias
727+
const self = this;
728+
const transformToAppProfile = (
729+
chunk: google.bigtable.admin.v2.IAppProfile,
730+
enc: string,
731+
callback: Function
732+
) => {
733+
const appProfile = self.appProfile(chunk.name!.split('/').pop()!);
734+
appProfile.metadata = chunk;
735+
callback(null, appProfile);
736+
};
737+
return pumpify.obj([
738+
this.bigtable.request({
739+
client: 'BigtableInstanceAdminClient',
740+
method: 'listAppProfilesStream',
741+
reqOpts,
742+
gaxOpts: gaxOptions,
743+
}),
744+
new Transform({objectMode: true, transform: transformToAppProfile}),
745+
]);
746+
}
747+
689748
getClusters(options?: CallOptions): Promise<GetClustersResponse>;
690749
getClusters(options: CallOptions, callback: GetClustersCallback): void;
691750
getClusters(callback: GetClustersCallback): void;

system-test/bigtable.ts

+14
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,20 @@ describe('Bigtable', () => {
163163
assert(appProfiles.length > 0);
164164
});
165165

166+
it('should retrieve a list of app profiles in stream mode', done => {
167+
const appProfiles: AppProfile[] = [];
168+
INSTANCE.getAppProfilesStream()
169+
.on('error', done)
170+
.on('data', appProfile => {
171+
assert(appProfile instanceof AppProfile);
172+
appProfiles.push(appProfile);
173+
})
174+
.on('end', () => {
175+
assert(appProfiles.length > 0);
176+
done();
177+
});
178+
});
179+
166180
it('should check if an app profile exists', async () => {
167181
const [exists] = await APP_PROFILE.exists();
168182
assert.strictEqual(exists, true);

test/index.ts

+73-62
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,13 @@ describe('Bigtable', () => {
635635
gaxOpts: {},
636636
};
637637

638+
const gapicStreamingMethods = [
639+
'listTablesStream',
640+
'listInstancesStream',
641+
'listAppProfilesStream',
642+
'listClustersStream',
643+
];
644+
638645
beforeEach(() => {
639646
bigtable.getProjectId_ = (callback: Function) => {
640647
callback(null, PROJECT_ID);
@@ -874,76 +881,80 @@ describe('Bigtable', () => {
874881
});
875882
});
876883

877-
describe('makeGapicStreamRequest', () => {
878-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
879-
let GAX_STREAM: any;
880-
const config = {
881-
client: 'client',
882-
method: 'listTablesStream',
883-
reqOpts: {
884-
a: 'b',
885-
c: 'd',
886-
},
887-
gaxOpts: {},
888-
};
889-
890-
beforeEach(() => {
891-
GAX_STREAM = new PassThrough();
892-
bigtable.api[config.client][config.method] = {
893-
bind() {
894-
return () => {
895-
return GAX_STREAM;
884+
gapicStreamingMethods.forEach(method => {
885+
describe('makeGapicStreamRequest', () => {
886+
describe(method, () => {
887+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
888+
let GAX_STREAM: any;
889+
const config = {
890+
client: 'client',
891+
method: method,
892+
reqOpts: {
893+
a: 'b',
894+
c: 'd',
895+
},
896+
gaxOpts: {},
897+
};
898+
899+
beforeEach(() => {
900+
GAX_STREAM = new PassThrough();
901+
bigtable.api[config.client][config.method] = {
902+
bind() {
903+
return () => {
904+
return GAX_STREAM;
905+
};
906+
},
896907
};
897-
},
898-
};
899-
});
908+
});
900909

901-
it('should expose an abort function', done => {
902-
GAX_STREAM.cancel = done;
910+
it('should expose an abort function', done => {
911+
GAX_STREAM.cancel = done;
903912

904-
const requestStream = bigtable.request(config);
905-
requestStream.emit('reading');
906-
requestStream.abort();
907-
});
913+
const requestStream = bigtable.request(config);
914+
requestStream.emit('reading');
915+
requestStream.abort();
916+
});
908917

909-
it('should prepare the request once reading', done => {
910-
bigtable.api[config.client][config.method] = {
911-
bind(gaxClient: {}, reqOpts: {}, gaxOpts: {}) {
912-
assert.strictEqual(gaxClient, bigtable.api[config.client]);
913-
assert.deepStrictEqual(reqOpts, config.reqOpts);
914-
assert.strictEqual(gaxOpts, config.gaxOpts);
915-
setImmediate(done);
916-
return () => {
917-
return GAX_STREAM;
918+
it('should prepare the request once reading', done => {
919+
bigtable.api[config.client][config.method] = {
920+
bind(gaxClient: {}, reqOpts: {}, gaxOpts: {}) {
921+
assert.strictEqual(gaxClient, bigtable.api[config.client]);
922+
assert.deepStrictEqual(reqOpts, config.reqOpts);
923+
assert.strictEqual(gaxOpts, config.gaxOpts);
924+
setImmediate(done);
925+
return () => {
926+
return GAX_STREAM;
927+
};
928+
},
918929
};
919-
},
920-
};
921930

922-
const requestStream = bigtable.request(config);
923-
requestStream.emit('reading');
924-
});
931+
const requestStream = bigtable.request(config);
932+
requestStream.emit('reading');
933+
});
925934

926-
it('should destroy the stream with prepare error', done => {
927-
const error = new Error('Error.');
928-
bigtable.getProjectId_ = (callback: Function) => {
929-
callback(error);
930-
};
931-
const requestStream = bigtable.request(config);
932-
requestStream.emit('reading');
933-
requestStream.on('error', (err: Error) => {
934-
assert.strictEqual(err, error);
935-
done();
936-
});
937-
});
935+
it('should destroy the stream with prepare error', done => {
936+
const error = new Error('Error.');
937+
bigtable.getProjectId_ = (callback: Function) => {
938+
callback(error);
939+
};
940+
const requestStream = bigtable.request(config);
941+
requestStream.emit('reading');
942+
requestStream.on('error', (err: Error) => {
943+
assert.strictEqual(err, error);
944+
done();
945+
});
946+
});
938947

939-
it('should destroy the stream with GAX error', done => {
940-
const error = new Error('Error.');
941-
const requestStream = bigtable.request(config);
942-
requestStream.emit('reading');
943-
GAX_STREAM.emit('error', error);
944-
requestStream.on('error', (err: Error) => {
945-
assert.strictEqual(err, error);
946-
done();
948+
it('should destroy the stream with GAX error', done => {
949+
const error = new Error('Error.');
950+
const requestStream = bigtable.request(config);
951+
requestStream.emit('reading');
952+
GAX_STREAM.emit('error', error);
953+
requestStream.on('error', (err: Error) => {
954+
assert.strictEqual(err, error);
955+
done();
956+
});
957+
});
947958
});
948959
});
949960
});

test/instance.ts

+98
Original file line numberDiff line numberDiff line change
@@ -724,6 +724,104 @@ describe('Bigtable/Instance', () => {
724724
});
725725
});
726726

727+
describe('getApprofilesStream', () => {
728+
let returnStream: PassThrough;
729+
beforeEach(() => {
730+
returnStream = new PassThrough({
731+
objectMode: true,
732+
});
733+
});
734+
735+
it('should provide the proper request options', done => {
736+
const stub = sandbox.stub(pumpify, 'obj');
737+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
738+
(instance.bigtable.request as Function) = (config: any) => {
739+
assert.strictEqual(config.client, 'BigtableInstanceAdminClient');
740+
assert.strictEqual(config.method, 'listAppProfilesStream');
741+
assert.deepStrictEqual(config.reqOpts, {
742+
parent: INSTANCE_NAME,
743+
});
744+
assert.deepStrictEqual(config.gaxOpts, undefined);
745+
setImmediate(done);
746+
return returnStream;
747+
};
748+
instance.getAppProfilesStream();
749+
assert.strictEqual(stub.getCall(0).args[0][0], returnStream);
750+
});
751+
752+
it('should accept gaxOptions', done => {
753+
const gaxOptions = {timeout: 1000};
754+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
755+
(instance.bigtable.request as Function) = (config: any) => {
756+
assert.strictEqual(config.gaxOpts, gaxOptions);
757+
setImmediate(done);
758+
return returnStream;
759+
};
760+
instance.getAppProfilesStream(gaxOptions);
761+
});
762+
763+
it('should return error from gapic', done => {
764+
const error = new Error('Error.');
765+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
766+
(instance.bigtable.request as Function) = () => {
767+
return returnStream;
768+
};
769+
setImmediate(() => {
770+
returnStream.destroy(error);
771+
});
772+
773+
instance.getAppProfilesStream().on('error', err => {
774+
assert.strictEqual(err, error);
775+
done();
776+
});
777+
});
778+
779+
it('should return an array of AppProfile objects', done => {
780+
const response = [
781+
{
782+
name:
783+
'/projects/my-project/instances/my-instance/appProfiles/my-appProfile-a',
784+
},
785+
{
786+
name:
787+
'/projects/my-project/instances/my-instance/appProfiles/my-appProfile-a',
788+
},
789+
];
790+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
791+
(instance.bigtable.request as Function) = () => {
792+
return returnStream;
793+
};
794+
setImmediate(() => {
795+
response.forEach(r => {
796+
returnStream.push(r);
797+
});
798+
returnStream.push(null);
799+
});
800+
801+
const appProfiles: AppProfile[] = [];
802+
instance
803+
.getAppProfilesStream()
804+
.on('error', assert.ifError)
805+
.on('data', appProfile => {
806+
assert(appProfile instanceof FakeAppProfile);
807+
appProfiles.push(appProfile);
808+
})
809+
.on('end', () => {
810+
assert.strictEqual(
811+
appProfiles[0].id,
812+
response[0].name.split('/').pop()
813+
);
814+
assert.deepStrictEqual(appProfiles[0].metadata, response[0]);
815+
assert.strictEqual(
816+
appProfiles[1].id,
817+
response[1].name.split('/').pop()
818+
);
819+
assert.deepStrictEqual(appProfiles[1].metadata, response[1]);
820+
done();
821+
});
822+
});
823+
});
824+
727825
describe('getClusters', () => {
728826
it('should provide the proper request options', done => {
729827
// eslint-disable-next-line @typescript-eslint/no-explicit-any

0 commit comments

Comments
 (0)