Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integration with Gapic-generated code #1463

Closed
jmuk opened this issue Aug 1, 2016 · 20 comments
Closed

Integration with Gapic-generated code #1463

jmuk opened this issue Aug 1, 2016 · 20 comments
Assignees
Labels

Comments

@jmuk
Copy link
Contributor

jmuk commented Aug 1, 2016

I'm in charge of Gapic codegen and GAX for NodeJS

It was an independent effort, but recently we reached to an agreement that bringing the generated code into a part of gcloud itself.

It's still unclear to me where the generated code will be located and how these files are accessible from users, so this is an issue to discuss about these things.

@stephenplusplus
Copy link
Contributor

Hello! Is there any "getting started" material for those projects? Example code would be very helpful as well.

@jmuk
Copy link
Contributor Author

jmuk commented Aug 2, 2016

Sorry, we don't 😞
We are working on it, but for now, I want to generate on my local setup and send the generated files as a PR.

@jmuk
Copy link
Contributor Author

jmuk commented Aug 3, 2016

Hmm, I've noticed that the handling of grpc-client is significantly different.

Our code generator will generate the code assuming to obtain grpc.load() results and inject some features (like retries or converting paged responses to a stream) through the GAX library, whereas gcloud-node's lib/common provides another abstraction layer with features like retries.

Let me keep the existing scheme for now, but probably this common library and GAX would converge in some way in the future.

@stephenplusplus
Copy link
Contributor

No problem, I'm excited to see the PR.

but probably this common library and GAX would converge in some way in the future.

Yeah, that would be great! I'd really like if we deferred to GAX for retries/error handling best practices.

@stephenplusplus
Copy link
Contributor

@jmuk -- related to #1346 -- Since we're going to eventually send our requests through gax, will it be possible for a user to simply provide an auth token instead of going through a keyfile/default credentials auth flow?

@jmdobry
Copy link
Contributor

jmdobry commented Aug 8, 2016

Proposals that came out of my meeting with @jmuk, using the Speech API as an example:

Variant 1 - expose auto-gen on exported module

Importing

// Initialize hand-written client:
var Speech = require('@google-cloud/speech');
var speech = Speech({
  keyFilename: '/path/to/keyfile.json',
  projectId: 'grape-spaceship-123'
});

// Use an idiomatic convenience method, which was implemented using
// the v2 "bare metal" method shown below
speech.createDetectJob(...);

// Initialize auto-generated client for v2:
var speech_v2 = Speech.v2();

// Use a "bare metal" grpc method
speech_v2.asyncrecognize(...);

//Initialize auto-generated client for v1:
var speech_v1 = Speech.v1();

// Use a "bare metal" grpc method
speech_v1.asyncrecognize(...);

This requires that the exported Speech module has the auto-gen clients attached to it:

function Speech (...) {...}

Speech.v2 = require('./v2');
Speech.v1 = require('./v1');

module.exports = Speech;

Getting auto-gen updates integrated into gcloud-node would require a little more
engineering in the generated PRs, as the root index.js file has to be edited.

File structure

packages/
  speech/
    src/
      index.js // hand-written layer, uses auto-gen under the hood
      v1/
        index.js // auto-generated
      v2/
        index.js // auto-generated
    package.json

package.json contains "main": "./src/index.js".

What if the API doesn't have or need a hand-written layer?

The root index.js file would just export one of the auto-gen clients.

// Initialize hand-written client:
var simpleapi = require('@google-cloud/simpleapi')();

// Initialize auto-generated client for v2 of simpleapi:
var simpleapi_v2 = simpleapi.v2();

assert.deepEqual(simpleapi, simpleapi_v2)

Variant 2 - user imports auto-gen on separate path

Importing

// Initialize hand-written client:
var speech = require('@google-cloud/speech')({
  keyFilename: '/path/to/keyfile.json',
  projectId: 'grape-spaceship-123'
});

// Use an idiomatic convenience method, which was implemented using
// the v2 "bare metal" method shown below
speech.createDetectJob(...);

// Initialize auto-generated client for v2:
var speech_v2 = require('@google-cloud/speech/v2')();

// Use a "bare metal" grpc method
speech_v2.asyncrecognize(...);

// Initialize auto-generated client for v1:
var speech_v1 = require('@google-cloud/speech/v1')();

// Use a "bare metal" grpc method
speech_v1.asyncrecognize(...);

This does NOT require the exported Speech module to have the auto-gen clients attached to it:

function Speech (...) {...}

module.exports = Speech;

Instead the user gets a hold of an auto-gen client by importing it from a deeper path:

var speech_v1 = require('@google-cloud/speech/v1')();

The updated folder structure in this variant allows require's resolution algorithm
to find the correct file within the package.

Getting auto-gen updates integrated into gcloud-node would be a little easier,
as the root index.js does not have to be edited. auto-gen clients for new API
versions could be picked up automatically.

File structure

packages/
  speech/
    index.js // hand-written layer, uses auto-gen under the hood
    v1/
      index.js // auto-generated
    v2/
      index.js // auto-generated
    package.json

package.json omits the main field.

What if the API doesn't have or need a hand-written layer?

The root index.js file would just export one of the auto-gen clients.

// Initialize hand-written client:
var simpleapi = require('@google-cloud/simpleapi')();

// Initialize auto-generated client for v2 of simpleapi:
var simpleapi_v2 = require('@google-cloud/simpleapi/v2')();

assert.deepEqual(simpleapi, simpleapi_v2)

@stephenplusplus
Copy link
Contributor

Variant 1 has my vote. That makes the transition seamless to the users that just want the convenience API, then uses API versions as the concise, logical entry points to the users that need full access. 👍

@jmdobry
Copy link
Contributor

jmdobry commented Aug 8, 2016

One additional thought I had is that the hand-written layer inevitably must instantiate an auto-gen client in order to use it to implement idiomatic methods, thus, the exposed hand-written client should make its auto-gen client methods available on itself, for example:

// Initialize hand-written client:
var Speech = require('@google-cloud/speech');
var speech = Speech({
  keyFilename: '/path/to/keyfile.json',
  projectId: 'grape-spaceship-123'
});

// use hand-written method
speech.createDetectJob(...);
// now use bare-metal method, a pass-thru to auto-gen client
speech.asyncrecognize(...);

// This is wasteful, as the v2 auto-gen client has now been initialized twice.
// I shouldn't HAVE to do this just to get to that bare-metal method
var speech_v2 = Speech.v2();
speech_v2.asyncrecognize(...);

@stephenplusplus
Copy link
Contributor

I actually prefer the separation, but we also have name clashing to watch out for. An alternative to a call to "version()" could be just dropping the methods inside a "version" property on the instantiated object:

pubsub.getTopics()
pubsub.v1beta1.getTopics()

@stephenplusplus
Copy link
Contributor

Actually not sure about that ^. If required scopes change between API releases, we couldn't re-use an auth client. So without a function call to create a new "v2()", we would have to use some kind of lazy-instantiating code that creates a new auth-client. That often means confusing code and unintended side effects.

I personally don't mind the "v2()" call and like the separation implied by that hierarchy, but happy to hear other thoughts on the matter.

@jmdobry
Copy link
Contributor

jmdobry commented Aug 9, 2016

I personally don't mind the "v2()" call and like the separation implied by that hierarchy, but happy to hear other thoughts on the matter.

You probably right. If someone wants to use a bare-metal method, they can just instantiate an auto-gen client. However, documentation for it is hard to generalize because the auto-gen clients are namespaced by a version. The user would have to be aware of the API version the want to use in order to do it, which is an extra thing to have to think about.

@jmuk
Copy link
Contributor Author

jmuk commented Aug 9, 2016

Hi,

I pushed the initial patch for Speech API client as jmuk@ec6ecf7

Please take a look for how it goes. A minor difference is -- we generate a 'class' (or a service object) per grpc service. Because an API can consist of multiple services, it will be:

var Speech = require('@google-cloud/speech');

// hand-written layere
var speech = Speech({
  keyFilename: ...
  projectId: ...
});

// auto-gen layer
var api = new Speech.v1.SpeechApi();
api.nonStreamingRecognize(...);

rathrer than Speech.v1(...) or something. A better example would be pubsub -- it would consist of pubsub.v1.PublisherApi and pubsub.v1.SubscriberApi. I believe that won't affect the conclusion though.

@jmuk
Copy link
Contributor Author

jmuk commented Aug 9, 2016

Regarding the auth-tokens (sorry I'm late on the response): GAX depends on the auth library ("google-auth-library"), the generated code would (and should) support the customized authentication as the auth library accepts.

@stephenplusplus
Copy link
Contributor

That makes sense. Any opinions on if we should try to avoid making users deal with new?

// auto-gen layer
- var api = new Speech.v1.SpeechApi();
+ var api = speech.v1.speechApi();
api.nonStreamingRecognize(...);

@stephenplusplus
Copy link
Contributor

Regarding the auth-tokens (sorry I'm late on the response): GAX depends on the auth library ("google-auth-library"), the generated code would (and should) support the customized authentication as the auth library accepts.

Okay, thanks. I'll have to make an issue there to see if they can support that feature.

@jmuk
Copy link
Contributor Author

jmuk commented Aug 9, 2016

Hm, I didn't consider the option about omitting new. Probably allowing both would make more sense. Let me fix the code generator.

@jmdobry
Copy link
Contributor

jmdobry commented Aug 9, 2016

Yeah, you can see here how the Speech client does it: https://github.com/jmdobry/gcloud-node/blob/speech/packages/speech/src/index.js#L101

jmuk added a commit to jmuk/gcloud-node that referenced this issue Aug 10, 2016
- language_service_api.js and language_service_client_config.json:
  auto-generated
- v1beta1/index.js: manually written
- index.js and package.json: manually modified

Updates googleapis#1463
@jmuk
Copy link
Contributor Author

jmuk commented Aug 10, 2016

I've created #1476 for adding Gapic-created code for Cloud-Language API. This was created because Speech API is still in PR, and I want to request the review for the auto-generated code. Please take a look at this.

@jmuk
Copy link
Contributor Author

jmuk commented Aug 10, 2016

For authentication -- GAX actually invokes 'getApplicationDefault', which might not fit greatly with gcloud-node parameters. I've filed googleapis/gax-nodejs#31 for this purpose.

jmuk added a commit to jmuk/gax-nodejs that referenced this issue Aug 11, 2016
As we discussed in googleapis/google-cloud-node#1463',
many of auth parameters will be called in a initialization
of the module itself and then used later.

For this purpose, GAX also serves an object which keeps
the auth data and handle its feature under the same context.

This also offers a new dependency of google-auto-auth library
instead of google-auth-library, and supports nice optional parameters
like keyFile.
jmuk added a commit to jmuk/gax-nodejs that referenced this issue Aug 12, 2016
As we discussed in googleapis/google-cloud-node#1463',
many of auth parameters will be called in a initialization
of the module itself and then used later.

For this purpose, GAX also serves an object which keeps
the auth data and handle its feature under the same context.

This also offers a new dependency of google-auto-auth library
instead of google-auth-library, and supports nice optional parameters
like keyFile.
jmuk added a commit to jmuk/gcloud-node that referenced this issue Aug 15, 2016
- language_service_api.js and language_service_client_config.json:
  auto-generated
- v1beta1/index.js: manually written
- index.js and package.json: manually modified

Updates googleapis#1463
@jmuk
Copy link
Contributor Author

jmuk commented Aug 15, 2016

Just for the record: we continued the discussion about loading the generated files at #1476, and it seems we agreed to take Variant 1 style.

var speech = require('@google-cloud/speech');
var speechV1 = speech.v1({keyFile: ...});
var api = speechV1.SpeechApi();
api.nonStreamingRecognize(...);

jmuk added a commit to googleapis/gax-nodejs that referenced this issue Aug 17, 2016
* Refactor gRPC and Auth situation.

As we discussed in googleapis/google-cloud-node#1463',
many of auth parameters will be called in a initialization
of the module itself and then used later.

For this purpose, GAX also serves an object which keeps
the auth data and handle its feature under the same context.

This also offers a new dependency of google-auto-auth library
instead of google-auth-library, and supports nice optional parameters
like keyFile.

* Minor style fix
jmuk added a commit to jmuk/gcloud-node that referenced this issue Aug 17, 2016
- language_service_api.js and language_service_client_config.json:
  auto-generated
- v1beta1/index.js: manually written
- index.js and package.json: manually modified

Updates googleapis#1463
jmuk added a commit to jmuk/gcloud-node that referenced this issue Aug 20, 2016
- language_service_api.js and language_service_client_config.json:
  auto-generated
- v1beta1/index.js: manually written
- index.js and package.json: manually modified

Updates googleapis#1463
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants