diff --git a/.gitignore b/.gitignore
index a1c2a23..5ddaba9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,23 +1,31 @@
-# Compiled class file
-*.class
+target/
+!.mvn/wrapper/maven-wrapper.jar
+.checkstyle
-# Log file
-*.log
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
-# BlueJ files
-*.ctxt
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
-# Mobile Tools for Java (J2ME)
-.mtj.tmp/
+### NetBeans ###
+nbproject/private/
+build/
+nbbuild/
+dist/
+nbdist/
+.nb-gradle/
-# Package Files #
-*.jar
-*.war
-*.nar
-*.ear
-*.zip
-*.tar.gz
-*.rar
+### Log file ###
+*.log
-# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+### virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml ###
hs_err_pid*
diff --git a/README.md b/README.md
index aa68257..4030142 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,2 @@
-# github
+# GitHub
A collection of Github connectors and plugins
diff --git a/checkstyle.xml b/checkstyle.xml
new file mode 100644
index 0000000..07209ea
--- /dev/null
+++ b/checkstyle.xml
@@ -0,0 +1,406 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/GithubBatchSource-batchsource.md b/docs/GithubBatchSource-batchsource.md
new file mode 100644
index 0000000..2c7dee0
--- /dev/null
+++ b/docs/GithubBatchSource-batchsource.md
@@ -0,0 +1,28 @@
+# GitHub batch source
+
+Description
+-----------
+This plugin is used to query GitHub API.
+
+Using this plugin users can select the data sets associated with the specified repository and collect raw level data.
+
+Properties
+----------
+### Basic
+
+**Reference Name:** Name used to uniquely identify this source for lineage, annotating metadata, etc.
+
+**Repository owner name:** GitHub username who owns the repository from which the data is retrieved.
+
+**Repository name:** Repository name from which the data is retrieved.
+
+**Dataset name:** Dataset name that you would like to retrieve.
+
+### Advanced
+
+**GitHub API hostname:** GitHub API hostname from which the data is retrieved. Optional, for GitHub Enterprise only.
+By default, _api.github.com_
+
+### Credentials
+
+**Authorization token:** Authorization token to be used to authenticate to GitHub API.
diff --git a/icons/GithubBatchSource-batchsource.png b/icons/GithubBatchSource-batchsource.png
new file mode 100644
index 0000000..4790adf
Binary files /dev/null and b/icons/GithubBatchSource-batchsource.png differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..960e81c
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,366 @@
+
+
+ 4.0.0
+
+ io.cdap.plugin
+ github-plugin
+ GitHub plugin
+ 1.0.0-SNAPSHOT
+ A collection of gitHub connectors and plugins
+ https://github.com/data-integrations/github
+
+
+
+ The Apache Software License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0.txt
+ repo
+ A business-friendly OSS license
+
+
+
+
+
+ CDAP
+ cdap-dev@googlegroups.com
+ CDAP
+ http://www.cdap.io
+
+
+
+
+
+ sonatype
+ https://oss.sonatype.org/content/groups/public
+
+
+ sonatype-snapshots
+ https://oss.sonatype.org/content/repositories/snapshots
+
+
+ eclipse
+ https://repo.eclipse.org/content/groups/releases
+
+
+
+
+ https://issues.cask.co/browse/CDAP
+
+
+
+ UTF-8
+
+ widgets
+ docs
+
+ ${project.basedir}
+ 6.1.0-SNAPSHOT
+ 2.3.0
+ 2.3.0-SNAPSHOT
+ 4.11
+ 1.10.19
+ 2.1.3
+ 3.11.1
+ 3.9.0
+ 1.32.1
+
+
+
+
+ io.cdap.cdap
+ cdap-etl-api
+ ${cdap.version}
+
+
+ io.cdap.plugin
+ hydrator-common
+ ${hydrator.version}
+
+
+ org.apache.hadoop
+ hadoop-common
+ ${hadoop.version}
+ provided
+
+
+ commons-logging
+ commons-logging
+
+
+ log4j
+ log4j
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+ org.apache.avro
+ avro
+
+
+ org.apache.zookeeper
+ zookeeper
+
+
+ com.google.guava
+ guava
+
+
+ jersey-core
+ com.sun.jersey
+
+
+ jersey-json
+ com.sun.jersey
+
+
+ jersey-server
+ com.sun.jersey
+
+
+ servlet-api
+ javax.servlet
+
+
+ org.mortbay.jetty
+ jetty
+
+
+ org.mortbay.jetty
+ jetty-util
+
+
+ jasper-compiler
+ tomcat
+
+
+ jasper-runtime
+ tomcat
+
+
+ jsp-api
+ javax.servlet.jsp
+
+
+ slf4j-api
+ org.slf4j
+
+
+
+
+ org.apache.hadoop
+ hadoop-mapreduce-client-core
+ ${hadoop.version}
+ provided
+
+
+ org.slf4j
+ slf4j-log4j12
+
+
+ com.google.inject.extensions
+ guice-servlet
+
+
+ com.sun.jersey
+ jersey-core
+
+
+ com.sun.jersey
+ jersey-server
+
+
+ com.sun.jersey
+ jersey-json
+
+
+ com.sun.jersey.contribs
+ jersey-guice
+
+
+ javax.servlet
+ servlet-api
+
+
+ com.google.guava
+ guava
+
+
+
+
+ com.google.http-client
+ google-http-client-gson
+ ${google-http-client-gson.version}
+
+
+
+
+ io.cdap.cdap
+ cdap-data-pipeline
+ ${cdap.version}
+ test
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+ org.mockito
+ mockito-all
+ ${mockito.version}
+ test
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+
+ io.github.benas
+ random-beans
+ test
+ ${random-beans.version}
+
+
+ io.cdap.cdap
+ hydrator-test
+ ${cdap.version}
+ test
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.9.8
+ test
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+ 2.9.0
+ test
+
+
+ com.google.inject
+ guice
+ 4.2.2
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.7.0
+
+ 1.8
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.14.1
+
+ -Xmx5000m -Djava.awt.headless=true -XX:+UseG1GC -XX:OnOutOfMemoryError="kill -9 %p"
+ -Djava.net.preferIPv4Stack=true
+
+ false
+ plain
+
+ ${project.build.directory}
+
+
+ **/*TestsSuite.java
+ **/*TestSuite.java
+ **/Test*.java
+ **/*Test.java
+ **/*TestCase.java
+
+
+ **/*TestRun.java
+
+
+
+
+ org.apache.maven.plugins
+ maven-checkstyle-plugin
+ 2.17
+
+
+ validate
+ process-test-classes
+
+ checkstyle.xml
+ suppressions.xml
+ UTF-8
+ true
+ true
+ true
+ **/org/apache/cassandra/**,**/org/apache/hadoop/**
+
+
+ check
+
+
+
+
+
+ com.puppycrawl.tools
+ checkstyle
+ 6.19
+
+
+
+
+ org.apache.felix
+ maven-bundle-plugin
+ 3.3.0
+ true
+
+
+ *;inline=false;scope=compile
+ true
+
+ <_exportcontents>io.cdap.plugin.github.*
+ lib
+
+
+
+
+ package
+
+ bundle
+
+
+
+
+
+ io.cdap
+ cdap-maven-plugin
+ 1.1.0
+
+
+ system:cdap-data-pipeline[6.1.0-SNAPSHOT,7.0.0-SNAPSHOT)
+
+
+
+
+ create-artifact-config
+ prepare-package
+
+ create-plugin-json
+
+
+
+
+
+
+
+
diff --git a/src/main/java/io/cdap/plugin/github/source/batch/GithubBatchSource.java b/src/main/java/io/cdap/plugin/github/source/batch/GithubBatchSource.java
new file mode 100644
index 0000000..92e79e4
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/batch/GithubBatchSource.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.batch;
+
+import com.google.common.base.Preconditions;
+import io.cdap.cdap.api.annotation.Description;
+import io.cdap.cdap.api.annotation.Name;
+import io.cdap.cdap.api.annotation.Plugin;
+import io.cdap.cdap.api.data.batch.Input;
+import io.cdap.cdap.api.data.format.StructuredRecord;
+import io.cdap.cdap.api.data.schema.Schema;
+import io.cdap.cdap.api.dataset.lib.KeyValue;
+import io.cdap.cdap.etl.api.Emitter;
+import io.cdap.cdap.etl.api.FailureCollector;
+import io.cdap.cdap.etl.api.PipelineConfigurer;
+import io.cdap.cdap.etl.api.batch.BatchSource;
+import io.cdap.cdap.etl.api.batch.BatchSourceContext;
+import io.cdap.plugin.common.IdUtils;
+import io.cdap.plugin.common.LineageRecorder;
+import io.cdap.plugin.github.source.common.DatasetTransformer;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+import org.apache.hadoop.io.NullWritable;
+
+import java.util.stream.Collectors;
+
+/**
+ * Plugin returns records from Github API V3.
+ */
+@Plugin(type = BatchSource.PLUGIN_TYPE)
+@Name(GithubBatchSource.NAME)
+@Description(GithubBatchSource.DESCRIPTION)
+public class GithubBatchSource extends BatchSource {
+
+ public static final String NAME = "GithubBatchSource";
+ public static final String DESCRIPTION = "Reads data from Github API.";
+
+ private final GithubBatchSourceConfig config;
+
+ public GithubBatchSource(GithubBatchSourceConfig config) {
+ this.config = config;
+ }
+
+ public void prepareRun(BatchSourceContext batchSourceContext) {
+ validateConfiguration(batchSourceContext.getFailureCollector());
+ LineageRecorder lineageRecorder = new LineageRecorder(batchSourceContext, config.referenceName);
+ lineageRecorder.createExternalDataset(config.getSchema());
+ lineageRecorder.recordRead("Read", "Reading Github data",
+ Preconditions.checkNotNull(config.getSchema().getFields()).stream()
+ .map(Schema.Field::getName)
+ .collect(Collectors.toList()));
+
+ batchSourceContext.setInput(Input.of(config.referenceName, new GithubFormatProvider(config)));
+ }
+
+ @Override
+ public void configurePipeline(PipelineConfigurer pipelineConfigurer) {
+ FailureCollector failureCollector = pipelineConfigurer.getStageConfigurer().getFailureCollector();
+ IdUtils.validateReferenceName(config.referenceName, failureCollector);
+ validateConfiguration(failureCollector);
+ pipelineConfigurer.getStageConfigurer().setOutputSchema(config.getSchema());
+ }
+
+ @Override
+ public void transform(KeyValue input, Emitter emitter) {
+ emitter.emit(DatasetTransformer.transform(input.getValue(), config.getSchema()));
+ }
+
+ private void validateConfiguration(FailureCollector failureCollector) {
+ config.validate(failureCollector);
+ failureCollector.getOrThrowException();
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/batch/GithubBatchSourceConfig.java b/src/main/java/io/cdap/plugin/github/source/batch/GithubBatchSourceConfig.java
new file mode 100644
index 0000000..7ae7a68
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/batch/GithubBatchSourceConfig.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.cdap.plugin.github.source.batch;
+
+import com.google.common.base.Strings;
+import io.cdap.cdap.api.annotation.Description;
+import io.cdap.cdap.api.annotation.Macro;
+import io.cdap.cdap.api.annotation.Name;
+import io.cdap.cdap.api.data.schema.Schema;
+import io.cdap.cdap.etl.api.FailureCollector;
+import io.cdap.plugin.common.ReferencePluginConfig;
+import io.cdap.plugin.github.source.common.SchemaBuilder;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+import io.cdap.plugin.github.source.common.model.impl.Branch;
+import io.cdap.plugin.github.source.common.model.impl.Collaborator;
+import io.cdap.plugin.github.source.common.model.impl.Comment;
+import io.cdap.plugin.github.source.common.model.impl.Commit;
+import io.cdap.plugin.github.source.common.model.impl.Content;
+import io.cdap.plugin.github.source.common.model.impl.DeployKey;
+import io.cdap.plugin.github.source.common.model.impl.Deployment;
+import io.cdap.plugin.github.source.common.model.impl.Fork;
+import io.cdap.plugin.github.source.common.model.impl.Invitation;
+import io.cdap.plugin.github.source.common.model.impl.Page;
+import io.cdap.plugin.github.source.common.model.impl.Release;
+import io.cdap.plugin.github.source.common.model.impl.TrafficReferrer;
+import io.cdap.plugin.github.source.common.model.impl.Webhook;
+
+import javax.annotation.Nullable;
+
+/**
+ * Provides all required configuration for reading Github data from Batch Source.
+ */
+public class GithubBatchSourceConfig extends ReferencePluginConfig {
+
+ public static final String AUTHORIZATION_TOKEN = "authorizationToken";
+ public static final String AUTHORIZATION_TOKEN_DISPLAY_NAME = "Authorization token";
+ public static final String REPOSITORY_OWNER = "repoOwner";
+ public static final String REPOSITORY_OWNER_DISPLAY_NAME = "Repository owner name";
+ public static final String REPOSITORY_NAME = "repoName";
+ public static final String REPOSITORY_NAME_DISPLAY_NAME = "Repository name";
+ public static final String DATASET_NAME = "datasetName";
+ public static final String DATASET_NAME_DISPLAY_NAME = "Dataset name";
+ public static final String HOSTNAME = "hostname";
+
+ @Name(AUTHORIZATION_TOKEN)
+ @Description("Authorization token to access GitHub API")
+ @Macro
+ protected String authorizationToken;
+
+ @Name(REPOSITORY_OWNER)
+ @Description("GitHub repository owner")
+ @Macro
+ protected String repoOwner;
+
+ @Name(REPOSITORY_NAME)
+ @Description("GitHub repository name")
+ @Macro
+ protected String repoName;
+
+ @Name(DATASET_NAME)
+ @Description("Dataset name that you would like to retrieve")
+ @Macro
+ protected String datasetName;
+
+ @Name(HOSTNAME)
+ @Description("GitHub API hostname")
+ @Nullable
+ @Macro
+ protected String hostname;
+
+ private transient Schema schema = null;
+
+ public GithubBatchSourceConfig(String referenceName) {
+ super(referenceName);
+ }
+
+ public Schema getSchema() {
+ if (schema == null) {
+ schema = SchemaBuilder.buildSchema(datasetName, getDatasetClass());
+ }
+ return schema;
+ }
+
+ public String getAuthorizationToken() {
+ return authorizationToken;
+ }
+
+ public String getRepoOwner() {
+ return repoOwner;
+ }
+
+ public String getRepoName() {
+ return repoName;
+ }
+
+ public String getDatasetName() {
+ return datasetName;
+ }
+
+ public Class extends GitHubModel> getDatasetClass() {
+ switch (datasetName) {
+ case "Branches": {
+ return Branch.class;
+ }
+ case "Collaborators": {
+ return Collaborator.class;
+ }
+ case "Comments": {
+ return Comment.class;
+ }
+ case "Commits": {
+ return Commit.class;
+ }
+ case "Contents": {
+ return Content.class;
+ }
+ case "Deploy Keys": {
+ return DeployKey.class;
+ }
+ case "Deployments": {
+ return Deployment.class;
+ }
+ case "Forks": {
+ return Fork.class;
+ }
+ case "Invitations": {
+ return Invitation.class;
+ }
+ case "Pages": {
+ return Page.class;
+ }
+ case "Releases": {
+ return Release.class;
+ }
+ case "Traffic:Referrers": {
+ return TrafficReferrer.class;
+ }
+ case "Webhooks": {
+ return Webhook.class;
+ }
+ default: {
+ throw new IllegalArgumentException("Unsupported dataset name!");
+ }
+ }
+ }
+
+ @Nullable
+ public String getHostname() {
+ return hostname;
+ }
+
+ public void validate(FailureCollector failureCollector) {
+ if (!containsMacro(authorizationToken) && Strings.isNullOrEmpty(authorizationToken)) {
+ failureCollector
+ .addFailure(String.format("%s must be specified.", AUTHORIZATION_TOKEN_DISPLAY_NAME), null)
+ .withConfigProperty(AUTHORIZATION_TOKEN_DISPLAY_NAME);
+ }
+ if (!containsMacro(repoOwner) && Strings.isNullOrEmpty(repoOwner)) {
+ failureCollector
+ .addFailure(String.format("%s must be specified.", REPOSITORY_OWNER_DISPLAY_NAME), null)
+ .withConfigProperty(REPOSITORY_OWNER_DISPLAY_NAME);
+ }
+ if (!containsMacro(repoName) && Strings.isNullOrEmpty(repoName)) {
+ failureCollector
+ .addFailure(String.format("%s must be specified.", REPOSITORY_NAME_DISPLAY_NAME), null)
+ .withConfigProperty(REPOSITORY_NAME_DISPLAY_NAME);
+ }
+ if (!containsMacro(datasetName) && Strings.isNullOrEmpty(datasetName)) {
+ failureCollector
+ .addFailure(String.format("%s must be specified.", DATASET_NAME_DISPLAY_NAME), null)
+ .withConfigProperty(DATASET_NAME_DISPLAY_NAME);
+ }
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/batch/GithubFormatProvider.java b/src/main/java/io/cdap/plugin/github/source/batch/GithubFormatProvider.java
new file mode 100644
index 0000000..4ca1157
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/batch/GithubFormatProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.batch;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import io.cdap.cdap.api.data.batch.InputFormatProvider;
+
+import java.util.Map;
+
+/**
+ * InputFormatProvider used by cdap to provide configurations to mapreduce job
+ */
+public class GithubFormatProvider implements InputFormatProvider {
+
+ public static final String PROPERTY_CONFIG_JSON = "cdap.github.config";
+ private static final Gson GSON = new GsonBuilder().create();
+
+ private final Map conf;
+
+ public GithubFormatProvider(GithubBatchSourceConfig config) {
+ this.conf = new ImmutableMap.Builder()
+ .put(PROPERTY_CONFIG_JSON, GSON.toJson(config))
+ .build();
+ }
+
+ @Override
+ public String getInputFormatClassName() {
+ return GithubInputFormat.class.getName();
+ }
+
+ @Override
+ public Map getInputFormatConfiguration() {
+ return conf;
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/batch/GithubInputFormat.java b/src/main/java/io/cdap/plugin/github/source/batch/GithubInputFormat.java
new file mode 100644
index 0000000..01d8e2d
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/batch/GithubInputFormat.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.batch;
+
+import com.google.api.client.http.HttpRequest;
+import com.google.api.client.http.HttpResponse;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import io.cdap.plugin.github.source.common.GitHubRequestFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.mapreduce.InputFormat;
+import org.apache.hadoop.mapreduce.InputSplit;
+import org.apache.hadoop.mapreduce.JobContext;
+import org.apache.hadoop.mapreduce.RecordReader;
+import org.apache.hadoop.mapreduce.TaskAttemptContext;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * InputFormat for mapreduce job, which provides a single split of data.
+ */
+public class GithubInputFormat extends InputFormat {
+
+ private static final Gson GSON = new GsonBuilder().create();
+
+ @Override
+ public List getSplits(JobContext jobContext) throws IOException {
+ Configuration conf = jobContext.getConfiguration();
+ String configJson = conf.get(GithubFormatProvider.PROPERTY_CONFIG_JSON);
+ GithubBatchSourceConfig config = GSON.fromJson(configJson, GithubBatchSourceConfig.class);
+
+ String url = GitHubRequestFactory.generateFirstCallUrl(config);
+ HttpRequest httpRequest = GitHubRequestFactory.buildRequest(url, config.getAuthorizationToken());
+ HttpResponse response = httpRequest.execute();
+ Object paginationMetadata = response.getHeaders().get("Link");
+ if (Objects.nonNull(paginationMetadata)) {
+ String paginationUrls = (String) ((List>) paginationMetadata).get(0);
+ String lastLink = getLastLink(paginationUrls);
+
+ Integer totalPagesCount = getTotalPagesCount(lastLink);
+ return IntStream.range(1, totalPagesCount)
+ .mapToObj(pageNumber -> {
+ String pageLink = transformLink(lastLink, pageNumber);
+ return new GithubSplit(pageLink);
+ })
+ .collect(Collectors.toList());
+ } else {
+ return Collections.singletonList(new GithubSplit(url));
+ }
+ }
+
+
+ @Override
+ public RecordReader createRecordReader(InputSplit inputSplit, TaskAttemptContext taskAttemptContext) {
+ Configuration conf = taskAttemptContext.getConfiguration();
+ String configJson = conf.get(GithubFormatProvider.PROPERTY_CONFIG_JSON);
+ GithubBatchSourceConfig config = GSON.fromJson(configJson, GithubBatchSourceConfig.class);
+ return new GithubRecordReader(config, ((GithubSplit) inputSplit).getLink());
+ }
+
+ private String transformLink(String link, Integer pageNumber) {
+ int indexOfLastParamValue = link.lastIndexOf("=");
+ String substring = link.substring(0, indexOfLastParamValue + 1);
+ return substring + pageNumber;
+ }
+
+ private String getLastLink(String paginationHeader) {
+ String[] links = paginationHeader.split(",");
+ for (String link : links) {
+ if (link.contains("last")) {
+ String[] parts = link.split(";");
+ String url = parts[0];
+ return url.substring(2, url.length() - 1);
+ }
+ }
+ return null;
+ }
+
+ private Integer getTotalPagesCount(String lastLink) {
+ String[] split = lastLink.split("\\?");
+ String paramsString = split[1];
+ String[] params = paramsString.split("&");
+
+ for (String param : params) {
+ String[] keyValueString = param.split("=");
+ if ("page".equals(keyValueString[0])) {
+ return Integer.valueOf(keyValueString[1]);
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/batch/GithubRecordReader.java b/src/main/java/io/cdap/plugin/github/source/batch/GithubRecordReader.java
new file mode 100644
index 0000000..81276f6
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/batch/GithubRecordReader.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.batch;
+
+import com.google.api.client.http.HttpRequest;
+import com.google.api.client.http.HttpResponse;
+import io.cdap.plugin.github.source.common.GitHubRequestFactory;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+import org.apache.hadoop.io.NullWritable;
+import org.apache.hadoop.mapreduce.InputSplit;
+import org.apache.hadoop.mapreduce.RecordReader;
+import org.apache.hadoop.mapreduce.TaskAttemptContext;
+
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.Iterator;
+
+import static io.cdap.plugin.github.source.common.GitHubRequestFactory.DEFAULT_PAGE_SIZE;
+
+/**
+ * RecordReader implementation, which reads {@link GitHubModel} instances from GitHub repository API
+ */
+public class GithubRecordReader extends RecordReader {
+
+ private final GithubBatchSourceConfig config;
+ private final String link;
+
+ private Iterator currentPage;
+ private GitHubModel currentRow;
+ private Integer currentRowIndex = 0;
+
+ public GithubRecordReader(GithubBatchSourceConfig config, String link) {
+ this.config = config;
+ this.link = link;
+ }
+
+ @Override
+ public void initialize(InputSplit inputSplit, TaskAttemptContext taskAttemptContext) throws IOException {
+ HttpRequest httpRequest = GitHubRequestFactory.buildRequest(link, config.getAuthorizationToken());
+ HttpResponse response = httpRequest.execute();
+ Class extends GitHubModel[]> datasetClass = (Class extends GitHubModel[]>)
+ Array.newInstance(config.getDatasetClass(), 0).getClass();
+ currentPage = Arrays.stream(response.parseAs(datasetClass)).iterator();
+ }
+
+ @Override
+ public boolean nextKeyValue() {
+ if (!currentPage.hasNext()) {
+ return false;
+ }
+ currentRowIndex++;
+ currentRow = currentPage.next();
+ return true;
+ }
+
+ @Override
+ public NullWritable getCurrentKey() {
+ return null;
+ }
+
+ @Override
+ public GitHubModel getCurrentValue() {
+ return currentRow;
+ }
+
+ @Override
+ public float getProgress() {
+ return currentRowIndex / (float) DEFAULT_PAGE_SIZE;
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/batch/GithubSplit.java b/src/main/java/io/cdap/plugin/github/source/batch/GithubSplit.java
new file mode 100644
index 0000000..361c6ee
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/batch/GithubSplit.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package io.cdap.plugin.github.source.batch;
+
+import org.apache.hadoop.io.Writable;
+import org.apache.hadoop.mapreduce.InputSplit;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+/**
+ * A no-op split.
+ */
+public class GithubSplit extends InputSplit implements Writable {
+
+ private String link;
+
+ public GithubSplit() {
+ // For serialization
+ }
+
+ public GithubSplit(String link) {
+ this.link = link;
+ }
+
+ @Override
+ public void write(DataOutput dataOutput) throws IOException {
+ dataOutput.writeUTF(link);
+ }
+
+ @Override
+ public void readFields(DataInput dataInput) throws IOException {
+ this.link = dataInput.readUTF();
+ }
+
+ @Override
+ public long getLength() {
+ return 0;
+ }
+
+ @Override
+ public String[] getLocations() {
+ return new String[0];
+ }
+
+ public String getLink() {
+ return link;
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/DatasetTransformer.java b/src/main/java/io/cdap/plugin/github/source/common/DatasetTransformer.java
new file mode 100644
index 0000000..b27b85a
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/DatasetTransformer.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common;
+
+import io.cdap.cdap.api.data.format.StructuredRecord;
+import io.cdap.cdap.api.data.schema.Schema;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * This is helper class for transforming {@link GitHubModel} instance to {@link StructuredRecord}.
+ */
+public class DatasetTransformer {
+
+ /**
+ * Transforms {@link GitHubModel} instance to {@link StructuredRecord} instance accordingly to given schema.
+ */
+ public static StructuredRecord transform(Object model, Schema schema) {
+ try {
+ StructuredRecord.Builder builder = StructuredRecord.builder(schema);
+ Class> clazz = model.getClass();
+ List schemaFields = schema.getFields();
+ for (Schema.Field schemaField : schemaFields) {
+ Schema.Type schemaType = schemaField.getSchema().getType();
+ Field field = getFieldByName(schemaField.getName(), clazz);
+ if (schemaType.isSimpleType() || Schema.Type.UNION.equals(schemaType)) {
+ builder.set(schemaField.getName(), field.get(model));
+ } else if (Schema.Type.ARRAY.equals(schemaType)) {
+ Schema componentSchema = schemaField.getSchema().getComponentSchema();
+ Object result = field.get(model);
+ if (Objects.nonNull(componentSchema) && !componentSchema.isSimpleOrNullableSimple()) {
+ result = ((List>) result).stream()
+ .map(arrItem -> transform(arrItem, componentSchema))
+ .collect(Collectors.toList());
+ }
+ builder.set(schemaField.getName(), result);
+ } else {
+ StructuredRecord structuredRecord = transform(field.get(model), schemaField.getSchema());
+ builder.set(schemaField.getName(), structuredRecord);
+ }
+ }
+ return builder.build();
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(String.format("Exception when transforming %s to StructuredRecord",
+ model.getClass().getSimpleName()), e);
+ }
+ }
+
+ private static Field getFieldByName(String fieldName, Class> clazz) {
+ Field declaredField = null;
+ Class> currentClass = clazz;
+ while (currentClass != Object.class) {
+ try {
+ declaredField = currentClass.getDeclaredField(fieldName);
+ declaredField.setAccessible(true);
+ return declaredField;
+ } catch (NoSuchFieldException e) {
+ currentClass = currentClass.getSuperclass();
+ }
+ }
+ return declaredField;
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/GitHubRequestFactory.java b/src/main/java/io/cdap/plugin/github/source/common/GitHubRequestFactory.java
new file mode 100644
index 0000000..5eccf85
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/GitHubRequestFactory.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common;
+
+import com.google.api.client.http.GenericUrl;
+import com.google.api.client.http.HttpRequest;
+import com.google.api.client.http.HttpRequestFactory;
+import com.google.api.client.http.javanet.NetHttpTransport;
+import com.google.api.client.json.JsonObjectParser;
+import com.google.api.client.json.gson.GsonFactory;
+import io.cdap.plugin.github.source.batch.GithubBatchSourceConfig;
+
+import java.io.IOException;
+
+/**
+ * Helper class to create GitHub data requests.
+ */
+public class GitHubRequestFactory {
+
+ public static final Integer DEFAULT_PAGE_SIZE = 100;
+
+ private static HttpRequestFactory requestFactory = new NetHttpTransport()
+ .createRequestFactory((HttpRequest request) ->
+ request.setParser(new JsonObjectParser(GsonFactory.getDefaultInstance())));
+
+ public static String generateFirstCallUrl(GithubBatchSourceConfig config) {
+ String host = config.getHostname() != null ? config.getHostname() : "https://api.github.com";
+ return host + "/repos" + "/" + config.getRepoOwner() + "/" + config.getRepoName() + "/" +
+ getPathByDatasetName(config.getDatasetName()) + "?per_page=" + DEFAULT_PAGE_SIZE;
+ }
+
+ public static HttpRequest buildRequest(String url, String authToken) throws IOException {
+ HttpRequest httpRequest = requestFactory.buildGetRequest(new GenericUrl(url));
+ addHeaders(httpRequest, authToken);
+ return httpRequest;
+ }
+
+ private static void addHeaders(HttpRequest httpRequest, String authToken) {
+ httpRequest.getHeaders().setAuthorization("token " + authToken);
+ httpRequest.getHeaders().setUserAgent("curl/7.37.0");
+ }
+
+ private static String getPathByDatasetName(String dataset) {
+
+ switch (dataset) {
+ case "Branches": {
+ return "branches";
+ }
+ case "Collaborators": {
+ return "collaborators";
+ }
+ case "Comments": {
+ return "comments";
+ }
+ case "Commits": {
+ return "commits";
+ }
+ case "Contents": {
+ return "contents";
+ }
+ case "Deploy Keys": {
+ return "keys";
+ }
+ case "Deployments": {
+ return "deployments";
+ }
+ case "Forks": {
+ return "forks";
+ }
+ case "Invitations": {
+ return "invitations";
+ }
+ case "Pages": {
+ return "pages";
+ }
+ case "Releases": {
+ return "releases";
+ }
+ case "Traffic:Referrers": {
+ return "traffic/popular/referrers";
+ }
+ case "Webhooks": {
+ return "hooks";
+ }
+ default: {
+ throw new IllegalArgumentException(String.format("Unsupported dataset name %s!", dataset));
+ }
+ }
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/SchemaBuilder.java b/src/main/java/io/cdap/plugin/github/source/common/SchemaBuilder.java
new file mode 100644
index 0000000..936513a
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/SchemaBuilder.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common;
+
+import io.cdap.cdap.api.data.schema.Schema;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Helper class to map GitHub repository fields sets to final {@link Schema}.
+ */
+public class SchemaBuilder {
+
+ public static Schema buildSchema(String schemaName, Class> model) {
+ List fields = getModelFields(model);
+ List schemaFields = new ArrayList<>();
+ for (Field field : fields) {
+ Schema.Type schemaType = getSchemaType(field.getType());
+ if (schemaType.isSimpleType()) {
+ schemaFields.add(getSimpleSchemaField(field));
+ } else if (Schema.Type.ARRAY.equals(schemaType)) {
+ ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
+ Class> clazz = (Class>) parameterizedType.getActualTypeArguments()[0];
+ Schema.Type arraySchemaType = getSchemaType(clazz);
+ if (!arraySchemaType.isSimpleType()) {
+ Schema schema = buildSchema(field.getName(), clazz);
+ schemaFields.add(Schema.Field.of(field.getName(), Schema.arrayOf(schema)));
+ } else {
+ schemaFields.add(Schema.Field.of(field.getName(),
+ Schema.arrayOf(Schema.of(getSchemaType(clazz)))));
+ }
+ } else {
+ Schema schema = buildSchema(field.getName(), field.getType());
+ schemaFields.add(Schema.Field.of(field.getName(), schema));
+ }
+ }
+ return Schema.recordOf(schemaName, schemaFields);
+ }
+
+ public static List getModelFields(Class> clazz) {
+ List fields = new ArrayList<>();
+ Class currentClass = clazz;
+ while (currentClass != Object.class) {
+ Field[] declaredFields = currentClass.getDeclaredFields();
+ fields.addAll(Arrays.asList(declaredFields));
+ currentClass = currentClass.getSuperclass();
+ }
+ return fields;
+ }
+
+ private static Schema.Field getSimpleSchemaField(Field field) {
+ return Schema.Field.of(field.getName(), Schema.nullableOf(Schema.of(
+ getSchemaType(field.getType()))));
+ }
+
+ private static Schema.Type getSchemaType(Class clazz) {
+ if (String.class.equals(clazz)) {
+ return Schema.Type.STRING;
+ } else if (Integer.class.equals(clazz)) {
+ return Schema.Type.INT;
+ } else if (Long.class.equals(clazz)) {
+ return Schema.Type.LONG;
+ } else if (Boolean.class.equals(clazz)) {
+ return Schema.Type.BOOLEAN;
+ } else if (List.class.equals(clazz)) {
+ return Schema.Type.ARRAY;
+ } else {
+ return Schema.Type.RECORD;
+ }
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/GitHubModel.java b/src/main/java/io/cdap/plugin/github/source/common/model/GitHubModel.java
new file mode 100644
index 0000000..742121f
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/GitHubModel.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model;
+
+/**
+ * Generic GitHub model interface
+ */
+public interface GitHubModel {
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/Branch.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Branch.java
new file mode 100644
index 0000000..fc82f93
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Branch.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+
+import java.util.List;
+
+/**
+ * Branch model
+ */
+public class Branch implements GitHubModel {
+
+ @Key
+ private String name;
+ @Key
+ private Commit commit;
+ @Key("protected")
+ private Boolean isProtected;
+ @Key
+ private Protection protection;
+ @Key("protection_url")
+ private String protectionUrl;
+
+ /**
+ * Branch.Commit model
+ */
+ public static class Commit {
+ @Key
+ private String sha;
+ @Key
+ private String url;
+ }
+
+ /**
+ * Branch.Protection model
+ */
+ public static class Protection {
+ @Key
+ private Boolean enabled;
+ @Key("required_status_checks")
+ private RequiredStatusChecks requiredStatusChecks;
+
+ /**
+ * Branch.Protection.RequiredStatusChecks model
+ */
+ public static class RequiredStatusChecks {
+ @Key("enforcement_level")
+ private String enforcementLevel;
+ @Key
+ private List contexts;
+ }
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/Collaborator.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Collaborator.java
new file mode 100644
index 0000000..c349529
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Collaborator.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.impl.user.User;
+
+/**
+ * Collaborator model
+ */
+public class Collaborator extends User {
+
+ @Key
+ private Permissions permissions;
+
+ /**
+ * Collaborator.Permissions model
+ */
+ public static class Permissions {
+ @Key
+ private Boolean pull;
+ @Key
+ private Boolean push;
+ @Key
+ private Boolean admin;
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/Comment.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Comment.java
new file mode 100644
index 0000000..f3c8036
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Comment.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+import io.cdap.plugin.github.source.common.model.impl.user.User;
+
+/**
+ * Comment model
+ */
+public class Comment implements GitHubModel {
+
+ @Key("html_url")
+ private String htmlUrl;
+ @Key
+ private String url;
+ @Key
+ private Long id;
+ @Key("node_id")
+ private String nodeId;
+ @Key
+ private String body;
+ @Key
+ private String path;
+ @Key
+ private Integer position;
+ @Key
+ private Integer line;
+ @Key("commit_id")
+ private String commitId;
+ @Key
+ private User user;
+ @Key("created_at")
+ private String createdAt;
+ @Key("updated_at")
+ private String updatedAt;
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/Commit.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Commit.java
new file mode 100644
index 0000000..d0994a0
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Commit.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+import io.cdap.plugin.github.source.common.model.impl.user.User;
+
+import java.util.List;
+
+/**
+ * Commit model
+ */
+public class Commit implements GitHubModel {
+
+ @Key
+ private String url;
+ @Key
+ private String sha;
+ @Key("node_id")
+ private String nodeId;
+ @Key("html_url")
+ private String htmlUrl;
+ @Key("comments_url")
+ private String commentsUrl;
+ @Key
+ private CommitData commit;
+ @Key("author")
+ private User mainAuthor;
+ @Key("committer")
+ private User mainCommitter;
+ @Key
+ private List parents;
+
+ /**
+ * Commit.CommitData model
+ */
+ public static class CommitData {
+ @Key
+ private String url;
+ @Key
+ private CommitUser author;
+ @Key
+ private CommitUser committer;
+ @Key
+ private String message;
+ @Key
+ private Tree tree;
+ @Key("comment_count")
+ private Integer commentCount;
+ @Key
+ private Verification verification;
+
+ /**
+ * Commit.CommitData.CommitUser model
+ */
+ public static class CommitUser {
+ @Key
+ private String name;
+ @Key
+ private String email;
+ @Key
+ private String date;
+ }
+
+ /**
+ * Commit.CommitData.Tree model
+ */
+ public static class Tree {
+ @Key
+ private String url;
+ @Key
+ private String sha;
+ }
+
+ /**
+ * Commit.CommitData.Verification model
+ */
+ public static class Verification {
+ @Key
+ private Boolean verified;
+ @Key
+ private String reason;
+ @Key
+ private String signature;
+ @Key
+ private String payload;
+ }
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/Content.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Content.java
new file mode 100644
index 0000000..45061ea
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Content.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+
+/**
+ * Content model
+ */
+public class Content implements GitHubModel {
+
+ @Key
+ private String type;
+ @Key
+ private String encoding;
+ @Key
+ private Long size;
+ @Key
+ private String name;
+ @Key
+ private String path;
+ @Key
+ private String content;
+ @Key
+ private String sha;
+ @Key
+ private String url;
+ @Key("git_url")
+ private String gitUrl;
+ @Key("html_url")
+ private String htmlUrl;
+ @Key("download_url")
+ private String downloadUrl;
+ @Key("_links")
+ private Link links;
+
+ /**
+ * Content.Link model
+ */
+ public static class Link {
+ @Key
+ private String git;
+ @Key
+ private String self;
+ @Key
+ private String html;
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/DeployKey.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/DeployKey.java
new file mode 100644
index 0000000..0ac7264
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/DeployKey.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+
+/**
+ * DeployKey model
+ */
+public class DeployKey implements GitHubModel {
+
+ @Key
+ private Long id;
+ @Key
+ private String key;
+ @Key
+ private String url;
+ @Key
+ private String title;
+ @Key
+ private Boolean verified;
+ @Key("created_at")
+ private String createdAt;
+ @Key("read_only")
+ private Boolean readOnly;
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/Deployment.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Deployment.java
new file mode 100644
index 0000000..87ce0be
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Deployment.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+import io.cdap.plugin.github.source.common.model.impl.user.User;
+
+/**
+ * Deployment model
+ */
+public class Deployment implements GitHubModel {
+
+ @Key
+ private String url;
+ @Key
+ private Long id;
+ @Key("node_id")
+ private String nodeId;
+ @Key
+ private String sha;
+ @Key
+ private String ref;
+ @Key
+ private String task;
+ @Key
+ private Payload payload;
+ @Key("original_environment")
+ private String originalEnvironment;
+ @Key
+ private String environment;
+ @Key
+ private String description;
+ @Key
+ private User creator;
+ @Key("created_at")
+ private String createdAt;
+ @Key("updated_at")
+ private String updatedAt;
+ @Key("statuses_url")
+ private String statusesUrl;
+ @Key("repository_url")
+ private String repositoryUrl;
+ @Key("transient_environment")
+ private Boolean transientEnvironment;
+ @Key("production_environment")
+ private Boolean productionEnvironment;
+
+ /**
+ * Deployment.Payload model
+ */
+ public static class Payload {
+ @Key
+ private String deploy;
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/Fork.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Fork.java
new file mode 100644
index 0000000..2e7c9f2
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Fork.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+import io.cdap.plugin.github.source.common.model.impl.user.User;
+
+import java.util.List;
+
+/**
+ * Fork model
+ */
+public class Fork implements GitHubModel {
+
+ @Key
+ private Long id;
+ @Key("node_id")
+ private String nodeId;
+ @Key
+ private String name;
+ @Key("full_name")
+ private String fullName;
+ @Key
+ private User owner;
+ @Key("private")
+ private Boolean isPrivate;
+ @Key("html_url")
+ private String htmlUrl;
+ @Key
+ private String description;
+ @Key
+ private Boolean fork;
+ @Key
+ private String url;
+ @Key("archive_url")
+ private String archiveUrl;
+ @Key("assignees_url")
+ private String assigneesUrl;
+ @Key("blobs_url")
+ private String blobsUrl;
+ @Key("branches_url")
+ private String branchesUrl;
+ @Key("collaborators_url")
+ private String collaboratorsUrl;
+ @Key("comments_url")
+ private String commentsUrl;
+ @Key("commits_url")
+ private String commitsUrl;
+ @Key("compare_url")
+ private String compareUrl;
+ @Key("contents_url")
+ private String contentsUrl;
+ @Key("contributors_url")
+ private String contributorsUrl;
+ @Key("deployments_url")
+ private String deploymentsUrl;
+ @Key("downloads_url")
+ private String downloadsUrl;
+ @Key("events_url")
+ private String eventsUrl;
+ @Key("forks_url")
+ private String forksUrl;
+ @Key("git_commits_url")
+ private String gitCommitsUrl;
+ @Key("git_refs_url")
+ private String gitRefsUrl;
+ @Key("git_tags_url")
+ private String gitTagsUrl;
+ @Key("git_url")
+ private String gitUrl;
+ @Key("issue_comment_url")
+ private String issueCommentUrl;
+ @Key("issue_events_url")
+ private String issueEventsUrl;
+ @Key("issues_url")
+ private String issuesUrl;
+ @Key("keys_url")
+ private String keysUrl;
+ @Key("labels_url")
+ private String labelsUrl;
+ @Key("languages_url")
+ private String languagesUrl;
+ @Key("merges_url")
+ private String mergesUrl;
+ @Key("milestones_url")
+ private String milestonesUrl;
+ @Key("notifications_url")
+ private String notificationsUrl;
+ @Key("pulls_url")
+ private String pullsUrl;
+ @Key("releases_url")
+ private String releasesUrl;
+ @Key("ssh_url")
+ private String sshUrl;
+ @Key("stargazers_url")
+ private String stargazersUrl;
+ @Key("statuses_url")
+ private String statusesUrl;
+ @Key("subscribers_url")
+ private String subscribersUrl;
+ @Key("subscription_url")
+ private String subscriptionUrl;
+ @Key("tags_url")
+ private String tagsUrl;
+ @Key("teams_url")
+ private String teamsUrl;
+ @Key("trees_url")
+ private String treesUrl;
+ @Key("clone_url")
+ private String cloneUrl;
+ @Key("mirror_url")
+ private String mirrorUrl;
+ @Key("hooks_url")
+ private String hooksUrl;
+ @Key("svn_url")
+ private String svnUrl;
+ @Key
+ private String homepage;
+ @Key
+ private String language;
+ @Key("forks_count")
+ private Integer forksCount;
+ @Key("stargazers_count")
+ private Integer stargazersCount;
+ @Key("watchers_count")
+ private Integer watchersCount;
+ @Key
+ private Long size;
+ @Key("default_branch")
+ private String defaultBranch;
+ @Key("open_issues_count")
+ private Integer openIssuesCount;
+ @Key("is_template")
+ private Boolean isTemplate;
+ @Key
+ private List topics;
+ @Key("has_issues")
+ private Boolean hasIssues;
+ @Key("has_projects")
+ private Boolean hasProjects;
+ @Key("has_wiki")
+ private Boolean hasWiki;
+ @Key("has_pages")
+ private Boolean hasPages;
+ @Key("has_downloads")
+ private Boolean hasDownloads;
+ @Key
+ private Boolean archived;
+ @Key
+ private Boolean disabled;
+ @Key("pushed_at")
+ private String pushedAt;
+ @Key("created_at")
+ private String createdAt;
+ @Key("updated_at")
+ private String updatedAt;
+ @Key
+ private Permissions permissions;
+ @Key("template_repository")
+ private String templateRepository;
+ @Key("subscribers_count")
+ private Integer subscribersCount;
+ @Key("network_count")
+ private Integer networkCount;
+ @Key
+ private License license;
+
+ /**
+ * Fork.License model
+ */
+ public static class License {
+ @Key
+ private String key;
+ @Key
+ private String name;
+ @Key("spdx_id")
+ private String spdxId;
+ @Key
+ private String url;
+ @Key("node_id")
+ private String nodeId;
+ }
+
+ /**
+ * Fork.Permissions model
+ */
+ public static class Permissions {
+ @Key
+ private Boolean pull;
+ @Key
+ private Boolean push;
+ @Key
+ private Boolean admin;
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/Invitation.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Invitation.java
new file mode 100644
index 0000000..6c10091
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Invitation.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+import io.cdap.plugin.github.source.common.model.impl.user.User;
+
+/**
+ * Invitation model
+ */
+public class Invitation implements GitHubModel {
+
+ @Key
+ private Long id;
+ @Key
+ private Repository repository;
+ @Key
+ private User invitee;
+ @Key
+ private User inviter;
+ @Key
+ private String permissions;
+ @Key("created_at")
+ private String createdAt;
+ @Key
+ private String url;
+ @Key("html_url")
+ private String htmlUrl;
+
+ /**
+ * Invitation.Repository model
+ */
+ public static class Repository {
+ @Key
+ private Long id;
+ @Key("node_id")
+ private String nodeId;
+ @Key
+ private String name;
+ @Key("full_name")
+ private String fullName;
+ @Key
+ private User owner;
+ @Key("private")
+ private Boolean isPrivate;
+ @Key("html_url")
+ private String htmlUrl;
+ @Key
+ private String description;
+ @Key
+ private Boolean fork;
+ @Key
+ private String url;
+ @Key("archive_url")
+ private String archiveUrl;
+ @Key("assignees_url")
+ private String assigneesUrl;
+ @Key("blobs_url")
+ private String blobsUrl;
+ @Key("branches_url")
+ private String branchesUrl;
+ @Key("collaborators_url")
+ private String collaboratorsUrl;
+ @Key("comments_url")
+ private String commentsUrl;
+ @Key("commits_url")
+ private String commitsUrl;
+ @Key("compare_url")
+ private String compareUrl;
+ @Key("contents_url")
+ private String contentsUrl;
+ @Key("contributors_url")
+ private String contributorsUrl;
+ @Key("deployments_url")
+ private String deploymentsUrl;
+ @Key("downloads_url")
+ private String downloadsUrl;
+ @Key("events_url")
+ private String eventsUrl;
+ @Key("forks_url")
+ private String forksUrl;
+ @Key("git_commits_url")
+ private String gitCommitsUrl;
+ @Key("git_refs_url")
+ private String gitRefsUrl;
+ @Key("git_tags_url")
+ private String gitTagsUrl;
+ @Key("git_url")
+ private String gitUrl;
+ @Key("issue_comment_url")
+ private String issueCommentUrl;
+ @Key("issue_events_url")
+ private String issueEventsUrl;
+ @Key("issues_url")
+ private String issuesUrl;
+ @Key("labels_url")
+ private String labelsUrl;
+ @Key("languages_url")
+ private String languagesUrl;
+ @Key("merges_url")
+ private String mergesUrl;
+ @Key("milestones_url")
+ private String milestonesUrl;
+ @Key("notifications_url")
+ private String notificationsUrl;
+ @Key("pulls_url")
+ private String pullsUrl;
+ @Key("releases_url")
+ private String releasesUrl;
+ @Key("ssh_url")
+ private String sshUrl;
+ @Key("stargazers_url")
+ private String stargazersUrl;
+ @Key("statuses_url")
+ private String statusesUrl;
+ @Key("subscribers_url")
+ private String subscribersUrl;
+ @Key("subscription_url")
+ private String subscriptionUrl;
+ @Key("tags_url")
+ private String tagsUrl;
+ @Key("teams_url")
+ private String teamsUrl;
+ @Key("trees_url")
+ private String treesUrl;
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/Page.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Page.java
new file mode 100644
index 0000000..6cc64c6
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Page.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+
+/**
+ * Page model
+ */
+public class Page implements GitHubModel {
+
+ @Key
+ private String url;
+ @Key
+ private String status;
+ @Key
+ private String cname;
+ @Key("custom_404")
+ private Boolean custom404;
+ @Key("html_url")
+ private String htmlUrl;
+ @Key
+ private Source source;
+
+ /**
+ * Page.Source model
+ */
+ public static class Source {
+ @Key
+ private String branch;
+ @Key
+ private String directory;
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/Release.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Release.java
new file mode 100644
index 0000000..242e291
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Release.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+import io.cdap.plugin.github.source.common.model.impl.user.User;
+
+import java.util.List;
+
+
+/**
+ * Release model
+ */
+public class Release implements GitHubModel {
+
+ @Key
+ private String url;
+ @Key("html_url")
+ private String htmlUrl;
+ @Key("assets_url")
+ private String assetsUrl;
+ @Key("upload_url")
+ private String uploadUrl;
+ @Key("tarball_url")
+ private String tarballUrl;
+ @Key("zipball_url")
+ private String zipballUrl;
+ @Key
+ private Long id;
+ @Key("node_id")
+ private String nodeId;
+ @Key("tag_name")
+ private String tagName;
+ @Key("target_commitish")
+ private String targetCommitish;
+ @Key
+ private String name;
+ @Key
+ private String body;
+ @Key
+ private Boolean draft;
+ @Key
+ private Boolean prerelease;
+ @Key("created_at")
+ private String createdAt;
+ @Key("published_at")
+ private String publishedAt;
+ @Key
+ private User author;
+ @Key
+ private List assets;
+
+ /**
+ * Release.Assert model
+ */
+ public static class Assert {
+ @Key
+ private String url;
+ @Key("browser_download_url")
+ private String browserDownloadUrl;
+ @Key
+ private Long id;
+ @Key("node_id")
+ private String nodeId;
+ @Key
+ private String name;
+ @Key
+ private String label;
+ @Key
+ private String state;
+ @Key("content_type")
+ private String contentType;
+ @Key
+ private Long size;
+ @Key("download_count")
+ private Integer downloadCount;
+ @Key("created_at")
+ private String createdAt;
+ @Key("updated_at")
+ private String updatedAt;
+ @Key
+ private User uploader;
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/TrafficReferrer.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/TrafficReferrer.java
new file mode 100644
index 0000000..67261a2
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/TrafficReferrer.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+
+/**
+ * TrafficReferrer model
+ */
+public class TrafficReferrer implements GitHubModel {
+
+ @Key
+ private String referrer;
+ @Key
+ private Integer count;
+ @Key
+ private Integer uniques;
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/Webhook.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Webhook.java
new file mode 100644
index 0000000..38fc246
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/Webhook.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+
+import java.util.List;
+
+/**
+ * Webhook model
+ */
+public class Webhook implements GitHubModel {
+
+ @Key
+ private String type;
+ @Key
+ private Long id;
+ @Key
+ private String name;
+ @Key
+ private Boolean active;
+ @Key
+ private List events;
+ @Key
+ private Config config;
+ @Key("updated_at")
+ private String updatedAt;
+ @Key("created_at")
+ private String createdAt;
+ @Key
+ private String url;
+ @Key("test_url")
+ private String testUrl;
+ @Key("ping_url")
+ private String pingUrl;
+ @Key
+ private LastResponse lastResponse;
+
+ /**
+ * Webhook.Config model
+ */
+ public static class Config {
+ @Key("content_type")
+ private String contentType;
+ @Key("insecure_ssl")
+ private String insecureSsl;
+ @Key
+ private String url;
+ }
+
+ /**
+ * Webhook.LastResponse model
+ */
+ public static class LastResponse {
+ @Key
+ private String code;
+ @Key
+ private String status;
+ @Key
+ private String message;
+ }
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/user/OwnerUser.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/user/OwnerUser.java
new file mode 100644
index 0000000..3f4e9f7
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/user/OwnerUser.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl.user;
+
+import com.google.api.client.util.Key;
+
+/**
+ * OwnerUser model
+ */
+public class OwnerUser extends User {
+
+ @Key
+ private String owner;
+}
diff --git a/src/main/java/io/cdap/plugin/github/source/common/model/impl/user/User.java b/src/main/java/io/cdap/plugin/github/source/common/model/impl/user/User.java
new file mode 100644
index 0000000..539716a
--- /dev/null
+++ b/src/main/java/io/cdap/plugin/github/source/common/model/impl/user/User.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright © 2019 Cask Data, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package io.cdap.plugin.github.source.common.model.impl.user;
+
+import com.google.api.client.util.Key;
+import io.cdap.plugin.github.source.common.model.GitHubModel;
+
+/**
+ * User model
+ */
+public class User implements GitHubModel {
+
+ @Key
+ private Long id;
+ @Key("node_id")
+ private String nodeId;
+ @Key("avatar_url")
+ private String avatarUrl;
+ @Key("gravatar_id")
+ private String gravatarId;
+ @Key
+ private String url;
+ @Key("html_url")
+ private String htmlUrl;
+ @Key("followers_url")
+ private String followersUrl;
+ @Key("following_url")
+ private String followingUrl;
+ @Key("gists_url")
+ private String gistsUrl;
+ @Key("starred_url")
+ private String starredUrl;
+ @Key("subscriptions_url")
+ private String subscriptionsUrl;
+ @Key("organizations_url")
+ private String organizationsUrl;
+ @Key("repos_url")
+ private String reposUrl;
+ @Key("events_url")
+ private String eventsUrl;
+ @Key("received_events_url")
+ private String receivedEventsUrl;
+ @Key
+ private String type;
+ @Key("site_admin")
+ private Boolean siteAdmin;
+}
diff --git a/src/test/java/io/cdap/plugin/github/source/batch/GithubBatchSourceConfigTest.java b/src/test/java/io/cdap/plugin/github/source/batch/GithubBatchSourceConfigTest.java
new file mode 100644
index 0000000..710b053
--- /dev/null
+++ b/src/test/java/io/cdap/plugin/github/source/batch/GithubBatchSourceConfigTest.java
@@ -0,0 +1,136 @@
+package io.cdap.plugin.github.source.batch;
+
+import io.cdap.cdap.etl.api.validation.ValidationFailure;
+import io.cdap.cdap.etl.mock.validation.MockFailureCollector;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.Collection;
+
+import static io.cdap.plugin.github.source.batch.GithubBatchSourceConfig.AUTHORIZATION_TOKEN;
+import static io.cdap.plugin.github.source.batch.GithubBatchSourceConfig.DATASET_NAME;
+import static io.cdap.plugin.github.source.batch.GithubBatchSourceConfig.REPOSITORY_NAME;
+import static io.cdap.plugin.github.source.batch.GithubBatchSourceConfig.REPOSITORY_OWNER;
+
+public class GithubBatchSourceConfigTest {
+
+ private MockFailureCollector failureCollector;
+
+ @Before
+ public void setUp() {
+ failureCollector = new MockFailureCollector();
+ }
+
+ @Test
+ public void testValidateFieldsCaseCorrectFields() {
+ //given
+ GithubBatchSourceConfig config = new GithubBatchSourceConfig("ref");
+ config.authorizationToken = "token";
+ config.repoOwner = "owner";
+ config.repoName = "repo";
+ config.datasetName = "dataset";
+
+ //when
+ config.validate(failureCollector);
+
+ //then
+ Assert.assertTrue(failureCollector.getValidationFailures().isEmpty());
+ }
+
+ @Test
+ public void testValidateFieldsCaseEmptyFields() {
+ //given
+ GithubBatchSourceConfig config = new GithubBatchSourceConfig("ref");
+
+ //when
+ config.validate(failureCollector);
+
+ //then
+ Assert.assertEquals(4, failureCollector.getValidationFailures().size());
+ }
+
+ @Test
+ public void testValidateConfigCaseAuthTokenNull() {
+ //given
+ GithubBatchSourceConfig config = new GithubBatchSourceConfig("ref");
+ MockFailureCollector failureCollector = new MockFailureCollector();
+ config.repoOwner = "owner";
+ config.repoName = "repo";
+ config.datasetName = "dataset";
+
+ //when
+ config.validate(failureCollector);
+
+ boolean isStartDateFailure = failureCollector.getValidationFailures().stream()
+ .map(ValidationFailure::getCauses)
+ .flatMap(Collection::stream)
+ .anyMatch(cause -> cause.getAttributes().containsValue(AUTHORIZATION_TOKEN));
+
+ //then
+ Assert.assertTrue(isStartDateFailure);
+ }
+
+ @Test
+ public void testValidateConfigCaseRepoOwnerNull() {
+ //given
+ GithubBatchSourceConfig config = new GithubBatchSourceConfig("ref");
+ MockFailureCollector failureCollector = new MockFailureCollector();
+ config.authorizationToken = "token";
+ config.repoName = "repo";
+ config.datasetName = "dataset";
+
+ //when
+ config.validate(failureCollector);
+
+ boolean isStartDateFailure = failureCollector.getValidationFailures().stream()
+ .map(ValidationFailure::getCauses)
+ .flatMap(Collection::stream)
+ .anyMatch(cause -> cause.getAttributes().containsValue(REPOSITORY_OWNER));
+
+ //then
+ Assert.assertTrue(isStartDateFailure);
+ }
+
+ @Test
+ public void testValidateConfigCaseRepoNameNull() {
+ //given
+ GithubBatchSourceConfig config = new GithubBatchSourceConfig("ref");
+ MockFailureCollector failureCollector = new MockFailureCollector();
+ config.authorizationToken = "token";
+ config.repoOwner = "owner";
+ config.datasetName = "dataset";
+
+ //when
+ config.validate(failureCollector);
+
+ boolean isStartDateFailure = failureCollector.getValidationFailures().stream()
+ .map(ValidationFailure::getCauses)
+ .flatMap(Collection::stream)
+ .anyMatch(cause -> cause.getAttributes().containsValue(REPOSITORY_NAME));
+
+ //then
+ Assert.assertTrue(isStartDateFailure);
+ }
+
+ @Test
+ public void testValidateConfigCaseDatasetNameNull() {
+ //given
+ GithubBatchSourceConfig config = new GithubBatchSourceConfig("ref");
+ MockFailureCollector failureCollector = new MockFailureCollector();
+ config.authorizationToken = "token";
+ config.repoOwner = "owner";
+ config.repoName = "repo";
+
+ //when
+ config.validate(failureCollector);
+
+ boolean isStartDateFailure = failureCollector.getValidationFailures().stream()
+ .map(ValidationFailure::getCauses)
+ .flatMap(Collection::stream)
+ .anyMatch(cause -> cause.getAttributes().containsValue(DATASET_NAME));
+
+ //then
+ Assert.assertTrue(isStartDateFailure);
+ }
+}
diff --git a/src/test/java/io/cdap/plugin/github/source/common/DatasetTransformerTest.java b/src/test/java/io/cdap/plugin/github/source/common/DatasetTransformerTest.java
new file mode 100644
index 0000000..b86b771
--- /dev/null
+++ b/src/test/java/io/cdap/plugin/github/source/common/DatasetTransformerTest.java
@@ -0,0 +1,61 @@
+package io.cdap.plugin.github.source.common;
+
+import io.cdap.cdap.api.data.format.StructuredRecord;
+import io.cdap.cdap.api.data.schema.Schema;
+import io.cdap.plugin.github.source.common.model.impl.Commit;
+import io.github.benas.randombeans.api.EnhancedRandom;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+import static io.github.benas.randombeans.EnhancedRandomBuilder.aNewEnhancedRandom;
+
+@RunWith(Parameterized.class)
+public class DatasetTransformerTest {
+
+ private static final EnhancedRandom random = aNewEnhancedRandom();
+
+ private Class> clazz;
+ private Object model;
+
+ public DatasetTransformerTest(Class> clazz) {
+ this.clazz = clazz;
+ this.model = random.nextObject(clazz);
+ }
+
+ @Parameterized.Parameters
+ public static Collection