Skip to content

Commit

Permalink
Merge pull request #124 from amihaiemil/123
Browse files Browse the repository at this point in the history
#123 select Project by input OR full_name
  • Loading branch information
amihaiemil authored Aug 29, 2021
2 parents 57164fa + 838de19 commit 6e168af
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 40 deletions.
94 changes: 54 additions & 40 deletions src/main/java/com/selfxdsd/selfpm/Webhooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,19 +95,14 @@ public Webhooks(final Self selfCore, final SelfTodos selfTodos) {

/**
* Webhook for Github projects.
* @param owner Owner's username (can be a user or organization name).
* @param owner Owner's username (can be a user or an organization name).
* @param name Repo's name.
* @param type Event type.
* @param signature Signature sent by Github.
* @param payload JSON Payload.
* @return ResponseEntity.
* @checkstyle ReturnCount (150 lines)
* @todo #118:60min We now read the repoFullName from the event payload. We
* cannot rely on input owner and name because the repo's name may
* change and we cannot update the Webhook when this happens.
* Remove the owner and name from this endpoint as they are not
* needed anymore. This first requires changes in the way self-core is
* setting up the webhook.
* @checkstyle ExecutableStatementCount (150 lines)
* @todo #118:120min Update the signature calculation based on the new
* X-Github-Signature-256 header, as described here:
* https://docs.github.com/en/developers/webhooks-and-events/webhooks
Expand All @@ -124,44 +119,63 @@ public ResponseEntity<Void> github(
final @RequestHeader("X-Hub-Signature") String signature,
final @RequestBody String payload
) {
final JsonObject repository = Json.createReader(
new StringReader(payload)
).readObject().getJsonObject("repository");
if(repository == null) {
return ResponseEntity.badRequest().build();
} else {
final String repoFullName = repository.getString("full_name");
LOG.debug(
"Received Github Webhook [" + type + "] from Repo "
+ repoFullName.split("/")[0] + "/"
+ repoFullName.split("/")[1] + ". "
);
final Project project = this.selfCore.projects().getProjectById(
repoFullName,
Provider.Names.GITHUB
);
if (project != null) {
final String calculated = this.hmacHexDigest(
project.webHookToken(),
payload
LOG.debug(
"Received Github Webhook [" + type + "] from Repo "
+ owner + "/" + name + ". "
);
Project project = this.selfCore.projects().getProjectById(
owner + "/" + name,
Provider.Names.GITHUB
);
if(project == null) {
LOG.debug("Project not found, trying repository.full_name.");
final JsonObject repository = Json.createReader(
new StringReader(payload)
).readObject().getJsonObject("repository");
if (repository == null) {
LOG.debug("repository object not found, bad request.");
return ResponseEntity.badRequest().build();
} else {
final String fullName = repository.getString("full_name");
LOG.debug("Found full_name " + fullName + "... ");
project = this.selfCore.projects().getProjectById(
fullName,
Provider.Names.GITHUB
);
if(calculated != null && calculated.equals(signature)) {
if("push".equalsIgnoreCase(type)) {
this.selfTodos.post(project, payload);
} else {
project.resolve(
WebhookEvents.create(project, type, payload)
);
}
} else {
System.out.println("CALCULATED: " + calculated);
return ResponseEntity.badRequest().build();
if (project == null) {
LOG.debug(
"Project " + fullName + " not found either. No Content."
);
return ResponseEntity.noContent().build();
}
}
}
LOG.debug(
"Found Project " + project.repoFullName()
+ ". Calculating signature..."
);
final String calculated = this.hmacHexDigest(
project.webHookToken(),
payload
);
if(calculated != null && calculated.equals(signature)) {
LOG.debug("Signature OK.");
if("push".equalsIgnoreCase(type)) {
LOG.debug("POSTing push event to SelfTodos...");
this.selfTodos.post(project, payload);
LOG.debug("Successfully posted.");
} else {
return ResponseEntity.noContent().build();
LOG.debug("Resolving webhook event...");
project.resolve(
WebhookEvents.create(project, type, payload)
);
LOG.debug("Event successfully resolved.");
}
return ResponseEntity.ok().build();
} else {
LOG.debug("Signature doesn't match. Bad Request.");
return ResponseEntity.badRequest().build();
}
return ResponseEntity.ok().build();
}

/**
Expand Down
101 changes: 101 additions & 0 deletions src/test/java/com/selfxdsd/selfpm/WebhooksTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -458,4 +458,105 @@ public void gitlabWebhookResolvesEvent() {
Mockito.times(1)
).resolve(Mockito.any(Event.class));
}

/**
* A Github project resolves ok, but the Project is not found by the initial
* input, it's found by the full_name from the payload.
*/
@Test
public void githubProjectResolvesOkFoundByFullName() {
final Project project = Mockito.mock(Project.class);
Mockito.when(project.repoFullName()).thenReturn("john/newName");
Mockito.when(project.webHookToken()).thenReturn("project_wh_token");
Mockito.when(project.provider()).thenReturn(Provider.Names.GITHUB);
Mockito.doNothing()
.when(project)
.resolve(Mockito.any(Event.class));
final Projects all = Mockito.mock(Projects.class);
Mockito.when(
all.getProjectById("john/test", Provider.Names.GITHUB)
).thenReturn(null);
Mockito.when(
all.getProjectById("john/newName", Provider.Names.GITHUB)
).thenReturn(project);
final Self self = Mockito.mock(Self.class);
Mockito.when(self.projects()).thenReturn(all);
final Webhooks hook = new Webhooks(self);
MatcherAssert.assertThat(
hook.github(
"john",
"test",
"issues",
"sha1=3af1d9e7033bccac6c22cd4c6f2844f233d73794",
"{\"repository\":{\"full_name\":\"john/newName\"}}"
).getStatusCode(),
Matchers.equalTo(HttpStatus.OK)
);
}

/**
* A Github event is received, but the Project is not found by the initial
* input. It is also not found by the full_name from the payload.
*/
@Test
public void githubProjectNotFoundByFullName() {
final Project project = Mockito.mock(Project.class);
Mockito.when(project.webHookToken()).thenReturn("project_wh_token");
Mockito.when(project.provider()).thenReturn(Provider.Names.GITHUB);
Mockito.doNothing()
.when(project)
.resolve(Mockito.any(Event.class));
final Projects all = Mockito.mock(Projects.class);
Mockito.when(
all.getProjectById("john/test", Provider.Names.GITHUB)
).thenReturn(null);
Mockito.when(
all.getProjectById("john/newName", Provider.Names.GITHUB)
).thenReturn(null);
final Self self = Mockito.mock(Self.class);
Mockito.when(self.projects()).thenReturn(all);
final Webhooks hook = new Webhooks(self);
MatcherAssert.assertThat(
hook.github(
"john",
"test",
"issues",
"sha1=3af1d9e7033bccac6c22cd4c6f2844f233d73794",
"{\"repository\":{\"full_name\":\"john/newName\"}}"
).getStatusCode(),
Matchers.equalTo(HttpStatus.NO_CONTENT)
);
}

/**
* A Github webhook event is received and the Project is not found using the
* input values. We try to get the full_name from the repository object in
* the payload, but it is missing.
*/
@Test
public void githubProjectMissingRepositoryElement() {
final Project project = Mockito.mock(Project.class);
Mockito.when(project.webHookToken()).thenReturn("project_wh_token");
Mockito.when(project.provider()).thenReturn(Provider.Names.GITHUB);
Mockito.doNothing()
.when(project)
.resolve(Mockito.any(Event.class));
final Projects all = Mockito.mock(Projects.class);
Mockito.when(
all.getProjectById("john/test", Provider.Names.GITHUB)
).thenReturn(null);
final Self self = Mockito.mock(Self.class);
Mockito.when(self.projects()).thenReturn(all);
final Webhooks hook = new Webhooks(self);
MatcherAssert.assertThat(
hook.github(
"john",
"test",
"issues",
"sha1=3af1d9e7033bccac6c22cd4c6f2844f233d73794",
"{}"
).getStatusCode(),
Matchers.equalTo(HttpStatus.BAD_REQUEST)
);
}
}

0 comments on commit 6e168af

Please sign in to comment.