{"action":"created","comment":{"url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls/comments/150361599","pull_request_review_id":75906737,"id":150361599,"diff_hunk":"@@ -13,47 +13,387 @@\n  * See the License for the specific language governing permissions and\n  * limitations under the License.\n  */\n+import * as http from 'http';\n import * as path from 'path';\n+import * as pify from 'pify';\n+import * as zlib from 'zlib';\n \n-import {AuthenticationConfig, Common, Logger, Service, ServiceConfig} from '../third_party/types/common-types';\n-\n-import {Config, ProfilerConfig} from './config';\n+import {perftools} from '../../proto/profile';\n+import {AuthenticationConfig, Common, Logger, Service, ServiceConfig, ServiceObject, ServiceObjectConfig} from '../third_party/types/common-types';\n+import {ProfilerConfig} from './config';\n import {HeapProfiler} from './profilers/heap-profiler';\n import {TimeProfiler} from './profilers/time-profiler';\n \n+export const common: Common = require('@google-cloud/common');\n const pjson = require('../../package.json');\n-const common: Common = require('@google-cloud/common');\n+const API = 'https://cloudprofiler.googleapis.com/v2';\n+const zoneNameLabel = 'zone';\n+const versionLabel = 'version';\n+const instanceLabelName = 'instance';\n+const scope = 'https://www.googleapis.com/auth/monitoring.write';\n+const gzip = pify(zlib.gzip);\n+\n+enum ProfileTypes {\n+  Wall = 'WALL',\n+  Heap = 'HEAP'\n+}\n+\n+/**\n+ * Parses string containing a time duration, and returns this duration in ms.\n+ * If duration cannot be parsed, returns undefined.\n+ *\n+ * Public for testing.\n+ */\n+export function parseDurationMillis(durationString: string): number|undefined {\n+  if (!/(d+h)?(d+m)?(d+s)?(d+ms)?(d+us)?(d+ns)?/.test(durationString)) {\n+    return undefined;\n+  }\n+  const values = durationString.match(/\\d+(h|m(?!s)|s|ms|us|ns)/g);\n+  if (values == null) {\n+    return undefined;\n+  }\n+  let durationMillis = 0;\n+  values.forEach(element => {\n+    const digit = element.match(/\\d+/);\n+    const unit = element.match(/\\D+/);\n+    if (unit && digit) {\n+      const num = Number(digit[0]);\n+      switch (unit[0]) {\n+        case 'h':\n+          durationMillis = durationMillis + num * 60 * 60 * 1000;\n+          break;\n+        case 'm':\n+          durationMillis = durationMillis + num * 60 * 1000;\n+          break;\n+        case 's':\n+          durationMillis = durationMillis + num * 1000;\n+          break;\n+        case 'ms':\n+          durationMillis = durationMillis + num;\n+          break;\n+        case 'us':\n+          durationMillis = durationMillis + num / 1000;\n+          break;\n+        case 'ns':\n+          durationMillis = durationMillis + num / (1000 * 1000);\n+          break;\n+        default:\n+          break;\n+      }\n+    }\n+  });\n+  return durationMillis;\n+}\n+\n+/**\n+ * Returns true if http status code indicates an error.\n+ */\n+function isErrorResponseStatusCode(code: number) {\n+  return code < 200 || code >= 300;\n+}\n \n-// TODO: finish implementing Profiler.\n-// TODO: add stop() method to stop profiling.\n-export class Profiler {\n-  config: ProfilerConfig;\n-  logger: Logger;\n-  service: Service;\n+/**\n+ * Interface for deployment field of RequestProfile. Profiles with matching\n+ * deployments will be grouped together.\n+ * Used as body of request when creating profile using the profiler API.\n+ *\n+ * Public for testing.\n+ */\n+export interface Deployment {\n+  projectId?: string;\n+  target?: string;\n+  labels?: {zone?: string, version?: string};\n+}\n+\n+/**\n+ * Interface for body of response from profiler API when creating\n+ * profile and used as body of request to profiler API when\n+ * uploading a profile.\n+ *\n+ * Public for testing.\n+ */\n+export interface RequestProfile {\n+  name: string;\n+  profileType?: string;\n+  duration: string;\n+  profileBytes?: string;\n+  deployment?: Deployment;\n+  labels?: {instance: string};\n+}\n+\n+/**\n+ * Returns true if an prof is a RequestProfile.\n+ */\n+// tslint:disable-next-line: no-any\n+function isRequestProfile(prof: any): prof is RequestProfile {\n+  return prof && typeof prof.name === 'string' &&\n+      typeof prof.profileType === 'string' && typeof prof.duration === 'string';\n+}\n+\n+/**\n+ * Returns true if response has statusCode.\n+ */\n+// tslint:disable-next-line: no-any\n+function hasHttpStatusCode(response: any):\n+    response is {statusCode: number, statusMessage: string} {\n+  return response && typeof response.statusCode === 'number';\n+}\n+\n+/**\n+ * Converts a profile to a compressed, base64 encoded string.\n+ *\n+ * @param p - profile to be converted to string.\n+ */\n+async function profileBytes(p: perftools.profiles.IProfile): Promise<string> {\n+  const pwriter = perftools.profiles.Profile.encode(p);\n+  const buffer = new Buffer(pwriter.finish());\n+  const gzBuf = await gzip(buffer);\n+  return gzBuf.toString('base64');\n+}\n+\n+/**\n+ * Polls profiler server for instructions on behalf of a task and\n+ * collects and uploads profiles as requested\n+ */\n+export class Profiler extends common.ServiceObject {\n+  private config: ProfilerConfig;\n+  private logger: Logger;\n+  private profileLabels: {instance: string};\n+  private deployment: Deployment;\n+  private profileTypes: string[];\n+\n+  // Public for testing.\n+  timeProfiler: TimeProfiler|undefined;\n+  heapProfiler: HeapProfiler|undefined;\n \n   constructor(config: ProfilerConfig) {\n-    this.config = config;\n+    config = common.util.normalizeArguments(null, config);\n+    const serviceConfig = {\n+      baseUrl: API,\n+      scopes: [scope],\n+      packageJson: pjson,\n+    };\n+    super({parent: new common.Service(serviceConfig, config), baseUrl: '/'});\n \n-    this.service = new common.Service(\n-        {\n-          baseUrl: 'https://cloudprofiler.googleapis.com/v2',\n-          scopes: ['https://www.googleapis.com/auth/monitoring.write'],\n-        },\n-        this.config);\n+    this.config = config;\n \n     this.logger = new common.logger({\n       level: common.logger.LEVELS[config.logLevel as number],\n       tag: pjson.name\n     });\n+\n+    this.deployment = {\n+      projectId: this.config.projectId,\n+      target: this.config.serviceContext.service,\n+      labels: {\n+        zone: this.config.zone,\n+        version: this.config.serviceContext.version\n+      }\n+    };\n+\n+    this.profileLabels = {instance: this.config.instance};\n+\n+    this.profileTypes = [];\n+    if (!this.config.disableTime) {\n+      this.profileTypes.push(ProfileTypes.Wall);\n+      this.timeProfiler = new TimeProfiler(this.config.timeIntervalMicros);\n+    }\n+    if (!this.config.disableHeap) {\n+      this.profileTypes.push(ProfileTypes.Heap);\n+      this.heapProfiler = new HeapProfiler(\n+          this.config.heapIntervalBytes, this.config.heapMaxStackDepth);\n+    }\n+  }\n+\n+  /**\n+   * Starts and endless loop to poll profiler server for\n+   * instructions, and collects and uploads profiles as requested.\n+   * If there is a problem when collecting a profile or uploading a profile to\n+   * profiler server, this problem will be logged at the debug level and\n+   * otherwise ignored.\n+   * If there is a problem polling profiler server for instructions\n+   * on the type of profile created, this problem will be logged and profile\n+   * creation will be retried.\n+   * This promise will not resolve while profiling is still occurring. This\n+   * promise should not ever be resolved or rejected.","path":"ts/src/profiler.ts","position":223,"original_position":223,"commit_id":"77e7a402495a05880ee94deb0edfaf67069453e9","original_commit_id":"77e7a402495a05880ee94deb0edfaf67069453e9","user":{"login":"ofrobots","id":79017,"avatar_url":"https://avatars1.githubusercontent.com/u/79017?v=4","gravatar_id":"","url":"https://api.github.com/users/ofrobots","html_url":"https://github.com/ofrobots","followers_url":"https://api.github.com/users/ofrobots/followers","following_url":"https://api.github.com/users/ofrobots/following{/other_user}","gists_url":"https://api.github.com/users/ofrobots/gists{/gist_id}","starred_url":"https://api.github.com/users/ofrobots/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ofrobots/subscriptions","organizations_url":"https://api.github.com/users/ofrobots/orgs","repos_url":"https://api.github.com/users/ofrobots/repos","events_url":"https://api.github.com/users/ofrobots/events{/privacy}","received_events_url":"https://api.github.com/users/ofrobots/received_events","type":"User","site_admin":false},"body":"Let's not return a promise (or anything else) then.\r\n\r\nThe comment above needs to be updated. It no longer matches the implementation.","created_at":"2017-11-10T23:46:23Z","updated_at":"2017-11-10T23:49:46Z","html_url":"https://github.com/GoogleCloudPlatform/cloud-profiler-nodejs/pull/16#discussion_r150361599","pull_request_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls/16","author_association":"COLLABORATOR","_links":{"self":{"href":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls/comments/150361599"},"html":{"href":"https://github.com/GoogleCloudPlatform/cloud-profiler-nodejs/pull/16#discussion_r150361599"},"pull_request":{"href":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls/16"}}},"pull_request":{"url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls/16","id":147698308,"html_url":"https://github.com/GoogleCloudPlatform/cloud-profiler-nodejs/pull/16","diff_url":"https://github.com/GoogleCloudPlatform/cloud-profiler-nodejs/pull/16.diff","patch_url":"https://github.com/GoogleCloudPlatform/cloud-profiler-nodejs/pull/16.patch","issue_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/issues/16","number":16,"state":"open","locked":false,"title":"feat: add communications with Stackdriver Profiler API","user":{"login":"nolanmar511","id":14046562,"avatar_url":"https://avatars0.githubusercontent.com/u/14046562?v=4","gravatar_id":"","url":"https://api.github.com/users/nolanmar511","html_url":"https://github.com/nolanmar511","followers_url":"https://api.github.com/users/nolanmar511/followers","following_url":"https://api.github.com/users/nolanmar511/following{/other_user}","gists_url":"https://api.github.com/users/nolanmar511/gists{/gist_id}","starred_url":"https://api.github.com/users/nolanmar511/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nolanmar511/subscriptions","organizations_url":"https://api.github.com/users/nolanmar511/orgs","repos_url":"https://api.github.com/users/nolanmar511/repos","events_url":"https://api.github.com/users/nolanmar511/events{/privacy}","received_events_url":"https://api.github.com/users/nolanmar511/received_events","type":"User","site_admin":false},"body":"","created_at":"2017-10-20T00:58:12Z","updated_at":"2017-11-10T23:49:46Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b4b2d899d0df6986bde2528139ef79ddc830e760","assignee":null,"assignees":[],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls/16/commits","review_comments_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls/16/comments","review_comment_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls/comments{/number}","comments_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/issues/16/comments","statuses_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/statuses/77e7a402495a05880ee94deb0edfaf67069453e9","head":{"label":"nolanmar511:create-and-upload","ref":"create-and-upload","sha":"77e7a402495a05880ee94deb0edfaf67069453e9","user":{"login":"nolanmar511","id":14046562,"avatar_url":"https://avatars0.githubusercontent.com/u/14046562?v=4","gravatar_id":"","url":"https://api.github.com/users/nolanmar511","html_url":"https://github.com/nolanmar511","followers_url":"https://api.github.com/users/nolanmar511/followers","following_url":"https://api.github.com/users/nolanmar511/following{/other_user}","gists_url":"https://api.github.com/users/nolanmar511/gists{/gist_id}","starred_url":"https://api.github.com/users/nolanmar511/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nolanmar511/subscriptions","organizations_url":"https://api.github.com/users/nolanmar511/orgs","repos_url":"https://api.github.com/users/nolanmar511/repos","events_url":"https://api.github.com/users/nolanmar511/events{/privacy}","received_events_url":"https://api.github.com/users/nolanmar511/received_events","type":"User","site_admin":false},"repo":{"id":100729494,"name":"cloud-profiler-nodejs","full_name":"nolanmar511/cloud-profiler-nodejs","owner":{"login":"nolanmar511","id":14046562,"avatar_url":"https://avatars0.githubusercontent.com/u/14046562?v=4","gravatar_id":"","url":"https://api.github.com/users/nolanmar511","html_url":"https://github.com/nolanmar511","followers_url":"https://api.github.com/users/nolanmar511/followers","following_url":"https://api.github.com/users/nolanmar511/following{/other_user}","gists_url":"https://api.github.com/users/nolanmar511/gists{/gist_id}","starred_url":"https://api.github.com/users/nolanmar511/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nolanmar511/subscriptions","organizations_url":"https://api.github.com/users/nolanmar511/orgs","repos_url":"https://api.github.com/users/nolanmar511/repos","events_url":"https://api.github.com/users/nolanmar511/events{/privacy}","received_events_url":"https://api.github.com/users/nolanmar511/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/nolanmar511/cloud-profiler-nodejs","description":null,"fork":true,"url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs","forks_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/forks","keys_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/teams","hooks_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/hooks","issue_events_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/issues/events{/number}","events_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/events","assignees_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/assignees{/user}","branches_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/branches{/branch}","tags_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/tags","blobs_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/git/refs{/sha}","trees_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/statuses/{sha}","languages_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/languages","stargazers_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/stargazers","contributors_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/contributors","subscribers_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/subscribers","subscription_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/subscription","commits_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/commits{/sha}","git_commits_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/git/commits{/sha}","comments_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/comments{/number}","issue_comment_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/issues/comments{/number}","contents_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/contents/{+path}","compare_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/merges","archive_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/downloads","issues_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/issues{/number}","pulls_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/pulls{/number}","milestones_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/milestones{/number}","notifications_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/labels{/name}","releases_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/releases{/id}","deployments_url":"https://api.github.com/repos/nolanmar511/cloud-profiler-nodejs/deployments","created_at":"2017-08-18T16:11:07Z","updated_at":"2017-10-23T17:29:32Z","pushed_at":"2017-11-10T22:46:57Z","git_url":"git://github.com/nolanmar511/cloud-profiler-nodejs.git","ssh_url":"git@github.com:nolanmar511/cloud-profiler-nodejs.git","clone_url":"https://github.com/nolanmar511/cloud-profiler-nodejs.git","svn_url":"https://github.com/nolanmar511/cloud-profiler-nodejs","homepage":null,"size":1101,"stargazers_count":0,"watchers_count":0,"language":"TypeScript","has_issues":false,"has_projects":true,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"GoogleCloudPlatform:master","ref":"master","sha":"b913ba73f1cd6517c467da97f0db2ae48b1d4bef","user":{"login":"GoogleCloudPlatform","id":2810941,"avatar_url":"https://avatars0.githubusercontent.com/u/2810941?v=4","gravatar_id":"","url":"https://api.github.com/users/GoogleCloudPlatform","html_url":"https://github.com/GoogleCloudPlatform","followers_url":"https://api.github.com/users/GoogleCloudPlatform/followers","following_url":"https://api.github.com/users/GoogleCloudPlatform/following{/other_user}","gists_url":"https://api.github.com/users/GoogleCloudPlatform/gists{/gist_id}","starred_url":"https://api.github.com/users/GoogleCloudPlatform/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GoogleCloudPlatform/subscriptions","organizations_url":"https://api.github.com/users/GoogleCloudPlatform/orgs","repos_url":"https://api.github.com/users/GoogleCloudPlatform/repos","events_url":"https://api.github.com/users/GoogleCloudPlatform/events{/privacy}","received_events_url":"https://api.github.com/users/GoogleCloudPlatform/received_events","type":"Organization","site_admin":false},"repo":{"id":50680973,"name":"cloud-profiler-nodejs","full_name":"GoogleCloudPlatform/cloud-profiler-nodejs","owner":{"login":"GoogleCloudPlatform","id":2810941,"avatar_url":"https://avatars0.githubusercontent.com/u/2810941?v=4","gravatar_id":"","url":"https://api.github.com/users/GoogleCloudPlatform","html_url":"https://github.com/GoogleCloudPlatform","followers_url":"https://api.github.com/users/GoogleCloudPlatform/followers","following_url":"https://api.github.com/users/GoogleCloudPlatform/following{/other_user}","gists_url":"https://api.github.com/users/GoogleCloudPlatform/gists{/gist_id}","starred_url":"https://api.github.com/users/GoogleCloudPlatform/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GoogleCloudPlatform/subscriptions","organizations_url":"https://api.github.com/users/GoogleCloudPlatform/orgs","repos_url":"https://api.github.com/users/GoogleCloudPlatform/repos","events_url":"https://api.github.com/users/GoogleCloudPlatform/events{/privacy}","received_events_url":"https://api.github.com/users/GoogleCloudPlatform/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/GoogleCloudPlatform/cloud-profiler-nodejs","description":null,"fork":false,"url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs","forks_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/forks","keys_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/teams","hooks_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/hooks","issue_events_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/issues/events{/number}","events_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/events","assignees_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/assignees{/user}","branches_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/branches{/branch}","tags_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/tags","blobs_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/git/refs{/sha}","trees_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/statuses/{sha}","languages_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/languages","stargazers_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/stargazers","contributors_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/contributors","subscribers_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/subscribers","subscription_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/subscription","commits_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/commits{/sha}","git_commits_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/git/commits{/sha}","comments_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/comments{/number}","issue_comment_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/issues/comments{/number}","contents_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/contents/{+path}","compare_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/merges","archive_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/downloads","issues_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/issues{/number}","pulls_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls{/number}","milestones_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/milestones{/number}","notifications_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/labels{/name}","releases_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/releases{/id}","deployments_url":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/deployments","created_at":"2016-01-29T18:06:50Z","updated_at":"2017-11-10T12:06:08Z","pushed_at":"2017-11-10T22:46:59Z","git_url":"git://github.com/GoogleCloudPlatform/cloud-profiler-nodejs.git","ssh_url":"git@github.com:GoogleCloudPlatform/cloud-profiler-nodejs.git","clone_url":"https://github.com/GoogleCloudPlatform/cloud-profiler-nodejs.git","svn_url":"https://github.com/GoogleCloudPlatform/cloud-profiler-nodejs","homepage":null,"size":783,"stargazers_count":8,"watchers_count":8,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":7,"mirror_url":null,"archived":false,"open_issues_count":11,"forks":7,"open_issues":11,"watchers":8,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls/16"},"html":{"href":"https://github.com/GoogleCloudPlatform/cloud-profiler-nodejs/pull/16"},"issue":{"href":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/issues/16"},"comments":{"href":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/issues/16/comments"},"review_comments":{"href":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls/16/comments"},"review_comment":{"href":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/pulls/16/commits"},"statuses":{"href":"https://api.github.com/repos/GoogleCloudPlatform/cloud-profiler-nodejs/statuses/77e7a402495a05880ee94deb0edfaf67069453e9"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"ofrobots"}}