Skip to content

Commit

Permalink
Centralised Webhook Management.
Browse files Browse the repository at this point in the history
- Create new WebHookSettingsManager
- Convert uses of ProjectSettingsManager to WebHookSettingsManager.
- Add support for showing webhook count on Templates list page.
- Prevent template from being deleted if in use.
- Show context relevant information in Template delete dialog.
- Update to Java8 and new versions of spring-core and teamcity (10.0)
- Remove reference to payload format where applicable
- Now supports project ID search
- Hover state on table rows.
- Handle 403 errors from API.
- Fix up template names, and tidy up display beans.
- Add enabled/disabled tag
- Add tags for headers, filters, parameters.
- Add show=all, and unfiltered result count.
- Add webhook count and link to admin tab.
- Add support for displaying tags and generalisedUrl.
- Add buildType search
- Only show full URL if permissioned.
- Show tags in Tags column.
- Add ability to search with buildTypeId=<externalBuildTypeId>
  • Loading branch information
netwolfuk committed Aug 27, 2019
1 parent 8f057f7 commit ac9b9bf
Show file tree
Hide file tree
Showing 21 changed files with 577 additions and 434 deletions.
2 changes: 1 addition & 1 deletion tcwebhooks-rest-api-legacy/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
/.classpath
/.project
/.settings
/bin
/.apt_generated_tests/
/bin
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package webhook.teamcity.server.rest.data;

import java.util.Collection;
import java.util.Objects;
import java.util.stream.Collectors;

import org.jetbrains.annotations.NotNull;

import jetbrains.buildServer.server.rest.data.Locator;
Expand All @@ -8,25 +12,24 @@
import jetbrains.buildServer.server.rest.model.Fields;
import jetbrains.buildServer.serverSide.ProjectManager;
import jetbrains.buildServer.serverSide.SProject;
import jetbrains.buildServer.serverSide.settings.ProjectSettingsManager;
import jetbrains.buildServer.util.StringUtil;
import webhook.teamcity.WebHookListener;
import webhook.teamcity.server.rest.model.webhook.ProjectWebhook;
import webhook.teamcity.server.rest.util.BeanContext;
import webhook.teamcity.settings.WebHookConfig;
import webhook.teamcity.settings.WebHookProjectSettings;
import webhook.teamcity.settings.WebHookSettingsManager;

public class WebHookFinder {

@NotNull private final ProjectManager projectManager;
@NotNull private final ProjectSettingsManager projectSettingsManager;
@NotNull private final WebHookSettingsManager webhookSettingsManager;

public WebHookFinder(
@NotNull final ProjectManager projectManager,
@NotNull final ProjectSettingsManager projectSettingsManager)
@NotNull final WebHookSettingsManager projectSettingsManager)
{
this.projectManager = projectManager;
this.projectSettingsManager = projectSettingsManager;
this.webhookSettingsManager = projectSettingsManager;
}

public static String getLocator(final WebHookConfig webhook) {
Expand All @@ -38,7 +41,7 @@ public WebHookProjectSettings getWebHookProjectSettings(String projectExternalId
if (sProject == null) {
throw new NotFoundException("No project found with that project id");
}
return (WebHookProjectSettings) projectSettingsManager.getSettings(sProject.getProjectId(), WebHookListener.WEBHOOKS_SETTINGS_ATTRIBUTE_NAME);
return webhookSettingsManager.getSettings(sProject.getProjectId());
}

public ProjectWebhook findWebHookById(String projectExternalId, String webHookLocator, final @NotNull Fields fields, @NotNull final BeanContext beanContext) {
Expand All @@ -62,15 +65,28 @@ public ProjectWebhook findWebHookById(String projectExternalId, String webHookLo

throw new BadRequestException("Sorry: Searching for multiple template is not supported.");
}

public int getTemplateUsageCount(String templateId) {
return this.webhookSettingsManager.getTemplateUsageCount(templateId);
}

private ProjectWebhook getWebHookConfigById(String projectExternalId, final Fields fields, final BeanContext beanContext,
final String singleValue) {
for (WebHookConfig webHookConfig : getWebHookProjectSettings(projectExternalId).getWebHooksConfigs()) {

if (singleValue.equals(webHookConfig.getUniqueKey())) {
return new ProjectWebhook(webHookConfig, projectExternalId, fields, beanContext);
return new ProjectWebhook(webHookConfig, projectExternalId, fields, beanContext, webHookConfig.getEnabledBuildTypesSet());
}
}
throw new NotFoundException("Could not find a webhook with that id");
}

public Collection<String> getBuildTypeExternalIds(Collection<String> internalIds) {
return internalIds.stream()
.filter(id ->
Objects.nonNull(this.projectManager.findBuildTypeById(id))
&& Objects.nonNull(this.projectManager.findBuildTypeById(id).getExternalId()))
.map(id -> this.projectManager.findBuildTypeById(id).getExternalId())
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package webhook.teamcity.server.rest.errors;

import webhook.teamcity.server.rest.model.template.ErrorResult;

public class TemplateInUseException extends RuntimeException {

private static final long serialVersionUID = 1062265324610559830L;
private final ErrorResult result;

public TemplateInUseException(String message, ErrorResult result) {
super(message);
this.result = result;
}

public TemplateInUseException(String message, Throwable cause, ErrorResult result) {
super(message, cause);
this.result = result;
}

public ErrorResult getResult() {
return result;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package webhook.teamcity.server.rest.errors;

import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

import jetbrains.buildServer.server.rest.jersey.ExceptionMapperUtil;

@Provider
public class TemplateInUseExceptionMapper extends ExceptionMapperUtil implements ExceptionMapper<TemplateInUseException> {

public Response toResponse(TemplateInUseException exception) {
Response.ResponseBuilder builder = Response.status(409);
builder.entity(exception.getResult());
return builder.build();
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package webhook.teamcity.server.rest.jersey;

import java.lang.reflect.Type;

import javax.ws.rs.core.Context;
import javax.ws.rs.ext.Provider;

import org.jetbrains.annotations.NotNull;

import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.core.spi.component.ComponentScope;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;

import webhook.teamcity.settings.WebHookSettingsManager;

@Provider
public class WebHookSettingsManagerProvider implements InjectableProvider<Context, Type>, Injectable<WebHookSettingsManager> {
private final WebHookSettingsManager webHookSettingsManager;

/**
* Injected by Spring
* @param webHookSettingsManager
*/
public WebHookSettingsManagerProvider(
@NotNull final WebHookSettingsManager webHookSettingsManager
) {
this.webHookSettingsManager = webHookSettingsManager;
}

public ComponentScope getScope() {
return ComponentScope.Singleton;
}

public Injectable<WebHookSettingsManager> getInjectable(final ComponentContext ic, final Context context, final Type type) {
if (type.equals(WebHookSettingsManager.class)) {
return this;
}
return null;
}

public WebHookSettingsManager getValue() {
return webHookSettingsManager;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ public class ErrorResult implements Serializable {
private static final long serialVersionUID = -8395102761842280396L;
private Map<String, String> errors = new LinkedHashMap<>();

public void addError(String fieldname, String errorMessage) {
public ErrorResult addError(String fieldname, String errorMessage) {
this.errors.put(fieldname, errorMessage);
return this;
}

public boolean isErrored() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,31 @@
package webhook.teamcity.server.rest.model.webhook;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

/*
* <custom-template type="buildStatusHtml" template="${branchDisplayName} ${projectId}" enabled="true"/>
*/

@XmlRootElement(name="custom-template")
public class CustomTemplate {
private String type;
private String template;
private Boolean enabled;

@XmlAttribute
public String getType() {
return type;
}

@XmlAttribute
public String getTemplate() {
return template;
}

@XmlAttribute
public Boolean getEnabled() {
return enabled;
}

}
package webhook.teamcity.server.rest.model.webhook;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

/*
* <custom-template type="buildStatusHtml" template="${branchDisplayName} ${projectId}" enabled="true"/>
*/

@XmlRootElement(name="customTemplate")
public class CustomTemplate {
private String type;
private String template;
private Boolean enabled;

@XmlAttribute
public String getType() {
return type;
}

@XmlAttribute
public String getTemplate() {
return template;
}

@XmlAttribute
public Boolean getEnabled() {
return enabled;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package webhook.teamcity.server.rest.model.webhook;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.Collection;

@XmlRootElement(name="buildType")
@NoArgsConstructor
@AllArgsConstructor
@XmlAccessorType(XmlAccessType.FIELD)
@Getter @Setter
public class ProjectWebHookBuildType {

@XmlAttribute
private Boolean allEnabled;

@XmlAttribute
private Boolean subProjectsEnabled;

@XmlElement(name = "id") @XmlElementWrapper(name = "enabledBuildTypes")
private Collection<String> enabledBuildTypes;
}
Loading

0 comments on commit ac9b9bf

Please sign in to comment.