{"action":"created","comment":{"url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls/comments/176084118","pull_request_review_id":105723814,"id":176084118,"diff_hunk":"@@ -1,321 +1,143 @@\n #include \"Streamer.h\"\n \n #include \"helper.h\"\n-#include \"logger.h\"\n-\n-#include <librdkafka/rdkafkacpp.h>\n-\n-#include <algorithm>\n-#include <memory>\n \n namespace FileWriter {\n std::chrono::milliseconds systemTime() {\n-  using namespace std::chrono;\n-  system_clock::time_point now = system_clock::now();\n-  return duration_cast<std::chrono::milliseconds>(now.time_since_epoch());\n+  std::chrono::system_clock::time_point now = std::chrono::system_clock::now();\n+  return std::chrono::duration_cast<std::chrono::milliseconds>(\n+      now.time_since_epoch());\n }\n } // namespace FileWriter\n \n-/// Create and configure the RdKafka configuration used in the Streamer. If the\n-/// RdKafka fails in the creations of the configuration log the error, set\n-/// RunStatus to SEC::configuration_error and return an empty pointer. Else\n-/// return a unique_ptr pointing to the configuration. The configuration is\n-/// created using the options described in Options. For the list of available\n-/// options see\n-/// https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md . If an\n-/// invalid option is passed log a warning.\n-/// \\param Options an instance of StreamerOptions that contains the\n-/// RdKafka::Conf options.\n-std::unique_ptr<RdKafka::Conf> FileWriter::Streamer::createConfiguration(\n-    const FileWriter::StreamerOptions &Options) {\n-\n-  std::unique_ptr<RdKafka::Conf> Conf(\n-      RdKafka::Conf::create(RdKafka::Conf::CONF_GLOBAL));\n-  if (!Conf) {\n-    LOG(Sev::Error, \"Error: invalid configuration\");\n-    RunStatus = SEC::configuration_error;\n-    return nullptr;\n-  }\n-\n-  std::string ErrStr;\n-  for (std::pair<std::string, std::string> Option : Options.RdKafkaOptions) {\n-    if (Conf->set(Option.first, Option.second, ErrStr) !=\n-        RdKafka::Conf::CONF_OK) {\n-      LOG(Sev::Warning, \"{}\", ErrStr);\n-    }\n-  }\n-  return Conf;\n-}\n-\n-/// Create the RdKafka::KafkaConsumer using the specified RdKafka::Conf\n-/// configuration and store the consumer in a unique_ptr. If the creation\n-/// succeed return SEC::no_error, else SEC::consumer_error\n-/// \\param conf the RdKafka configuration to be used to create the consumer\n-FileWriter::Status::StreamerErrorCode\n-FileWriter::Streamer::createConsumer(std::unique_ptr<RdKafka::Conf> &&Conf) {\n-  std::string ErrStr;\n-  Consumer.reset(RdKafka::KafkaConsumer::create(Conf.get(), ErrStr));\n-  if (!Consumer) {\n-    LOG(Sev::Error, \"{}\", ErrStr);\n-    return SEC::consumer_error;\n-  }\n-  return SEC::no_error;\n-}\n-\n-/// Retrieves the RdKafka::Matadata associated with the consumer and return it\n-/// as a unique_ptr. If fail log the error, set the RunStatus to\n-/// SEC::metadata_error and return a null pointer.\n-std::unique_ptr<RdKafka::Metadata> FileWriter::Streamer::createMetadata() {\n-  RdKafka::Metadata *Metadata{nullptr};\n-  std::unique_ptr<RdKafka::Topic> Topic;\n-  int retry{0};\n-  auto err = Consumer->metadata(Topic != nullptr, Topic.get(), &Metadata, 1000);\n-  while (err != RdKafka::ERR_NO_ERROR && retry < Options.NumMetadataRetry) {\n-    err = Consumer->metadata(Topic != nullptr, Topic.get(), &Metadata, 1000);\n-    ++retry;\n-  }\n-  if (err != RdKafka::ERR_NO_ERROR) {\n-    LOG(Sev::Error, \"{}\", RdKafka::err2str(err));\n-    RunStatus = SEC::metadata_error;\n-    return std::unique_ptr<RdKafka::Metadata>{nullptr};\n-  } else {\n-    return std::unique_ptr<RdKafka::Metadata>{Metadata};\n-  }\n-}\n-\n-/// Reads the metadata structure and fills the RdKafka::TopicPartition vector\n-/// with all the partitions corresponding to the given topic\n-/// \\param metadata\n-FileWriter::Status::StreamerErrorCode\n-FileWriter::Streamer::createTopicPartition(\n-    const std::string &TopicName,\n-    std::unique_ptr<RdKafka::Metadata> &&Metadata) {\n-  if (RunStatus == SEC::metadata_error || !Metadata) {\n-    return SEC::metadata_error;\n-  }\n-  using PartitionMetadataVector =\n-      std::vector<const RdKafka::PartitionMetadata *>;\n-  const PartitionMetadataVector *pmv{nullptr};\n-  for (auto &t : *Metadata->topics()) {\n-    if (t->topic() == TopicName) {\n-      pmv = t->partitions();\n-      break;\n-    }\n-  }\n-  if (!pmv) {\n-    LOG(Sev::Error, \"Error: unable to find partition for topic {}\", TopicName);\n-    return SEC::topic_partition_error;\n-  }\n-  if (!pmv->empty()) {\n-    for (auto p : *pmv) {\n-      pushTopicPartition(TopicName, p->id());\n-      if (!TopicPartitionVector.back()) {\n-        LOG(Sev::Error, \"Error: unable to create partition {} for topic {}\",\n-            p->id(), TopicName);\n-        return SEC::topic_partition_error;\n-      }\n-    }\n-  } else {\n-    LOG(Sev::Error, \"Error: no partitions for topic {}\", TopicName);\n-    return SEC::topic_partition_error;\n-  }\n-\n-  return SEC::no_error;\n-}\n-\n-/// Givent the (topic,partition) pair extracted from the metadata structure push\n-/// a RdKafka::TopicPartition object into the TopicPartitionVector. If a start\n-/// timestamp is specified in the Options object the TopicPartition is\n-/// initialised at the correct log, else the TopicPartition points to\n-/// RdKafka::Topic::OFFSET_END\n-/// \\param topic name of the topic to listen\n-/// \\param partition index of the partition\n-void FileWriter::Streamer::pushTopicPartition(const std::string &TopicName,\n-                                              const int32_t &Partition) {\n-  if (Options.StartTimestamp.count()) {\n-    std::chrono::milliseconds ActualStartTime =\n-        Options.StartTimestamp - Options.BeforeStartTime;\n-    TopicPartitionVector.push_back(RdKafka::TopicPartition::create(\n-        TopicName, Partition, ActualStartTime.count()));\n-  } else {\n-    TopicPartitionVector.push_back(RdKafka::TopicPartition::create(\n-        TopicName, Partition, RdKafka::Topic::OFFSET_END));\n-  }\n-}\n-\n-/// Assign the RdKafka::TopicPartition vector to the consumer at the correct\n-/// point in time. If the Consumer is not allocated, the TopicPartitionVector\n-/// empty (e.g. the topic is not found) or can't assign the consumer return\n-/// SEC::topic_partition_error, else SEC::no_error\n-FileWriter::Streamer::SEC FileWriter::Streamer::assignTopicPartition() {\n-  if (!Consumer || TopicPartitionVector.empty()) {\n-    return SEC::topic_partition_error;\n-  }\n-  auto err = Consumer->offsetsForTimes(TopicPartitionVector, 1000);\n-  if (err != RdKafka::ERR_NO_ERROR) {\n-    LOG(Sev::Error,\n-        \"Error while look up the offsets for the given timestamp: {}\",\n-        RdKafka::err2str(err));\n-    return SEC::topic_partition_error;\n-  }\n-  err = Consumer->assign(TopicPartitionVector);\n-  if (err != RdKafka::ERR_NO_ERROR) {\n-    LOG(Sev::Error, \"Error while assign to topic-partition: {}\",\n-        RdKafka::err2str(err));\n-    return SEC::topic_partition_error;\n-  }\n-  return SEC::no_error;\n-};\n-\n FileWriter::Streamer::Streamer(const std::string &Broker,\n                                const std::string &TopicName,\n                                const FileWriter::StreamerOptions &Opts)\n     : RunStatus{SEC::not_initialized}, Options(Opts) {\n \n   if (TopicName.empty() || Broker.empty()) {\n-    LOG(Sev::Error, \"Broker and topic required\");\n     RunStatus = SEC::not_initialized;\n-    return;\n+    throw std::runtime_error(\"Missing broker or topic\");\n   }\n-  Options.RdKafkaOptions.emplace_back(\"metadata.broker.list\", Broker);\n-  Options.RdKafkaOptions.emplace_back(\"api.version.request\", \"true\");\n-  Options.RdKafkaOptions.emplace_back(\"group.id\", TopicName);\n \n-  ConnectThread = std::thread([&] {\n-    this->connect(std::ref(TopicName));\n-    return;\n-  });\n+  Options.Settings.ConfigurationStrings[\"group.id\"] = TopicName;\n+  Options.Settings.ConfigurationStrings[\"auto.create.topics.enable\"] = \"false\";\n+  Options.Settings.Address = Broker;\n \n-  std::unique_lock<std::mutex> lk(ConnectionLock);\n-  ConnectionInit.wait(lk, [&] { return this->Initialising.load(); });\n+  IsConnected =\n+      std::async(std::launch::async, &FileWriter::Streamer::Streamer::connect,\n+                 this, TopicName);\n }\n \n-FileWriter::Streamer::~Streamer() { closeStream(); }\n-\n-/// Create a RdKafka::Consumer a vector containing all the TopicPartition for\n-/// the given topic. If a start time is specified retrieve the correct initial\n-/// log. Assign the TopicPartition vector to the Consumer\n-/// \\param TopicName the topic that the Streamer will consume\n-/// \\param Options a StreamerOptions object\n-/// that contains configuration parameters for the Streamer and the KafkaConfig\n-void FileWriter::Streamer::connect(const std::string &TopicName) {\n-  std::lock_guard<std::mutex> lock(ConnectionReady);\n-\n-  std::lock_guard<std::mutex> lk(ConnectionLock);\n-  Initialising = true;\n-  ConnectionInit.notify_all();\n+// pass the topic by value: this allow the constructor to go out of scope\n+// without resulting in an error\n+FileWriter::Streamer::SEC FileWriter::Streamer::connect(std::string TopicName) {\n \n   LOG(Sev::Debug, \"Connecting to {}\", TopicName);\n-  auto Config = createConfiguration(Options);\n-  if (!Config) {\n-    return;\n-  }\n-  RunStatus = createConsumer(std::move(Config));\n-  if (RunStatus != SEC::no_error) {\n-    return;\n-  }\n-  auto Metadata = createMetadata();\n-  if (!Metadata) {\n-    return;\n-  }\n-  LOG(Sev::Debug, \"createMetadata\");\n-  RunStatus = createTopicPartition(TopicName, std::move(Metadata));\n-  if (RunStatus != SEC::no_error) {\n-    return;\n-  }\n-  LOG(Sev::Debug, \"createTopicPartition\");\n-  RunStatus = assignTopicPartition();\n-  if (RunStatus != SEC::no_error) {\n-    return;\n+  try {\n+    Consumer.reset(new KafkaW::Consumer(Options.Settings));\n+    if (Options.StartTimestamp.count()) {\n+      Consumer->addTopic(TopicName,\n+                         Options.StartTimestamp - Options.BeforeStartTime);\n+    } else {\n+      Consumer->addTopic(TopicName);\n+    }\n+    // Error if the topic cannot be found in the metadata\n+    if (!Consumer->topicPresent(TopicName)) {\n+      return SEC::topic_partition_error;\n+    }\n+  } catch (std::exception &Error) {\n+    LOG(Sev::Error, \"{}\", Error.what());\n+    return SEC::configuration_error;\n   }\n-  LOG(Sev::Debug, \"Connected to topic {}\", TopicName);\n-  RunStatus = SEC::writing;\n+\n+  return SEC::writing;\n }\n \n FileWriter::Streamer::SEC FileWriter::Streamer::closeStream() {\n-  std::lock_guard<std::mutex> lock(ConnectionReady);\n-  if (ConnectThread.joinable()) {\n-    ConnectThread.join();\n-  }\n-  if (Consumer) {\n-    Consumer->close();\n-  }\n-  TopicPartitionVector.clear();\n-  //  if (RunStatus == SEC::writing) {\n+  Sources.clear();\n   RunStatus = SEC::has_finished;\n-  //  }\n   return RunStatus;\n }\n \n template <>\n FileWriter::ProcessMessageResult\n FileWriter::Streamer::write(FileWriter::DemuxTopic &MessageProcessor) {\n \n-  if (RunStatus == SEC::not_initialized) {\n-    return ProcessMessageResult::OK();\n+  // wait for connect() to finish\n+  try {\n+    if (IsConnected.valid()) {\n+      if (IsConnected.wait_for(std::chrono::milliseconds(1000)) !=\n+          std::future_status::ready) {\n+        LOG(Sev::Critical, \"... still not ready\");\n+        return ProcessMessageResult::OK();\n+      }\n+      std::call_once(ConnectionStatus,","path":"src/Streamer.cxx","position":295,"original_position":295,"commit_id":"eba09c4ce295444b0e0e70c8e8371a3673da0305","original_commit_id":"eba09c4ce295444b0e0e70c8e8371a3673da0305","user":{"login":"dominikwerder","id":22543584,"avatar_url":"https://avatars3.githubusercontent.com/u/22543584?v=4","gravatar_id":"","url":"https://api.github.com/users/dominikwerder","html_url":"https://github.com/dominikwerder","followers_url":"https://api.github.com/users/dominikwerder/followers","following_url":"https://api.github.com/users/dominikwerder/following{/other_user}","gists_url":"https://api.github.com/users/dominikwerder/gists{/gist_id}","starred_url":"https://api.github.com/users/dominikwerder/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dominikwerder/subscriptions","organizations_url":"https://api.github.com/users/dominikwerder/orgs","repos_url":"https://api.github.com/users/dominikwerder/repos","events_url":"https://api.github.com/users/dominikwerder/events{/privacy}","received_events_url":"https://api.github.com/users/dominikwerder/received_events","type":"User","site_admin":false},"body":"Why is `std::call_once` necessary here?","created_at":"2018-03-21T13:41:50Z","updated_at":"2018-03-21T13:54:16Z","html_url":"https://github.com/ess-dmsc/kafka-to-nexus/pull/143#discussion_r176084118","pull_request_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls/143","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls/comments/176084118"},"html":{"href":"https://github.com/ess-dmsc/kafka-to-nexus/pull/143#discussion_r176084118"},"pull_request":{"href":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls/143"}}},"pull_request":{"url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls/143","id":174300105,"html_url":"https://github.com/ess-dmsc/kafka-to-nexus/pull/143","diff_url":"https://github.com/ess-dmsc/kafka-to-nexus/pull/143.diff","patch_url":"https://github.com/ess-dmsc/kafka-to-nexus/pull/143.patch","issue_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/issues/143","number":143,"state":"open","locked":false,"title":"Streamer uses KafkaW","user":{"login":"michele-brambilla","id":17079797,"avatar_url":"https://avatars0.githubusercontent.com/u/17079797?v=4","gravatar_id":"","url":"https://api.github.com/users/michele-brambilla","html_url":"https://github.com/michele-brambilla","followers_url":"https://api.github.com/users/michele-brambilla/followers","following_url":"https://api.github.com/users/michele-brambilla/following{/other_user}","gists_url":"https://api.github.com/users/michele-brambilla/gists{/gist_id}","starred_url":"https://api.github.com/users/michele-brambilla/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/michele-brambilla/subscriptions","organizations_url":"https://api.github.com/users/michele-brambilla/orgs","repos_url":"https://api.github.com/users/michele-brambilla/repos","events_url":"https://api.github.com/users/michele-brambilla/events{/privacy}","received_events_url":"https://api.github.com/users/michele-brambilla/received_events","type":"User","site_admin":false},"body":"This PR introduces two main changes\r\n\r\n1. the ``streamer``now  uses ``KafkaW`` instead of librdkafka; \r\n2. the ``connect()`` task is now executed in task-based parallelism instead of thread-based;\r\n\r\nand a new feature\r\n\r\n3. added tests for the ``Streamer``.\r\n\r\nConsequences:\r\n\r\n1) makes the filewriter implementation more uniform. ``KafkaW`` is the only explicit connection with librdkafka;\r\n2) let us remove explicit locks and condition variables. The flow is now more linear;\r\n3) needs the fake librdkafka implementation to be specified using ``-DFAKE_RDKAFKA=<path>`` when invoking ``cmake``.","created_at":"2018-03-12T08:34:47Z","updated_at":"2018-03-21T13:54:16Z","closed_at":null,"merged_at":null,"merge_commit_sha":"cfd93791759d45df1aef23e0ce7ce89b967cfa1e","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls/143/commits","review_comments_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls/143/comments","review_comment_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls/comments{/number}","comments_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/issues/143/comments","statuses_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/statuses/eba09c4ce295444b0e0e70c8e8371a3673da0305","head":{"label":"ess-dmsc:streamer_uses_kafkaw","ref":"streamer_uses_kafkaw","sha":"eba09c4ce295444b0e0e70c8e8371a3673da0305","user":{"login":"ess-dmsc","id":12100833,"avatar_url":"https://avatars1.githubusercontent.com/u/12100833?v=4","gravatar_id":"","url":"https://api.github.com/users/ess-dmsc","html_url":"https://github.com/ess-dmsc","followers_url":"https://api.github.com/users/ess-dmsc/followers","following_url":"https://api.github.com/users/ess-dmsc/following{/other_user}","gists_url":"https://api.github.com/users/ess-dmsc/gists{/gist_id}","starred_url":"https://api.github.com/users/ess-dmsc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ess-dmsc/subscriptions","organizations_url":"https://api.github.com/users/ess-dmsc/orgs","repos_url":"https://api.github.com/users/ess-dmsc/repos","events_url":"https://api.github.com/users/ess-dmsc/events{/privacy}","received_events_url":"https://api.github.com/users/ess-dmsc/received_events","type":"Organization","site_admin":false},"repo":{"id":81435658,"name":"kafka-to-nexus","full_name":"ess-dmsc/kafka-to-nexus","owner":{"login":"ess-dmsc","id":12100833,"avatar_url":"https://avatars1.githubusercontent.com/u/12100833?v=4","gravatar_id":"","url":"https://api.github.com/users/ess-dmsc","html_url":"https://github.com/ess-dmsc","followers_url":"https://api.github.com/users/ess-dmsc/followers","following_url":"https://api.github.com/users/ess-dmsc/following{/other_user}","gists_url":"https://api.github.com/users/ess-dmsc/gists{/gist_id}","starred_url":"https://api.github.com/users/ess-dmsc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ess-dmsc/subscriptions","organizations_url":"https://api.github.com/users/ess-dmsc/orgs","repos_url":"https://api.github.com/users/ess-dmsc/repos","events_url":"https://api.github.com/users/ess-dmsc/events{/privacy}","received_events_url":"https://api.github.com/users/ess-dmsc/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ess-dmsc/kafka-to-nexus","description":null,"fork":false,"url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus","forks_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/forks","keys_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/teams","hooks_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/hooks","issue_events_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/issues/events{/number}","events_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/events","assignees_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/assignees{/user}","branches_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/branches{/branch}","tags_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/tags","blobs_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/git/refs{/sha}","trees_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/statuses/{sha}","languages_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/languages","stargazers_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/stargazers","contributors_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/contributors","subscribers_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/subscribers","subscription_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/subscription","commits_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/commits{/sha}","git_commits_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/git/commits{/sha}","comments_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/comments{/number}","issue_comment_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/issues/comments{/number}","contents_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/contents/{+path}","compare_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/merges","archive_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/downloads","issues_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/issues{/number}","pulls_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls{/number}","milestones_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/milestones{/number}","notifications_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/labels{/name}","releases_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/releases{/id}","deployments_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/deployments","created_at":"2017-02-09T09:55:55Z","updated_at":"2018-03-21T13:32:48Z","pushed_at":"2018-03-21T13:32:46Z","git_url":"git://github.com/ess-dmsc/kafka-to-nexus.git","ssh_url":"git@github.com:ess-dmsc/kafka-to-nexus.git","clone_url":"https://github.com/ess-dmsc/kafka-to-nexus.git","svn_url":"https://github.com/ess-dmsc/kafka-to-nexus","homepage":null,"size":2146,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":32,"license":null,"forks":0,"open_issues":32,"watchers":0,"default_branch":"master"}},"base":{"label":"ess-dmsc:master","ref":"master","sha":"e7dcbc4bb0cc3c075d76e65b7b41773d4a631a3e","user":{"login":"ess-dmsc","id":12100833,"avatar_url":"https://avatars1.githubusercontent.com/u/12100833?v=4","gravatar_id":"","url":"https://api.github.com/users/ess-dmsc","html_url":"https://github.com/ess-dmsc","followers_url":"https://api.github.com/users/ess-dmsc/followers","following_url":"https://api.github.com/users/ess-dmsc/following{/other_user}","gists_url":"https://api.github.com/users/ess-dmsc/gists{/gist_id}","starred_url":"https://api.github.com/users/ess-dmsc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ess-dmsc/subscriptions","organizations_url":"https://api.github.com/users/ess-dmsc/orgs","repos_url":"https://api.github.com/users/ess-dmsc/repos","events_url":"https://api.github.com/users/ess-dmsc/events{/privacy}","received_events_url":"https://api.github.com/users/ess-dmsc/received_events","type":"Organization","site_admin":false},"repo":{"id":81435658,"name":"kafka-to-nexus","full_name":"ess-dmsc/kafka-to-nexus","owner":{"login":"ess-dmsc","id":12100833,"avatar_url":"https://avatars1.githubusercontent.com/u/12100833?v=4","gravatar_id":"","url":"https://api.github.com/users/ess-dmsc","html_url":"https://github.com/ess-dmsc","followers_url":"https://api.github.com/users/ess-dmsc/followers","following_url":"https://api.github.com/users/ess-dmsc/following{/other_user}","gists_url":"https://api.github.com/users/ess-dmsc/gists{/gist_id}","starred_url":"https://api.github.com/users/ess-dmsc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ess-dmsc/subscriptions","organizations_url":"https://api.github.com/users/ess-dmsc/orgs","repos_url":"https://api.github.com/users/ess-dmsc/repos","events_url":"https://api.github.com/users/ess-dmsc/events{/privacy}","received_events_url":"https://api.github.com/users/ess-dmsc/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/ess-dmsc/kafka-to-nexus","description":null,"fork":false,"url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus","forks_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/forks","keys_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/keys{/key_id}","collaborators_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/teams","hooks_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/hooks","issue_events_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/issues/events{/number}","events_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/events","assignees_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/assignees{/user}","branches_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/branches{/branch}","tags_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/tags","blobs_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/git/refs{/sha}","trees_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/git/trees{/sha}","statuses_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/statuses/{sha}","languages_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/languages","stargazers_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/stargazers","contributors_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/contributors","subscribers_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/subscribers","subscription_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/subscription","commits_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/commits{/sha}","git_commits_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/git/commits{/sha}","comments_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/comments{/number}","issue_comment_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/issues/comments{/number}","contents_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/contents/{+path}","compare_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/compare/{base}...{head}","merges_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/merges","archive_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/downloads","issues_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/issues{/number}","pulls_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls{/number}","milestones_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/milestones{/number}","notifications_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/labels{/name}","releases_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/releases{/id}","deployments_url":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/deployments","created_at":"2017-02-09T09:55:55Z","updated_at":"2018-03-21T13:32:48Z","pushed_at":"2018-03-21T13:32:46Z","git_url":"git://github.com/ess-dmsc/kafka-to-nexus.git","ssh_url":"git@github.com:ess-dmsc/kafka-to-nexus.git","clone_url":"https://github.com/ess-dmsc/kafka-to-nexus.git","svn_url":"https://github.com/ess-dmsc/kafka-to-nexus","homepage":null,"size":2146,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":32,"license":null,"forks":0,"open_issues":32,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls/143"},"html":{"href":"https://github.com/ess-dmsc/kafka-to-nexus/pull/143"},"issue":{"href":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/issues/143"},"comments":{"href":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/issues/143/comments"},"review_comments":{"href":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls/143/comments"},"review_comment":{"href":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/pulls/143/commits"},"statuses":{"href":"https://api.github.com/repos/ess-dmsc/kafka-to-nexus/statuses/eba09c4ce295444b0e0e70c8e8371a3673da0305"}},"author_association":"COLLABORATOR"}}
{"actor":{"display_login":"dominikwerder"}}