Tutorial

Last updated: Mar 10th, 2017

This tutorial assumes that you've been though the quickstart. For convenience, all code snippets are in Python, use the Pyton API client presented in the quickstart, and use a variable named client which we assume is a reference to the Powertools API client object.

This tutorial covers in details the usage of some essential endpoints of the API. After reading it, it should be easy to understand how to use any of the many available endpoints directly from the reference.

In this tutorial, we'll create a new workpace, make Powertools manage a native Drive file in this workspace, add a tag to the file, create a new filetype, assign the filetype to the file, submit a version of the file and finally search for the file.

Create a new workspace

When we look at the workspaces section of the Reference, we see that to create a new workspace, we must use the POST method on the /workspaces endpoint, which corresponds to the operation ID workspaces.insert.

Endpoint: workspaces.insert

client.workspaces().insert(
  api_key=API_KEY,
  domain='mydomain.com',
  actionUser='admin@mydomain.com',
  ownerId='admin@mydomain.com',
  managerId='admin@mydomain.com',
  body={
    'name': 'MyProjects'
  }
).execute()
            

Other fields accepted in the body are pushToRoot and restrictedSharing, but they are optional because they have a default value.

Because inserting a workspace involves various calls on the Drive API, it can take some time. Therefore, the operation is done asynchronously and the API response is empty.

A way to programmatically wait for a workspace to be created is to try getting the workspace resource thanks to the workspaces.getByName endpoint at regular intervals:

Endpoint: workspaces.getByName

import time

found = False
while not found:
    results = client.workspaces().getByName(
        api_key=API_KEY,
        domain="mydomain.com",
        actionUser="admin@mydomain.com",
        workspaceName="MyProjects"
    )
    if "workspaces" in results:
        my_projects_workspace = results["workspaces"][0]
        found = True
    else:
        time.sleep(10)
            

The snippet of code above tries getting the workspace we just inserted thanks to its name "MyProjects". the workspaces.getByName endpoint returns a list of workspaces having the given name (multiple workspaces can have the same name) in the workspaces field. We test whether the field is set (at least one workspace found) or not (no workspace found) and if that's the case, we take the first element of the list and end the loop. Otherwise, we wait 10 seconds before looping.

It shouldn't take more than a few iterations before obtaining the workspace.

Make Powertools manage a native Drive file

The next step is creating a Drive file into the workspace and making it managed by Powertools. Creating a file into the workspace is not an operation supported by Powertools but rather by Drive itself. When you have a workspace resource, the field rootFolderId contains the ID of the Drive folder that materilizes the workspace. Using this ID, you can then use the Drive API to insert a file into the folder.

At this point, we assume that you have inserted a file into the workspace. We assume that the workspace ID is WORKSPACE_ID and the file ID is FILE_ID. You then can make Powertools manage this file using the files.configure endpoint:

Endpoint: files.configure

powertools_file = client.files().configure(
    api_key=API_KEY,
    domain="mydomain.com",
    actionUser="admin@mydomain.com",
    fileId=FILE_ID,
    workspaceId=WORKSPACE_ID
).execute()
            

Your file is now managed by Powertools and you can use all Powertools features (filetypes, metadata, tags, etc) on it.

Add a tag to the file

To start simple, we're going to add a tag to the file. A tag is a simple string that we associate to the file, and that will be available as a search criteria for the search.find endpoint.

Endpoint: files.tags.insert

client.files().tags().insert(
    api_key=API_KEY,
    domain="mydomain.com",
    actionUser="admin@mydomain.com",
    fileId=powertools_file["id"],
    body={
        'content': 'Specification'
    }
).execute()
            

Thanks to the powertools_file resource that we got by configuring the file, we retrieved the file ID in the id field (which happens to be the ID of the corresponding Drive file), and add a tag named "Specification" to this file.

Create a new file type, with an attribute

Tags are good, but we're going to create a whole new filetype for specifications, named "Specification". This filetype with have a piece of metadata attached to it named "Scope", whose value will be either "Functional" or "Technical" and which will be mandatory. This filetype will be available for all workspaces, but only product@mydomain.com will be able to edit this filetype and assign it to a file.

Attributes

In the API world of Powertools, pieces of metadata of a filetype are called attributes. One has to make the difference between a filetype's attribute, which describe the format, default value, etc of a piece of metadata associated to the filetype, and a file's attribute, which is an instantiation of the filetype's attribute for a specific file under this filetype.

Endpoint: fileTypes.insert

spec_file_type = client.fileTypes().insert(
    api_key=API_KEY,
    domain="mydomain.com",
    actionUser="admin@mydomain.com",
    body={
        "name": "Specification",
        "creator": "product@mydomain.com",
        "description": "Specifications for the product",
        "editors": ["product@mydomain.com"],
        "publishers": ["product@mydomain.com"],
        "attributes": [
            {
                "name": "Scope",
                "format": "list",
                "availableValues": [
                    "Functional",
                    "Technical"
                ],
                "status": "mandatory"
            }
        ]
    }
).execute()
            

We create the filetype with the format list, which means that the value of the attribute can be one item among a list of available values. Those values are given in the field availableValues

defaultValue v. availableValues

If the format of the attribute is a list format (list or mlist), the field defaultValue is ignored. Otherwise (any other format), the field availableValues is ignored.

Assign the filetype to the file

Now that we have our "Specification" filetype, we can assign it to our file. To do that, we need the file ID (powertools_file["id"]) and the filetype ID (spec_file_type["id"]).

Additionally, we're gonna give a value to the "Scope" attribute since it's a mandatory attribute. Arbitrarily, let's say we're gonna affect the "Technical" value. To do so, we're gonna need the attribute ID. The spec_file_type variable, being a filetype resource, conveniently contains a list of its attributes, so we can get the ID by looking at this list. Since attributes have unique names for a given filetype, we can't make any mistake:

Endpoint: files.setType

for attr in spec_file_type["attributes"]:
    if attr["name"] == "Scope":
        scope_attr_id = attr["id"]
        break

client.files().setType(
    api_key=API_KEY,
    domain="mydomain.com",
    actionUser="admin@mydomain.com",
    fileId=powertools_file["id"],
    fileTypeId=spec_file_type["id"],
    body={
        "attributesSetters": [
            {
                "attributeId": scope_attr_id,
                "value": "Technical"
            }
        ]
    }
).execute()
            

The body of the request contains an attributesSetters field which allows us to set a value to the filetype's attributes for this specific file.

Note that in this example, we explicitly gave the user to impersonate thanks to the actionUser field. Indded, we specifically stated that only product@mydomain.com could publish the filetype, but we made no assumption about who owns the file in Drive (the file's owner would have been impersonated user by default).

Submit a version of the file

Let's say that you work on the file (either using the Drive web interface or the Drive API) and you now want to create a version of it. To do so, you can use the files.versions.submit endpoint:

Endpoint: files.versions.submit

client.files().versions().submit(
    api_key=API_KEY,
    domain="mydomain.com",
    actionUser="admin@mydomain.com",
    fileId=powertools_file["id"],
).execute()
            

A version is a snapshot of the file as viewed by Powertools. This means that even if you switch the type of the file later, you'll still be able to see that the first version that you submitted was under the "Specification" filetype and that it had the "Scope" attribute set to "Technical", and so on.

Search for the file

The Powertools API features an advanced search endpoint (search.find) which is the API counterpart of the advanced search available in the Chrome extension. This endpoint is presented in details in its Reference. Here's how you would search for all files having a tag "Specification":

Endpoint: search.find

results = client.search().find(
    api_key=API_KEY,
    domain="mydomain.com",
    actionUser="admin@mydomain.com",
    body={
        "primitive": {
            "value": "Specification",
            "operator": "isin",
            "element": "tag"
        }
    } 
)
            

If the endpoint finds one or more results, then the result contains a files field containing the matched files.

Understanding pagination

Most endpoints that output lists of items, such as workspaces.list support pagination. When performing a request against such endpoint, you provide a maxResults parameters to indicates how many items you want to fetch. The reponse will contain at most that many items, but if there are more, it will also contain a nextPageToken field. You can then pass this token to a next call to the same endpoint in the pageToken field to obtain the next batch of items.

The following example illustrates the pagination process with workspaces. A first batch of 10 workspaces is fetched. And if there are more, a second batch if fetched as well. In an actual application, we can imagine that bewteen the two fetches, there could be a user interaction such as clicking on "More" to expand a list.

Endpoint: workspaces.list

# Retrieving a first batch a results
result = client.workspaces.list(
    api_key=SECRET_API_KEY,
    domain=MY_DOMAIN,
    actionUser="admin@mydomain.com",
    maxResults=10
).execute()

# Getting the token for the next batch, if any
next_page_token = result.get("nextPageToken")

# If there are more results, fetch another batch
if next_page_token is not None:
next_result = client.workspaces.list(
    api_key=SECRET_API_KEY,
    domain=MY_DOMAIN,
    actionUser="admin@mydomain.com",
    maxResults=10,
    pageToken=next_page_token
).execute
            

Handling API Errors

The API responds with the HTTP code 200 when the operation is successful. If the operation couldn't be carried out with success, it uses another HTTP code and the response's body contains a human-readable message indicating what went wrong. Instances of the API responding with an error are, for example, requesting a resource which was not found (code 404) or using a wrong format to submit a new resource (code 400).

In the reference, each endpoint contains a Response Messages category which lists the errors that this endpoint might repond with, and the associated reasons. In addition to the per-endpoint errors, there are a few errors that the API might raise for any endpoint. Those are:

  • 401 Unauthorized if the API key is invalid or has the wrong scope for the endpoint
  • 400 Bad Request if
    • actionUser wasn't found in the database
    • The caller is performing more than 1 request per second
    • The daily quota for the key have been exceeded

The last two errors relate to the quota the API is subject to. Each API key shouldn't be used to perform more than 1 request per second, and is subject to a daily quota, which defaults to 2000 requests per day.

Here's an example of the JSON response's body in case of an HTTP error:

Endpoint: fileTypes.get

{
 "error": {
  "code": 404, 
  "errors": [
   {
    "domain": "global", 
    "message": "File type not found", 
    "reason": "notFound"
   }
  ], 
  "message": "File type not found"
 }
}
            

What's next

Go the the reference to discover all API endpoints