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

Implement Service & ServiceObject classes #904

Closed

Conversation

stephenplusplus
Copy link
Contributor

This PR is going to be split into pieces

Core Service & ServiceObject: #928
BigQuery: #931
Compute: #932
Datastore: #897
DNS: #942
Resource: #943
Pub/Sub: #944
Search: #945
Storage: #946

Fixes #847
Fixes #862
Fixes #914

This will be a big PR, and is still a work in progress. @callmehiphop -- please review at your earliest convenience!

To Dos

  • Review by @callmehiphop
  • Implement across the API
  • Docs
    • Make sure all of the Service.serviceObject = function() {} docs remove "reference an existing ____"
    • Make sure create is shown with a config object where required
    • Breaking changes
      • BigQuery.dataset#createTable: previously (options, callback), now (id, options, callback)
  • Tests
    • Unit
      • Service
      • ServiceObject
      • BigQuery
        • dataset
        • index
        • job
        • table
      • Compute
        • address
        • disk
        • firewall
        • index
        • network
        • operation
        • region
        • snapshot
        • vm
        • zone
      • Datastore
      • DNS
        • change
        • index
        • record
        • zone
      • Pub/Sub
        • iam
        • index
        • subscription
        • topic
      • Resource
        • index
        • project
      • Search
        • document
        • field
        • index-class
        • index
      • Storage
        • acl
        • bucket
        • file
        • index
    • System
      • BigQuery
      • Compute
      • Datastore
      • DNS
      • Pub/Sub
      • Resource
      • Search
      • Storage

Goal

Add create, get (with autoCreate behavior), and exists methods to all service objects.

Implementation

Distinctly define the types of classes we have:

  1. Service classes (Datastore, Storage, Compute, ...)
  2. Service Object classes (Dataset, Bucket, VM, ...)

Define common methods for each:

  1. Service classes:
    1. request => make an authenticated request
  2. Service Object classes:
    1. create => create this object
    2. exists => does this object exist, returns true or false
    3. get => get the metadata for this object -- pass {autoCreate:true} to create the object if it doesn't exist
    4. delete => delete this object
    5. getMetadata => get the metadata for this object
    6. setMetadata => set the metadata for this object
    7. request => make an authenticated request

We've been re-writing these methods all over our library, which has led to some unwanted side effects (inconsistency / repeated code / repeated tests). This separation will allow us to write once, run everywhere.

The Problems

  1. Not every service object can utilize all of these methods. For example, a Compute Engine Zone can't be created. You can't set the metadata on a Compute Engine Snapshot. You can't delete a Compute Engine Region. This requires configuration so that the unwanted methods are blanked out.
  2. Documentation. What we want is to inherit a bunch of methods and be done with it, but we still need to document them matching the context of the service object. We don't want the user to see some generic "To delete this object, call object.delete(). And we have to uniquely document create, since each method will take different parameters.

@googlebot googlebot added the cla: yes This human has signed the Contributor License Agreement. label Oct 1, 2015
@stephenplusplus stephenplusplus changed the title Spp get or create Implement Service & ServiceObject classes Oct 1, 2015
this.makeReq_('DELETE', '', query, null, callback);
this.request({
method: 'DELETE',
uri: '',

This comment was marked as spam.

This comment was marked as spam.

@stephenplusplus
Copy link
Contributor Author

@callmehiphop can you take another quick glance over the changes I've made (latest commit)? I think I may have come up with an easier way to handle the inherits/excludes/borrows mess. Now, we pass an object listing the methods we want to inherit explicitly: methods. It's just a map of methodName: true, but the best part is that we can document these properties instead of overwriting the methds we want to extend, only to call them by ServiceObject.prototype.method... again.

We don't override methods just to call them again only for the docs to be built properly. I cheated around that by just documenting aobject that is used when we extend from ServiceObject.

Also, the docs are much simpler to avoid all of the replication. The create, get, and getOrCreate docs are basically shells that don't show anything more than basic usage that will always be true, even if the upstream API arguments change (like zone#createVM's config).

@callmehiphop
Copy link
Contributor

I like this approach much more than the previous.

However it still kinda feels odd to me, mainly because I don't think we should be adding code for the sake of documentation, even if it's just a little. I think I've suggested this before, but have we considered moving away from dox? JSDoc3 actually has pretty nice support for spitting out plain old JSON (minus custom tags). It also allows you to document "virtual" members, which is just code that exists elsewhere. (e.g. getOrCreate).

I realize this would be a large undertaking, so this is obviously not the time or place for a change like that, but it's still something I think we should consider. Aside from that, I think it looks good!

@stephenplusplus
Copy link
Contributor Author

mainly because I don't think we should be adding code for the sake of documentation, even if it's just a little

Which code does that?

@stephenplusplus
Copy link
Contributor Author

I think I've suggested this before, but have we considered moving away from dox? JSDoc3 actually has pretty nice support for spitting out plain old JSON (minus custom tags).

I'd be in support of this, but given the scope of the work involved, it's a pretty intimidating task to take on. Maybe switching over should happen when we get to the gcloud-* common docs task.

@callmehiphop
Copy link
Contributor

Maybe I misunderstood, but I thought the main reason to switch to an object containing method names flagged as true was for documentation purposes?

@callmehiphop
Copy link
Contributor

I'd be in support of this, but given the scope of the work involved, it's a pretty intimidating task to take on. Maybe switching over should happen when we get to the gcloud-* common docs task.

Yeah totally!

@stephenplusplus
Copy link
Contributor Author

It does work for that purpose, but it's also to communicate to ServiceObject what methods we're going to inherit.

@callmehiphop
Copy link
Contributor

It does work for that purpose, but it's also to communicate to ServiceObject what methods we're going to inherit.

Gotcha! Do you think it would be better to make a custom inherit function to do that? It might be slightly more efficient to tie ServiceObject methods to an api's prototype chain

inherit(BigQuery, ServiceObject, {
  getOrCreate: null
});

Either way, I approve!

@stephenplusplus
Copy link
Contributor Author

RE: the getOrCreate: null, I prefer the whitelist over the blacklist because:

  1. each class has to explicitly ask for what methods they want
  2. having all of the methods listed that you want to inherit is a logical place to put the documentation

There definitely might be a better way to do this though, so I'll think about it as I go on. Something worth noting is in some cases, we don't know if a class needs a method or not until it's instantiated.

@callmehiphop
Copy link
Contributor

Something worth noting is in some cases, we don't know if a class needs a method or not until it's instantiated.

Why is that?

this.name = name;
this.metadata = {};
function Snapshot(scope, name) {
var isDisk = scope.constructor.name === 'Disk';

This comment was marked as spam.

@stephenplusplus
Copy link
Contributor Author

@callmehiphop feel free to take a look at the new implementation of exists and get with autoCreate: true. getOrCreate has been removed. I think the pattern I'll be following re: docs, inheritance, ServiceObject is pretty much on lock now. I'll just be carrying over the code to the libraries that haven't been implemented yet.

Then... testing. Dun, dun, dun.


// The `request` method may have been overridden to hold any special behavior.
// Ensure we call the original `request` method.
ServiceObject.prototype.request.call(this, reqOpts, function(err, resp) {

This comment was marked as spam.

This comment was marked as spam.

@callmehiphop
Copy link
Contributor

getOrCreate has been removed

yay! looks good

@stephenplusplus stephenplusplus force-pushed the spp--getOrCreate branch 3 times, most recently from f15c67e to 59570b9 Compare October 30, 2015 16:18
@callmehiphop
Copy link
Contributor

This is getting pretty huge, GitHub can't even display the entire diff because of the size. Any chance we could break any of this up?

@stephenplusplus
Copy link
Contributor Author

Yes, very good idea. I'll start with Service & ServiceObject.

@stephenplusplus
Copy link
Contributor Author

@callmehiphop: #928

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes This human has signed the Contributor License Agreement. core
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants