google-ads-node icon indicating copy to clipboard operation
google-ads-node copied to clipboard

Project structure question

Open felixmosh opened this issue 3 years ago • 2 comments

Hi, I've some time to invest in order to optimize this lib so it will be more suitable for production use (#83 #87 #84)

But I'm not getting the structure of it. I did understood that it gets generated from googles protobuf defs.

  1. What is the relation between build/src folder and the build/protos/protos.js file?
  2. Do we need build/src on production usage? (is google-ads-api uses it?)

Any help will be really appreciated.

felixmosh avatar Jun 13 '22 14:06 felixmosh

The problem is how googleapis builds the protos. It builds it into a single monolithic proto, which is then built into a single Typescript file.

To avoid this, I recommend:

  1. Building the protos with protobuf-ts
  2. Running it with @grpc/grpc-js
  3. Not using this library at all

Building the protos

Here's a file to checkout the googleapis project and build the protos to a folder:

#!/bin/bash

GOOGLE_APIS_PATH="$1"
OUTPUT_DIR="$2"

if [ ! -e "$GOOGLE_APIS_PATH/.git" ]; then
  echo 'Cloning Git repository'
  git clone [email protected]:googleapis/googleapis.git "$GOOGLE_APIS_PATH"
fi

cd "$GOOGLE_APIS_PATH"

if [ ! -e "$GOOGLE_APIS_PATH/node_modules/@protobuf-ts/plugin" ]; then
  echo 'Installing protocol buffer compiler'
  # Install the protoc compiler temporarily in googleapis. We only do this
  # if it doesn't look installed to make rerunning this script faster
  git checkout . && git clean -f -d
  git fetch
  git reset --hard origin/master

  # Add the plugin needed to build googleApisProtos into Typescript files
  yarn init -y
  yarn add @protobuf-ts/plugin
fi

yarn protoc --ts_out "$OUTPUT_DIR" --proto_path . \
  --ts_opt generate_dependencies,long_type_number \
  google/ads/googleads/v11/services/customer_service.proto \
  google/ads/googleads/v11/services/conversion_upload_service.proto

Using the protos

These options will be needed for any endpoint:

import { UserRefreshClient } from 'google-auth-library';
import { credentials as gc } from '@grpc/grpc-js';
import { GrpcTransport } from '@protobuf-ts/grpc-transport';
import { RpcOptions } from '@protobuf-ts/runtime-rpc';

const config = { /* ... */ };

/**
 * These options include required options from Google Ads API documentation on
 * headers:
 * https://developers.google.com/google-ads/api/rest/auth#request_headers
 */
export const googleAdsRpcOptions: RpcOptions = {
  meta: {
    'developer-token': config.developerToken,
    'login-customer-id': config.customerId,
  },
};

/**
 * Creates a GrpcTransport with the signed-in credentials that can be used to
 * access the APIs.
 */
export function getGoogleAdsTransport() {
  const { refreshToken, clientId, clientSecret } = config;

  // Code to create the client and credentials were heavily based on:
  //
  // - google-ads-node library: https://github.com/Opteo/google-ads-api/blob/v11/src/service.ts#L71
  // - GRPC credentials documentation: https://grpc.github.io/grpc/node/grpc.credentials.html
  const authClient = new UserRefreshClient(
    clientId,
    clientSecret,
    refreshToken,
  );

  const credentials = gc.combineChannelCredentials(
    gc.createSsl(),
    gc.createFromGoogleCredential(authClient),
  );

  return new GrpcTransport({
    // URL was found in https://raw.githubusercontent.com/googleapis/googleapis/master/api-index-v1.json
    // Just do a find for "google/ads/googleads" to find the relevant
    // configuration
    host: 'googleads.googleapis.com',
    channelCredentials: credentials,
  });
}

Using the APIs

Pass in the transport to the constructor for the client, and pass in the options to

new CustomerServiceClient(getGoogleAdsTransport()).listAccessibleCustomers({}, googleAdsRpcOptions);

Additional notes

Note that the errors.proto is also huge, so if you only need to use it, you may want to edit it and reduce its size.

hsource avatar Sep 21 '22 20:09 hsource

Thank you @hsource , I think that suggesting to ditch the lib entirely is valid, but in my case it will require much more time to refactor the code since I have a heavy usage of this lib.

Maybe you can somehow incorporate your solution into this lib?

felixmosh avatar Oct 13 '22 16:10 felixmosh