{"action":"created","comment":{"url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls/comments/43308955","id":43308955,"diff_hunk":"@@ -0,0 +1,210 @@\n+<?php\n+\n+/**\n+ * @file\n+ * Contains \\Drupal\\cloudflare\\Plugin\\PurgePurger\\CloudFlarePurger.\n+ */\n+\n+namespace Drupal\\cloudflarepurger\\Plugin\\Purge\\Purger;\n+use Drupal\\Core\\Cache\\Cache;\n+use Drupal\\Core\\Config\\ConfigFactoryInterface;\n+use Drupal\\cloudflare\\CloudFlareStateInterface;\n+use Drupal\\cloudflarepurger\\EventSubscriber\\CloudFlareCacheTagHeaderGenerator;\n+use Drupal\\purge\\Plugin\\Purge\\Purger\\PurgerBase;\n+use Drupal\\purge\\Plugin\\Purge\\Purger\\PurgerInterface;\n+use Drupal\\purge\\Plugin\\Purge\\Invalidation\\InvalidationInterface;\n+use CloudFlarePhpSdk\\ApiEndpoints\\CloudFlareAPI;\n+use CloudFlarePhpSdk\\ApiEndpoints\\ZoneApi;\n+use Symfony\\Component\\DependencyInjection\\ContainerInterface;\n+use Psr\\Log\\LoggerInterface;\n+\n+/**\n+ * CloudFlare purger.\n+ *\n+ * @PurgePurger(\n+ *   id = \"cloudflare\",\n+ *   label = @Translation(\"CloudFlare\"),\n+ *   description = @Translation(\"Purger for CloudFlare.\"),\n+ *   configform = \"Drupal\\cloudflare\\Form\\CloudFlareAdminSettingsForm\",\n+ *   types = {\"tag\", \"url\", \"everything\"},\n+ *   multi_instance = FALSE,\n+ * )\n+ */\n+class CloudFlarePurger extends PurgerBase implements PurgerInterface {\n+\n+  /**\n+   * The settings configuration.\n+   *\n+   * @var \\Drupal\\Core\\Config\\Config\n+   */\n+  protected $config;\n+\n+  /**\n+   * A logger instance.\n+   *\n+   * @var \\Psr\\Log\\LoggerInterface\n+   */\n+  protected $logger;\n+\n+  /**\n+   * Tracks rate limits associated with CloudFlare Api.\n+   *\n+   * @var \\Drupal\\cloudflare\\CloudFlareStateInterface\n+   */\n+  protected $state;\n+\n+  /**\n+   * ZoneApi object for interfacing with CloudFlare Php Sdk.\n+   *\n+   * @var \\CloudFlarePhpSdk\\ApiEndpoints\\ZoneApi\n+   */\n+  protected $zoneApi;\n+\n+  /**\n+   * The current cloudflare ZoneId.\n+   *\n+   * @var string\n+   */\n+  protected $zone;\n+\n+  /**\n+   * {@inheritdoc}\n+   */\n+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {\n+    return new static(\n+      $configuration,\n+      $plugin_id,\n+      $plugin_definition,\n+      $container->get('config.factory'),\n+      $container->get('cloudflare.state'),\n+      $container->get('logger.factory')->get('cloudflare')\n+    );\n+  }\n+\n+  /**\n+   * Constructs a \\Drupal\\Component\\Plugin\\CloudFlareTagPurger.\n+   *\n+   * @param array $configuration\n+   *   A configuration array containing information about the plugin instance.\n+   * @param string $plugin_id\n+   *   The plugin_id for the plugin instance.\n+   * @param mixed $plugin_definition\n+   *   The plugin implementation definition.\n+   * @param \\Drupal\\Core\\Config\\ConfigFactoryInterface $config\n+   *   The factory for configuration objects.\n+   * @param \\Drupal\\cloudflare\\CloudFlareStateInterface $state\n+   *   Tracks rate limits associated with CloudFlare Api.\n+   * @param \\Psr\\Log\\LoggerInterface $logger\n+   *   A logger instance.\n+   *\n+   * @throws \\LogicException\n+   *   Thrown if $configuration['id'] is missing, see Purger\\Service::createId.\n+   */\n+  public function __construct(array $configuration, $plugin_id, $plugin_definition, ConfigFactoryInterface $config, CloudFlareStateInterface $state, LoggerInterface $logger) {\n+    $this->config = $config->get('cloudflare.settings');\n+    $this->state = $state;\n+    $this->logger = $logger;\n+\n+    // This is a unique case where the ApiSdk is being accessed directly and not\n+    // via a service.  Purging should only ever happen through the purge module\n+    // which is why this is NOT in a service.\n+    $api_key = $this->config->get('apikey');\n+    $email = $this->config->get('email');\n+    $this->zone = $this->config->get('zone');\n+    $this->zoneApi = new ZoneApi($api_key, $email);\n+\n+    parent::__construct($configuration, $plugin_id, $plugin_definition);\n+  }\n+\n+  /**\n+   * {@inheritdoc}\n+   */\n+  public function invalidate(InvalidationInterface $invalidation) {\n+    $this->invalidateMultiple([$invalidation]);\n+  }\n+\n+  /**\n+   * {@inheritdoc}\n+   */\n+  public function invalidateMultiple(array $invalidations) {\n+    // @todo figure out why this is happening.\n+    if (count($invalidations) == 0) {\n+      $this->logger->warning(\"No Invalidations?  That's odd.\" . print_r($invalidations, TRUE));\n+      return;\n+    }\n+\n+    $chunks = array_chunk($invalidations, CloudFlareAPI::MAX_TAG_PURGES_PER_REQUEST);\n+    foreach ($chunks as $chunk) {\n+      $this->purgeChunk($chunk);\n+    }\n+  }\n+\n+  /**\n+   * {@inheritdoc}\n+   */\n+  public function getTimeHint() {\n+    return 1.3;\n+  }\n+\n+  /**\n+   * Purges a chunk of tags.\n+   *\n+   * Integration point between purge and CloudFlareAPI.  Purge requires state\n+   * tracking on each item purged.  This function provides that accounting and\n+   * calls CloudflareApi.\n+   *\n+   * CloudFlare only allows us to purge 30 tags at once.\n+   *\n+   * @param array $invalidations\n+   *   Chunk of purge module invalidation objects to purge via CloudFlare.\n+   */\n+  private function purgeChunk(array &$invalidations) {\n+    $api_targets_to_purge = [];\n+\n+    // This method is unfortunately a bit verbose due to the fact that we\n+    // need to update the purge states as we proceed.\n+    foreach ($invalidations as $invalidation) {\n+      $invalidation->setState(InvalidationInterface::STATE_PURGING);\n+      $api_targets_to_purge[] = $invalidation->getExpression();\n+    }\n+\n+    try {\n+      // Interface with the CloudFlarePhpSdk.\n+      $invalidation_type = $invalidations[0]->getPluginId();\n+      if ($invalidation_type == 'tag') {\n+        // @todo Remove this wrapper once CloudFlare supports 16k headers.\n+        // Also invalidate the cache tags as hashes, to automatically also work\n+        // for responses that exceed CloudFlare's Cache-Tag header limit.\n+        $hashes = CloudFlareCacheTagHeaderGenerator::cacheTagsToHashes($api_targets_to_purge);\n+        $tags = Cache::mergeTags($api_targets_to_purge, $hashes);\n+\n+        $this->zoneApi->purgeTags($this->zone, $tags);\n+        $this->state->incrementTagPurgeDailyCount();\n+      }\n+\n+      elseif ($invalidation_type == 'url') {\n+        $this->zoneApi->purgeIndividualFiles($this->zone, $api_targets_to_purge);\n+      }\n+\n+      elseif ($invalidation_type == 'everything') {\n+        $this->zoneApi->purgeAllFiles($this->zone);\n+      }\n+\n+      foreach ($invalidations as $invalidation) {\n+        $invalidation->setState(InvalidationInterface::STATE_PURGED);\n+      }\n+    }\n+\n+    catch (\\Exception $e) {\n+      foreach ($invalidations as $invalidation) {\n+        $this->logger->error($e->getMessage());\n+        $invalidation->setState(InvalidationInterface::STATE_FAILED);\n+      }\n+    }\n+\n+    finally {","path":"modules/cloudflarepurger/src/Plugin/Purge/Purger/CloudFlarePurger.php","position":205,"original_position":205,"commit_id":"33df21cc85d3538fc814a2ac059b72f71a26ef74","original_commit_id":"33df21cc85d3538fc814a2ac059b72f71a26ef74","user":{"login":"nielsvm","id":1073680,"avatar_url":"https://avatars.githubusercontent.com/u/1073680?v=3","gravatar_id":"","url":"https://api.github.com/users/nielsvm","html_url":"https://github.com/nielsvm","followers_url":"https://api.github.com/users/nielsvm/followers","following_url":"https://api.github.com/users/nielsvm/following{/other_user}","gists_url":"https://api.github.com/users/nielsvm/gists{/gist_id}","starred_url":"https://api.github.com/users/nielsvm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nielsvm/subscriptions","organizations_url":"https://api.github.com/users/nielsvm/orgs","repos_url":"https://api.github.com/users/nielsvm/repos","events_url":"https://api.github.com/users/nielsvm/events{/privacy}","received_events_url":"https://api.github.com/users/nielsvm/received_events","type":"User","site_admin":false},"body":"Nifty, I never heard about this statement... it feels semi-pointless though: *...within the finally block will always be executed after the try and catch blocks, regardless of whether an exception has been thrown, and before normal execution resumes.*\r\n\r\nSo that replaces a newline? ;)","created_at":"2015-10-28T19:56:36Z","updated_at":"2015-10-28T19:56:36Z","html_url":"https://github.com/d8-contrib-modules/cloudflare/pull/32#discussion_r43308955","pull_request_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls/32","_links":{"self":{"href":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls/comments/43308955"},"html":{"href":"https://github.com/d8-contrib-modules/cloudflare/pull/32#discussion_r43308955"},"pull_request":{"href":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls/32"}}},"pull_request":{"url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls/32","id":47993653,"html_url":"https://github.com/d8-contrib-modules/cloudflare/pull/32","diff_url":"https://github.com/d8-contrib-modules/cloudflare/pull/32.diff","patch_url":"https://github.com/d8-contrib-modules/cloudflare/pull/32.patch","issue_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/issues/32","number":32,"state":"open","locked":false,"title":"Integrate CloudFlare module with Purge","user":{"login":"aweingarten","id":6517820,"avatar_url":"https://avatars.githubusercontent.com/u/6517820?v=3","gravatar_id":"","url":"https://api.github.com/users/aweingarten","html_url":"https://github.com/aweingarten","followers_url":"https://api.github.com/users/aweingarten/followers","following_url":"https://api.github.com/users/aweingarten/following{/other_user}","gists_url":"https://api.github.com/users/aweingarten/gists{/gist_id}","starred_url":"https://api.github.com/users/aweingarten/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aweingarten/subscriptions","organizations_url":"https://api.github.com/users/aweingarten/orgs","repos_url":"https://api.github.com/users/aweingarten/repos","events_url":"https://api.github.com/users/aweingarten/events{/privacy}","received_events_url":"https://api.github.com/users/aweingarten/received_events","type":"User","site_admin":false},"body":"See Issue: https://github.com/d8-contrib-modules/cloudflare/issues/6\r\nThis PR is based off https://github.com/d8-contrib-modules/cloudflare/pull/26\r\n\r\nIt also contains significant refactoring from https://github.com/d8-contrib-modules/cloudflare/pull/29 to incoporate Niels' feedback.  In particular this latest PR incorporates diagnostics to check for CloudFlare API rate limits.","created_at":"2015-10-17T19:38:34Z","updated_at":"2015-10-28T19:56:36Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e485ee7fca39aeee4df9ecd3a8e1343a5dcd05d2","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls/32/commits","review_comments_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls/32/comments","review_comment_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls/comments{/number}","comments_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/issues/32/comments","statuses_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/statuses/33df21cc85d3538fc814a2ac059b72f71a26ef74","head":{"label":"aweingarten:purge-integration-aw2","ref":"purge-integration-aw2","sha":"33df21cc85d3538fc814a2ac059b72f71a26ef74","user":{"login":"aweingarten","id":6517820,"avatar_url":"https://avatars.githubusercontent.com/u/6517820?v=3","gravatar_id":"","url":"https://api.github.com/users/aweingarten","html_url":"https://github.com/aweingarten","followers_url":"https://api.github.com/users/aweingarten/followers","following_url":"https://api.github.com/users/aweingarten/following{/other_user}","gists_url":"https://api.github.com/users/aweingarten/gists{/gist_id}","starred_url":"https://api.github.com/users/aweingarten/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aweingarten/subscriptions","organizations_url":"https://api.github.com/users/aweingarten/orgs","repos_url":"https://api.github.com/users/aweingarten/repos","events_url":"https://api.github.com/users/aweingarten/events{/privacy}","received_events_url":"https://api.github.com/users/aweingarten/received_events","type":"User","site_admin":false},"repo":{"id":40602521,"name":"cloudflare","full_name":"aweingarten/cloudflare","owner":{"login":"aweingarten","id":6517820,"avatar_url":"https://avatars.githubusercontent.com/u/6517820?v=3","gravatar_id":"","url":"https://api.github.com/users/aweingarten","html_url":"https://github.com/aweingarten","followers_url":"https://api.github.com/users/aweingarten/followers","following_url":"https://api.github.com/users/aweingarten/following{/other_user}","gists_url":"https://api.github.com/users/aweingarten/gists{/gist_id}","starred_url":"https://api.github.com/users/aweingarten/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/aweingarten/subscriptions","organizations_url":"https://api.github.com/users/aweingarten/orgs","repos_url":"https://api.github.com/users/aweingarten/repos","events_url":"https://api.github.com/users/aweingarten/events{/privacy}","received_events_url":"https://api.github.com/users/aweingarten/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/aweingarten/cloudflare","description":"Cloudflare Module for Drupal","fork":true,"url":"https://api.github.com/repos/aweingarten/cloudflare","forks_url":"https://api.github.com/repos/aweingarten/cloudflare/forks","keys_url":"https://api.github.com/repos/aweingarten/cloudflare/keys{/key_id}","collaborators_url":"https://api.github.com/repos/aweingarten/cloudflare/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/aweingarten/cloudflare/teams","hooks_url":"https://api.github.com/repos/aweingarten/cloudflare/hooks","issue_events_url":"https://api.github.com/repos/aweingarten/cloudflare/issues/events{/number}","events_url":"https://api.github.com/repos/aweingarten/cloudflare/events","assignees_url":"https://api.github.com/repos/aweingarten/cloudflare/assignees{/user}","branches_url":"https://api.github.com/repos/aweingarten/cloudflare/branches{/branch}","tags_url":"https://api.github.com/repos/aweingarten/cloudflare/tags","blobs_url":"https://api.github.com/repos/aweingarten/cloudflare/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/aweingarten/cloudflare/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/aweingarten/cloudflare/git/refs{/sha}","trees_url":"https://api.github.com/repos/aweingarten/cloudflare/git/trees{/sha}","statuses_url":"https://api.github.com/repos/aweingarten/cloudflare/statuses/{sha}","languages_url":"https://api.github.com/repos/aweingarten/cloudflare/languages","stargazers_url":"https://api.github.com/repos/aweingarten/cloudflare/stargazers","contributors_url":"https://api.github.com/repos/aweingarten/cloudflare/contributors","subscribers_url":"https://api.github.com/repos/aweingarten/cloudflare/subscribers","subscription_url":"https://api.github.com/repos/aweingarten/cloudflare/subscription","commits_url":"https://api.github.com/repos/aweingarten/cloudflare/commits{/sha}","git_commits_url":"https://api.github.com/repos/aweingarten/cloudflare/git/commits{/sha}","comments_url":"https://api.github.com/repos/aweingarten/cloudflare/comments{/number}","issue_comment_url":"https://api.github.com/repos/aweingarten/cloudflare/issues/comments{/number}","contents_url":"https://api.github.com/repos/aweingarten/cloudflare/contents/{+path}","compare_url":"https://api.github.com/repos/aweingarten/cloudflare/compare/{base}...{head}","merges_url":"https://api.github.com/repos/aweingarten/cloudflare/merges","archive_url":"https://api.github.com/repos/aweingarten/cloudflare/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/aweingarten/cloudflare/downloads","issues_url":"https://api.github.com/repos/aweingarten/cloudflare/issues{/number}","pulls_url":"https://api.github.com/repos/aweingarten/cloudflare/pulls{/number}","milestones_url":"https://api.github.com/repos/aweingarten/cloudflare/milestones{/number}","notifications_url":"https://api.github.com/repos/aweingarten/cloudflare/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/aweingarten/cloudflare/labels{/name}","releases_url":"https://api.github.com/repos/aweingarten/cloudflare/releases{/id}","created_at":"2015-08-12T13:35:51Z","updated_at":"2015-10-20T12:25:20Z","pushed_at":"2015-10-28T14:11:44Z","git_url":"git://github.com/aweingarten/cloudflare.git","ssh_url":"git@github.com:aweingarten/cloudflare.git","clone_url":"https://github.com/aweingarten/cloudflare.git","svn_url":"https://github.com/aweingarten/cloudflare","homepage":null,"size":276,"stargazers_count":1,"watchers_count":1,"language":"PHP","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":1,"default_branch":"master"}},"base":{"label":"d8-contrib-modules:master","ref":"master","sha":"2450876c0e6b1d6ba1d2687c7fa89a8ab08ac8b8","user":{"login":"d8-contrib-modules","id":13561676,"avatar_url":"https://avatars.githubusercontent.com/u/13561676?v=3","gravatar_id":"","url":"https://api.github.com/users/d8-contrib-modules","html_url":"https://github.com/d8-contrib-modules","followers_url":"https://api.github.com/users/d8-contrib-modules/followers","following_url":"https://api.github.com/users/d8-contrib-modules/following{/other_user}","gists_url":"https://api.github.com/users/d8-contrib-modules/gists{/gist_id}","starred_url":"https://api.github.com/users/d8-contrib-modules/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/d8-contrib-modules/subscriptions","organizations_url":"https://api.github.com/users/d8-contrib-modules/orgs","repos_url":"https://api.github.com/users/d8-contrib-modules/repos","events_url":"https://api.github.com/users/d8-contrib-modules/events{/privacy}","received_events_url":"https://api.github.com/users/d8-contrib-modules/received_events","type":"Organization","site_admin":false},"repo":{"id":39284441,"name":"cloudflare","full_name":"d8-contrib-modules/cloudflare","owner":{"login":"d8-contrib-modules","id":13561676,"avatar_url":"https://avatars.githubusercontent.com/u/13561676?v=3","gravatar_id":"","url":"https://api.github.com/users/d8-contrib-modules","html_url":"https://github.com/d8-contrib-modules","followers_url":"https://api.github.com/users/d8-contrib-modules/followers","following_url":"https://api.github.com/users/d8-contrib-modules/following{/other_user}","gists_url":"https://api.github.com/users/d8-contrib-modules/gists{/gist_id}","starred_url":"https://api.github.com/users/d8-contrib-modules/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/d8-contrib-modules/subscriptions","organizations_url":"https://api.github.com/users/d8-contrib-modules/orgs","repos_url":"https://api.github.com/users/d8-contrib-modules/repos","events_url":"https://api.github.com/users/d8-contrib-modules/events{/privacy}","received_events_url":"https://api.github.com/users/d8-contrib-modules/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/d8-contrib-modules/cloudflare","description":"Cloudflare Module for Drupal","fork":false,"url":"https://api.github.com/repos/d8-contrib-modules/cloudflare","forks_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/forks","keys_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/keys{/key_id}","collaborators_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/teams","hooks_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/hooks","issue_events_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/issues/events{/number}","events_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/events","assignees_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/assignees{/user}","branches_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/branches{/branch}","tags_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/tags","blobs_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/git/refs{/sha}","trees_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/git/trees{/sha}","statuses_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/statuses/{sha}","languages_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/languages","stargazers_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/stargazers","contributors_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/contributors","subscribers_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/subscribers","subscription_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/subscription","commits_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/commits{/sha}","git_commits_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/git/commits{/sha}","comments_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/comments{/number}","issue_comment_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/issues/comments{/number}","contents_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/contents/{+path}","compare_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/compare/{base}...{head}","merges_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/merges","archive_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/downloads","issues_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/issues{/number}","pulls_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls{/number}","milestones_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/milestones{/number}","notifications_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/labels{/name}","releases_url":"https://api.github.com/repos/d8-contrib-modules/cloudflare/releases{/id}","created_at":"2015-07-18T02:59:13Z","updated_at":"2015-10-20T12:25:09Z","pushed_at":"2015-10-28T14:11:44Z","git_url":"git://github.com/d8-contrib-modules/cloudflare.git","ssh_url":"git@github.com:d8-contrib-modules/cloudflare.git","clone_url":"https://github.com/d8-contrib-modules/cloudflare.git","svn_url":"https://github.com/d8-contrib-modules/cloudflare","homepage":null,"size":618,"stargazers_count":3,"watchers_count":3,"language":"PHP","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":4,"mirror_url":null,"open_issues_count":16,"forks":4,"open_issues":16,"watchers":3,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls/32"},"html":{"href":"https://github.com/d8-contrib-modules/cloudflare/pull/32"},"issue":{"href":"https://api.github.com/repos/d8-contrib-modules/cloudflare/issues/32"},"comments":{"href":"https://api.github.com/repos/d8-contrib-modules/cloudflare/issues/32/comments"},"review_comments":{"href":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls/32/comments"},"review_comment":{"href":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/d8-contrib-modules/cloudflare/pulls/32/commits"},"statuses":{"href":"https://api.github.com/repos/d8-contrib-modules/cloudflare/statuses/33df21cc85d3538fc814a2ac059b72f71a26ef74"}}}}