Google APIs Client Library for C++ A C++ library for client applications to access Google APIs.
Jumping In

This document provides all the basic information you need to start using the library. It covers important library concepts, shows examples for various use cases, and gives links to more information.

Contents

  1. Preparing to use the SDK
  2. Background Information
  3. A Basic Example (Calendar)

Preparing to use the SDK

Before jumping into a basic example there are some preparations that you should make.

  1. Create a Google Account if you do not already have one for other Google products or services (such as Gmail).
  2. Download, build, and install the C++ Client.
  3. Register a project for the sample as explained below.

Most interesting applications access private user data. The example in this document reads and updates users' private calendars. The cloud services protect that information by requiring applications to prove that the user is authorizing access to this information. How this happens is not important for this quick introduction. However as part of that security mechanism, applications must be registered with the Google Cloud Platform in order to even try to obtain the users permission. Therefore in order for you to run the example presented below, you need to register it as a client.

Authorization is discussed further in the document Authentication and Authorization Overview. The following steps are sufficient to jump in:

  1. Go to the Google APIs Console.
  2. Click Create Project.
    • If you already have projects then select Create from the bottom of the products dropdown.
    • Enter a name of your choosing, such as Calendar Sample.
  3. Select the API Access pane.
  4. Click the Create an OAuth 2.0 client ID button.
  5. Enter a product name, such as Google APIs Calendar Sample.
    • You can leave the other information empty.
    • You will be able to edit/change this information later.
  6. Select the Installed application application type.
  7. Click the Create client ID button.
  8. Select the Download JSON link on the right hand side.
    • Rename this file to something such as calendar_sample_secrets.json so as not to confuse it with others you may download in the future.
    • Change the permissions of the file so that it is only readable by you. In UNIX you can set the correct permissions with the command chmod 400 calendar_sample_secrets.json.
  9. Select the Service pane.
  10. Click the toggle for the Calendar API to turn it on.
You now have a client registered with the Google Cloud Platform that is allowed to access the Calendar API. The code in the remainder of this tutorial will operate as that registered client.

Background Information

The Google Cloud Services available for your clients to interact with typically use a REST architectural style. For purposes of the tutorial, think of this as interacting with a web server. Each operation is an individual GET or POST into the server's global state. When you create, modify, ordelete something, anyone (with permissions) will generally see the effect the next time they access the resource. Likewise you will see the effect of other peoples interactions (if they have permissions). There are not normally larger transactions or modes requiring a "save" operation to finalize them.

Services written for the Google Cloud Platform publish their APIs using a Discovery Document. While not necessary for this tutorial, you may find it helpful to briefly browse the Getting Started section of the Google APIs Discovery Service, which provides an introduction and overview to how services present themselves and expect to be used. The Google APIs Client Library for C++ will automatically take care of many of the tedious details for interpreting and complying with the discovery documents so that you can write simpler and familiar C++ code. However at some point understanding the Discovery Service will help you understand more specifically how your client is perceived by the servers that it is talking to.

A Basic Example (Calendar)

The remainder of this document walks through a very basic Sample Calendar program. The program is not particularly useful other than supporting this tutorial. It creates a calendar, makes queries and modifications to it, then deletes the calendar.

Sample Overview

The sample program makes use of the custom C++ API that was generated for the Google Calendar API. To generate APIs for other specific services on the Google Cloud Platform, see the document Generating Service Libraries.

The sample is written as a single encapsulated class. Normal applications are not necessarily written this way. The samples use this style to make them eaiser to read, follow, and account for all the parts. The class was specifically designed for the tutorial with each method being a different part in the tutorial.

The tutorial follows the flow of the application. The most complicated parts of an application are scafolding and configuring it. Unfortunately this means that the first step or two of the tutorial is atypical of most application code and presents a larger number of different pieces, some of which will not be heard from again. If you are just looking for the gist of calling services then you might want to jump right into the application parts, such as Add a Calendar, then come back to Initializing the SDK Runtime later.

class CalendarSample {
 public:
  static util::Status Startup(int argc, char* argv[]);
  void Run();

 private:
  // Gets authorization to access the user's personal calendar data.
  util::Status Authorize();

  // Prints some current calendar data to the console to show the effects
  // from the calls that the sample has made.
  void ShowCalendars();

  // Demonstrates adding a new resource. For this example, it is a calendar.
  // Returns a proxy to the calendar added in the Calendar Service (cloud).
  // We only really need the ID so that's all we return.
  string AddCalendar();

  // Demonstrates adding an embedded resource.
  // For this example it is a calendar event.
  //
  // This takes a calendar ID such as that returned by AddCalendar().
  // We'll take a configured event as input and modify its ID with the
  // ID assigned by the Calendar Service (cloud). We could have just returned
  // the ID as in the case of Calendar but we are doing it differently for
  // demonstration purposes.
  void AddEvent(const string& calendar_id, Event* event);

  // Demonstrates using a ServiceRequestPager to list all the events on the
  // given calendar.
  void PageThroughAllEvents(const string& calendar_id, int events_per_page);

  // Demonstrates deleting a resource. For this example, it is a calendar.
  // We are deleting the calendar in the Calendar Service (cloud) that
  // has the given calendar_id.
  void DeleteCalendar(const string& calendar_id);

  // Demonstrates getting a resource.
  // For this example, it is a calendar event.
  // Returns the final status for the request. If not ok() then event wasn't
  // populated.
  util::Status GetEvent(
      const string& calendar_id, const StringPiece& event_id, Event* event);

  // Demonstrates patching a resource.
  // For this example, it is a calendar event.
  void PatchEvent(const string& calendar_id, const Event& event);

  // Demonstrates updating a resource.
  // For this example, it is a calendar event.
  void UpdateEvent(const string& calendar_id, const Event& event);

  OAuth2Credential credential_;
  static scoped_ptr<CalendarService> service_;
  static scoped_ptr<OAuth2AuthorizationFlow> flow_;
  static scoped_ptr<HttpTransportLayerConfig> config_;
};

Initializing the SDK Runtime

The current release of the SDK libraries require a few explicit configurations in each application. In addition, for illustration purposes, this sample keeps an instance of the CalendarService object and authorization flow for it. It initializes all these together. Configuration is discussed in more detail in the document Configuring the HTTP Transport Layer

util::Status CalendarSample::Startup(int argc, char* argv[]) {
  if ((argc < 2) || (argc > 3)) {
    string error =
        StrCat("Invalid Usage:\n",
               argv[0], " <client_secrets_file> [<cacerts_path>]\n");
    return StatusInvalidArgument(error);
  }

  // Set up HttpTransportLayer.
  util::Status status;
  config_.reset(new HttpTransportLayerConfig);
  googleapis_client::HttpTransportFactory* factory =
      new googleapis_client::CurlHttpTransportFactory(config_.get());
  config_->ResetDefaultTransportFactory(factory);
  if (argc > 2) {
    config_->mutable_default_transport_options()->set_cacerts_path(argv[2]);
  }

  // Set up OAuth 2.0 flow for getting credentials to access personal data.
  const StringPiece client_secrets_file = argv[1];
  flow_.reset(OAuth2AuthorizationFlow::MakeFlowFromClientSecretsPath(
      client_secrets_file, config_->NewDefaultTransportOrDie(), &status));
  if (!status.ok()) return status;

  flow_->set_default_scopes(CalendarService::SCOPES::CALENDAR);
  flow_->mutable_client_spec()->set_redirect_uri(
      OAuth2AuthorizationFlow::kOutOfBandUrl);
  flow_->set_authorization_code_callback(
      NewPermanentCallback(&PromptShellForAuthorizationCode, flow_.get()));

  string home_path;
  status = FileCredentialStoreFactory::GetSystemHomeDirectoryStorePath(
      &home_path);
  if (status.ok()) {
    FileCredentialStoreFactory store_factory(home_path);
    // Use a credential store to save the credentials between runs so that
    // we dont need to get permission again the next time we run. We are
    // going to encrypt the data in the store, but leave it to the OS to
    // protect access since we do not authenticate users in this sample.
#if HAVE_OPENSSL
    OpenSslCodecFactory* openssl_factory = new OpenSslCodecFactory;
    status = openssl_factory->SetPassphrase(
        flow_->client_spec().client_secret());
    if (!status.ok()) return status;
    store_factory.set_codec_factory(openssl_factory);
#endif

    flow_->ResetCredentialStore(
        store_factory.NewCredentialStore("CalendarSample", &status));
  }
  if (!status.ok()) return status;

  // Now we'll initialize the calendar service proxy that we'll use
  // to interact with the calendar from this sample program.
  HttpTransport* transport = config_->NewDefaultTransport(&status);
  if (!status.ok()) return status;

  service_.reset(new CalendarService(transport));
  return status;
}

This method touches on a few concepts.

  • The SDK conveys errors using the type googleapis::util::Status. The status type is discussed in the Error Status section of the Foundation Types document. The SDK does not throw exceptions.
  • Use the client secrets file you previously created in the section Preparing to use the SDK to configure the authorization flow. Hold onto the flow in an application global so it is available whenever you need to authorize a user.
    • Authorization is discussed in detail in the document Authentication and Authorization Overview
    • Our sample application is customizing the flow with some IO to std::cin and std::cout to obtain the initial access tokens. Normally applications would have a fancier user interface but would still use this same callback mechanism.
      static util::Status PromptShellForAuthorizationCode(
          OAuth2AuthorizationFlow* flow,
          const OAuth2RequestOptions& options,
          string* authorization_code) {
        string url = flow->GenerateAuthorizationCodeRequestUrlWithOptions(options);
        cout << "Enter the following URL into a browser:\n" << url << endl;
        cout << endl;
        cout << "Enter the browser's response to confirm authorization: ";
      
        authorization_code->clear();
        cin >> *authorization_code;
        if (authorization_code->empty()) {
          return StatusCanceled("Canceled");
        } else {
          return StatusOk();
        }
      }
      
    • The snippet associates a CredentialStore with the authorization flow. This is optional and acts as a cache so that users will not have to authorize the application every time they run it. They only need to authorize it the first time or after they revoke access. In this case credentials are stored in unencrypted OS-protected files.
      • Recall that you registered this as (and are running it as) an Installed Application. This means that the application was installed to a device and is running on behalf of a particular user. In this case you have installed it on your computer and are running it for your Google Account.

Getting User Authorization

User authorization is encoded in OAuth2Credential objects. To better understand authorization and credentials, see the document Authentication and Authorization Overview. The client library simplifies this greatly, though it is still not trivial.

The following function is how the sample application obtains authorization. It is deceptively simple. Hidden beneath the covers is the callback that was bound to the flow earlier when constructed in the Initializing the SDK Runtime section (PromptShellForAuthorizationCode).

  1. When asking the flow to RefreshCredentialWithOptions initially, it will call that function to get the user's permission.
    • This is in the form of an provided by OAuth 2.0.
    • If you bound a CredentialStore to the flow, then the store is consulted first before even asking the user. If it finds previous permissions then it will automatically proceed with those, refreshing the access token if it expired.
  2. The flow can then turn this authorization code into an active access token and persistent refresh token and pass them in the credential object.
    • If you bound a CredentialStore to the flow then it will write the credentials to the store for future reference, such as the next time the program is run.

    util::Status CalendarSample::Authorize() {
      cout
        << endl
        << "Welcome to the Google APIs for C++ CalendarSample.\n"
        << "  You will need to authorize this program to look at your calendar.\n"
        << "  If you would like to save these credentials between runs\n"
        << "  (or restore from an earlier run) then enter a User ID.\n"
        << "  Otherwise just press return.\n"
        << endl
        << "  ID: ";
      string id;
      std::getline(cin, id);
      if (!id.empty()) {
        util::Status status = ValidateUserName(id);
        if (!status.ok()) {
          return status;
        }
      }
    
      OAuth2RequestOptions options;
      options.user_id = id;
      util::Status status =
            flow_->RefreshCredentialWithOptions(options, &credential_);
      if (!status.ok()) {
        return status;
      }
    
      credential_.set_flow(flow_.get());
      cout << "Authorized " << id << endl;
      return StatusOk();
    }
    

    The ValidateUserId call is part of this sample, not part of the SDK. The following is its implementation for this tutorial.

    static util::Status ValidateUserName(const string& name) {
      if (name.find("/") != string::npos) {
        return StatusInvalidArgument("UserNames cannot contain '/'");
      } else if (name == "." || name == "..") {
        return StatusInvalidArgument(
            StrCat("'", name, "' is not a valid UserName"));
      }
      return StatusOk();
    }
    

Adding a Server Data

To add server data, such as a new calendar or events on an existing calendar, you will issue Insert operations on the service resource managing the type of data. The sample application's AddCalendar method inserts a new Calendar. This sample adds a new calendar so as not to interfere with any calendars you might already be using.

string CalendarSample::AddCalendar() {
  scoped_ptr<Calendar> calendar(Calendar::New());
  calendar->set_summary("Calendar added by CalendarSample");

  scoped_ptr<CalendarsResource_InsertMethod> method(
      service_->get_calendars().NewInsertMethod(&credential_, *calendar));

  if (!method->ExecuteAndParseResponse(calendar.get()).ok()) {
    DisplayError(method.get());
    return "";
  }

  string result = calendar->get_id().as_string();
  cout << "Added new calendar ID=" << result << ":" << endl;
  Display("  ", *calendar);
  cout << endl;

  return result;
}

There are a few things worth noting in the above example.

  • To invoke a method, create a method object, then execute it. The methods on the CalendarService object are in support of creating these method objects. They do not interact with the Cloud service. Only the method objects do (when their Execute method is called).
  • All methods that operate on private data take an OAuth2Credential that proves authorization to access that data. The credential is always the first argument to a method.
  • Data objects, such as the Calendar, are usually dynamic objects allocated on the heap rather than on the stack. For synchronous methods such as the above example this is not required. However it is convenient because data objects usually do not have default constructors. When allocating dynamic data objects, use the static New methods explicitly provided by the data classes.
  • The insert method returns a copy of the object inserted. This is similar to the object passed to Insert but contains some additional attributes, notably the calendar_id attribute. It is this ID attribute, created by the server, that use required to refer to the object that was just inserted.
    • Note also that a calendar was passed as a const value to insert, and a separate copy was returned by the response. This example uses the same instance to write the result into, but the API does not make that assumption. This is true for all methods created by the code generator complementing the Google APIs Client Library for C++.

The following example inserts an event into a calendar, such as the one created in the previous example.

void CalendarSample::AddEvent(const string& calendar_id, Event* event) {
  scoped_ptr<EventsResource_InsertMethod> method(
      service_->get_events().NewInsertMethod(
          &credential_, calendar_id, *event));

  if (!method->ExecuteAndParseResponse(event).ok()) {
    DisplayError(method.get());
    return;
  }

  cout << "Added new event ID=" << event->get_id() << ":" << endl;
  Display("  ", *event);
  cout << endl;
}

There are a few things to note in this example as well:

  • The general pattern for adding an event is very similar to that used for adding a calendar. Type number and types of parameters differ, but the methods look very similar. This is not a coincidence; it will hold for other REST-oriented Google Services as well.
  • Both these methods were called _InsertMethod but they came from different resources.
  • You do not need the Calendar object to insert events into it. You only need the calendar_id attribute.
    • The actual calendar lives in the cloud on the Calendar Service's server. The Calendar object in the client's API is just a simple data object containing a snapshot. It is not necessarily current nor complete.

For purposes of illustration, the AddEvent method in the sample might be called with an event as follows to create an event that starts now and will last for the next hour.

DateTime now;
scoped_ptr<Event> event(Event::New());
event->set_summary("Calendar event added by CalendarSample");
event->mutable_start().set_date_time(now);
event->mutable_end().set_date_time(DateTime(now.ToEpochTime() + 60 * 60));

cout << endl << kSampleStepPrefix << "Add Calendar Event" << endl;
AddEvent(calendar_id, event.get());

Retrieving Server Data

There are usually two types of requests for reading data out of a server. The first of these is a List request which returns a list of objects, in this case the different calendars for the user.

void CalendarSample::ShowCalendars() {
  scoped_ptr<CalendarListResource_ListMethod> method(
      service_->get_calendarList().NewListMethod(&credential_));

  scoped_ptr<CalendarList> calendar_list(CalendarList::New());
  if (!method->ExecuteAndParseResponse(calendar_list.get()).ok()) {
    DisplayError(method.get());
    return;
  }
  DisplayList<CalendarList, CalendarListEntry>(
      "", "CalendarList", *calendar_list);
  cout << endl;
}

The second common pattern for reading data out of a server is through a Get request on a specific resource instance. In this case the event instance is identified by an ID unique within a calendar's ID.

  • The custom service APIs generated for the Google APIs Client Library for C++ have method constructors that take the required parameters and offer attribute setters on the method object for optional parameters.

util::Status CalendarSample::GetEvent(
    const string& calendar_id, const StringPiece& event_id, Event* event) {
  scoped_ptr<EventsResource_GetMethod> method(
      service_->get_events().NewGetMethod(
          &credential_, calendar_id, event_id));

  return method->ExecuteAndParseResponse(event);
}

Patching Server Data

The term Patching means updating only specific parts of attributes of a data resource. The following example patches a calendar event.

void CalendarSample::PatchEvent(
    const string& calendar_id, const Event& event) {
  scoped_ptr<EventsResource_PatchMethod> method(
      service_->get_events().NewPatchMethod(
          &credential_, calendar_id, event.get_id(), event));

  if (!method->Execute().ok()) {
    DisplayError(method.get());
    return;
  }

  scoped_ptr<Event> cloud_event(Event::New());
  util::Status status =
        GetEvent(calendar_id, event.get_id(), cloud_event.get());
  if (status.ok()) {
    cout << "Patched event:" << endl;
    Display("  ", *cloud_event);
  } else {
    cout << "** Could not get patched event: "
         << status.error_message() << endl;
  }
  cout << endl;
}

For purposes of illustration, the PatchEvent method in the sample might be called with an event as follows to patch the event you just created so that it has a different summary but leaves the rest of the attributes (start and stop time) intact.

cout << endl << kSampleStepPrefix << "Patch Calendar Event" << endl;
event->clear_start();
event->clear_end();
event->set_summary("Event patched by CalendarSample");
PatchEvent(calendar_id, *event);

Replacing Server Data

The term Updating means updating all the attributes of a data resource. The following example updates a calendar event. Note that this is syntactically virtually identical to a patch, however semantically very different.

void CalendarSample::UpdateEvent(
    const string& calendar_id, const Event& event) {
  scoped_ptr<EventsResource_UpdateMethod> method(
      service_->get_events().NewUpdateMethod(
          &credential_, calendar_id, event.get_id(), event));

  if (!method->Execute().ok()) {
    DisplayError(method.get());
    return;
  }

  scoped_ptr<Event> cloud_event(Event::New());
  util::Status status =
        GetEvent(calendar_id, event.get_id(), cloud_event.get());
  if (status.ok()) {
    cout << "Updated event:" << endl;
    Display("  ", *cloud_event);
  } else {
    cout << "** Could not get updated event: "
         << status.error_message() << endl;
  }
  cout << endl;
}

For purposes of illustration, the UpdateEvent method in the sample might be called with an event as follows to update the event so that it has a different start and end time. Because the snippet does not also include a summary, the summary on the existing event will be erased.

cout << endl << kSampleStepPrefix << "Update Calendar Event" << endl;
// An update requires a time.
// Go back a year and one day to distinguish it from the old value.
event->mutable_start().set_date_time(
    DateTime(now.ToEpochTime() - 60 * 60 * 24 * 367));
event->mutable_end().set_date_time(
    DateTime(now.ToEpochTime() - 60 * 60 * 24 * 366));
event->clear_summary();
UpdateEvent(calendar_id, *event);

Deleting Server Data

To delete server data, issue a Delete request with the identifier of the object that you wish to delete. The following example deletes a calendar. Since the calendar owns its entries, this will also delete the entries on the calendar. The sample will delete the calendar it created earlier to clean up after itself.

void CalendarSample::DeleteCalendar(const string& id) {
  scoped_ptr<CalendarsResource_DeleteMethod> method(
      service_->get_calendars().NewDeleteMethod(&credential_, id));

  if (!method->Execute().ok()) {
    DisplayError(method.get());
    return;
  }
  cout << "Deleted ID=" << id << endl;
  cout << endl;
}