Git Product home page Git Product logo

gogolica's Introduction

Gogolica

Always think of what is useful and not what is beautiful.
Beauty will come of its own accord.

Nikolai Vasilievich Gogol

You can't imagine how stupid the whole world has grown nowadays.

Also Nikolai Vasilievich Gogol



CircleCI

Warning: ALPHA STATUS

Gogolica is an auto-generated Clojure bindings library for Google APIs.

The implemented APIs are the ones provided from the Google Discovery Service's JSON description files of the available "new style" Google APIs.

The generator itself and the code it produces are Alpha.

Some APIs are alpha/beta, and indicated as such in the namespace (e.g., "gogolica.storage.v1alpha").

Available APIs

None yet.

Usage

gogolica.storage.v1

(require '[gogolica.storage.v1 :as gcs])

;; Authentication, if you have the env variable GOOGLE_APPLICATION_DEFAULT set,
;; then your service account key will be read from the path specified in it.
;; Otherwise you can load it manually:
(gogolica.core.auth/key-from-file "path/to/key.json")

;; List buckets for your project
(gcs/buckets-list "my-project-name" {})

;; List objects for a buckets
(gcs/objects-list "my-bucket-name" {})

;; Create new bucket
(gcs/buckets-insert {:name "my-new-bucket-name"} "my-project-name" {})

;; Upload a new object
(gcs/objects-insert "/absolute/path/to/file.png" {} "my-new-bucket-name" {:name "my-file-name"})

;; Download the new object - metadata
(gcs/objects-get "my-new-bucket-name" "my-file-name" {})

;; Download the new object - data as bytearray
(gcs/objects-get "my-new-bucket-name" "my-file-name" {:alt "media"})

Developing

Getting the JSON models for Google APIs

This will clone the Google auto-generated Go library in the vendor directory, and copy over their versioned json models to the model folder.

./script/copy-models

TODO

TODO

Testing

Unit tests (test pure functions)

lein test

Integration tests (test hitting APIs)

TODO

License

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

gogolica's People

Contributors

f-f avatar zudov avatar

Stargazers

 avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

gogolica's Issues

Rename library

@zudov I propose a renaming of the library: googlica -> gogolica.

There is a few reasons:

  1. The new one will get less hits on google. Also, while googling googlica, it gets autocorrected to "google", which is not nice.
  2. There is objectively strong ties to the haskell library
  3. Allows to refer to Gogol, with pictures and great quotes like this one:

"You can't imagine how stupid the whole world has grown nowadays."

Nikolai Vasilievich Gogol

Full media download support

Currently we simply allow the user to specify alt=media in order to get redirected to the downloadable content (I suppose the server then returns a SEE OTHER).

"Media Download" section however recommends constructing the download url by prefixing the path with /download/, which avoids the redirect.

Also there is a useMediaDownloadService, I'm not sure, but probably it means than when it's true the alt=media won't work. Or maybe it would, nevertheless they recommend to prefix url with /download/ at all times ๐Ÿคทโ€โ™‚๏ธ

I think it would be pretty nice if when the mediaDownload is true, we generate additional function (with -media) suffix that would perform that /download/ request.

So instead of (taken from README) doing:

(gcs/objects-get "my-new-bucket-name" "my-file-name" {:alt "media"})

One would do:

(gcs/objects-get-media "my-new-bucket-name" "my-file-name")

Alternative interface would be to allow some {:media-download true} option as an argument to normal objects-get, but it feels nice to have a separate function, since the functionality of "get the metadata of an object" and "get content of an object" feels like different functions (what you do with the response and the purpose of it is quite different).

The support for alt parameter would remain as part of #26, since it also allows to specify alternative response formats, we should be aware of that and decode the content appropriately.

Meaningful way of handling gogolica's and Google's errors

We should have some helpers and preferred practices (documented in the README) for handling various errors that are produced by either gogolica itself, like:

  • failed to load the credentials
  • given body doesn't match the required schema
  • the parameter that's conditionally required wasn't provided
  • ...

as well as the errors returned by google's services:

  • bucket doesn't exist doesn't exist
  • queue is full
  • machine hasn't started yet
  • user rate limit exceeded
  • service rate limit
  • ....

As usually, it's worth looking at how gogol does it. There is a hierarchy of errors, the errors are thrown as exceptions, convenient lens machinery is provided to focus on particular types of errors, and there are handy catching, trying functions.

I guess clojure can do that quite well by putting the error data into ex-info and providing some predicates (service-error?, credentials-error?, bucket-not-found-error?, rate-limit-error?) on those exceptions and using something like slingshot to catch them conveniently.

That was initially a comment on #21, but I thought it deserves a separate issue since it's not about the 500 errors, but more about the errors that you would actually want to handle in your code. #21 is more about the retries on availability errors.

Json shouldn't be converted to kebab case

We shouldn't be converting all the model json to kebab case. While it is nicer and more idiomatic to do destructuring with it, Google APIs expect parameters to be passed in with their syntax in requests.

The following code is being generated now:
image

While we could of course convert back the parameters to CamelCase, it might be that the conversion is not lossless, and I don't feel safe doing CamelCase -> kebab-case -> CamelCase.

Format generated code

Right now generated code doesn't contain any newlines, so it's pretty unreadable.
We should use something like cljfmt to format it.

Switch to proper uri-template library

We have a half-baked implementation of uri-template substitution, that takes a path string like "/a/{foo}/b/{bar}" and generates a Clojure vector with the proper symbols like ["/a/" foo "/b/" bar].

Testing this on the storage model works, but if we run it on all the 3931 methods, we discover that it's broken on some of them because it doesn't cover all of the standard.

Move all our utility namespaces into deeper namespace

The namespaces of generated modules look like gogolica.storage.v1, and our utility namespaces are like gogolica.auth, gogolica.utils, gogolica.model and so on.

To distinguish between them, we should consider somehow distinguish our "operational" namespaces from the generated one. I like the names that we currently generate, so maybe we could move the operational modules a level deeper. Something like gogolica.gen.{auth,utils,model,...}.

However, gogolica.gen is boring, and maybe we should give a nice name to the thing that would be generating the actual user-facing gogolica. I propose either Viy (more evil), or Bulba (more epic). Then we can have gogolica.viy or gogolica.bulba.

Recognise the enum parameters

For parameter like:

:projection
  {:type "string",
   :description "Set of properties to return. Defaults to noAcl.",
   :enum ["full" "noAcl"],
   :enumDescriptions
   ["Include all properties."
    "Omit owner, acl and defaultObjectAcl properties."],
   :location "query"}

We could spew out the code like:

:projection
  (case (name projection)
    "no-acl" "noAcl"
    (name projection))

to allow kebab case.

#10 (spec-checking) and #19 (docstring generation) would benefit from recognizing them too.

Respect parameterOrder

In API models every method has a parameterOrder field which specifies a canonical order in which the required parameters should be placed.
We need to take it in account when generating a vector of function's required parameters.

Typecheck/spec generated functions

Since the json files define JSON schemas for the response of each method, we should use this information to spec the generated code, with either core.spec or plumatic/schema.

Make optional parameters an actually optional argument

All the methods have an optional-parameters map in the end, which contains the optional parameters, but it's not optional in itself, and it has to be specified even when empty.
This makes for an awkward UI.

We should have an additional arity, where that map is actually optional.

Fetch the API models directly from API explorer

We currently get the models by submoduling the go repo and then having a bash script that copies things from there, but instead one could just get them from API explorer via HTTP:

http https://www.googleapis.com/discovery/v1/apis/storage/v1/rest | jq '.resources | keys'                                                             
[                                                                                                                                                                                 
  "bucketAccessControls",
  "buckets",
  "channels",
  "defaultObjectAccessControls",
  "notifications",
  "objectAccessControls",
  "objects",
  "projects"
]

I think we should get rid of irrelevant submodule and either rewrite the script to fetch all things via curl and jq, or just have code that can fetch them inside gogolica.gen.model.

Establish a procedure for commiting generated code into the repo

Checking in the kilolines of generated code can get quite messy, but this can be mitigated by establishing and documenting some sensible rules.

A couple of ideas to get started:

  • the commits that include generated things should never include anything that was written by hand
  • "regenerate code/documentation/models" things should be entirely done by self-contained scripts, that are checked into the repo. No arbitrary command line fiddling.
  • those scripts should perform the full pipeline of:
    • clean the repo
    • regenerate the relevant thing and stage it
    • request for confirmation and commit it
    • push and create a pull request (should be easy via hub)
  • the commit as well as pull request messages should be generated automatically and be useful:
    • specify the name of the script that produced it
    • include the hashes and versions of the upstream things
    • BONUS: include summary of changes (e.g. a list of affected namespaces/methods/functions)
  • we should specify some fictional commit author, while the person responsible for the commit is specified as commiter
    • BONUS: Give them a name, identity, create them a github user and automate them via CI. Then they could be as bold as to specify themselves as a commiter.

Handle paginated endpoints

The MVP interface could be just a lazy-seq + instructions on how to consume it. We might adopt some streaming API later if it's needed.

Handle auth

Some methods require authentication/authorization, while some are open.

I have no idea how to do this yet, but the general API reference says that we should use Oauth2, so we should probably implement that.

Support nested resources

Currently when mapping over :resources we treat it as a flat list, but SUDDENLY, resources can be nested, so we end up only grabbing the top level routes at the moment.

> (-> storage-model :resources :projects :resources :serviceAccount :methods :get)
{:id "storage.projects.serviceAccount.get",
 :path "projects/{projectId}/serviceAccount",
 :httpMethod "GET",
 :description
 "Get the email address of this project's Google Cloud Storage service account.",
 :parameters
 {:projectId
  {:type "string",
   :description "Project ID",
   :required true,
   :location "path"},
  :userProject
  {:type "string",
   :description
   "The project to be billed for this request, for Requester Pays buckets.",
   :location "query"}},
 :parameterOrder ["projectId"],
 :response {:$ref "ServiceAccount"},
 :scopes
 ["https://www.googleapis.com/auth/cloud-platform"
  "https://www.googleapis.com/auth/cloud-platform.read-only"
  "https://www.googleapis.com/auth/devstorage.full_control"
  "https://www.googleapis.com/auth/devstorage.read_only"
  "https://www.googleapis.com/auth/devstorage.read_write"]}

Add some tests

Right now lots of stuff is "tested" including repl outputs as comments, let's move this into actual tests.

Include parameters type and documentation in method docstring

The parameters list for every method has some information about the type and the meaning of every parameter.
While the actual typechecking of the params should be covered in #10, here we should include both the type and documentation of the params in the method docstring.

Handle HTTP errors/retries

Google specifies some guidelines about handling errors, which we should follow:

When uploading media, be sure to follow these best practices related to error handling:

  • Resume or retry uploads that fail due to connection interruptions or any 5xx errors, including:

    • 500 Internal Server Error
    • 502 Bad Gateway
    • 503 Service Unavailable
    • 504 Gateway Timeout
  • Use an exponential backoff strategy if any 5xx server error is returned when resuming or retrying upload requests. These errors can occur if a server is getting overloaded. Exponential backoff can help alleviate these kinds of problems when there is a high volume of requests or heavy network traffic.

  • When you receive an error other than a 5xx error, you do not need to use an exponential backoff strategy. Instead, limit the number of times you retry the request. For example, your code could report an error to the user after retrying a request 10 times.

Add namespace declaration generation

E.g. when reading the storage json, we take the name and version top level keys (which values are storage and v1 in this case), and generate the following ns definition:

(ns googlica.storage.v1
  (:gen-class)
  (:require [clj-http.client :as client])

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.