Skip to content

Commit

Permalink
Support for associating WebHook templates with Projects
Browse files Browse the repository at this point in the history
This allows a WebHook Template to be created and associated with a
project rather then just _Root.
Now group admins can create templates for use with their own webhooks.
This addresses issue #131

Note: WebHook Template Ids must be unique across a teamcity instance,
even if the user creating the template is not aware and is not
permissioned to see an existing template with that ID.

- Uses templateId rather than templateName when referring to ID.
- Added projectId field and getProjectId method to templates.
- Added validators and permissions
- Fixed up permissions for template editing in REST API
- Added Templates to WebHook Project tab
- Added Templates list to Webhook Project Config tab.
- Added Project Name and link to Templates page.
- WebHooks now resolve project specific templates when building payload.
  • Loading branch information
netwolfuk committed Mar 17, 2020
1 parent ac9b9bf commit 9cfb3e7
Show file tree
Hide file tree
Showing 27 changed files with 2,159 additions and 1,661 deletions.
Original file line number Diff line number Diff line change
@@ -1,71 +1,71 @@
package webhook.teamcity.server.rest;

import org.jetbrains.annotations.NotNull;

import jetbrains.buildServer.server.rest.PathTransformer;
import jetbrains.buildServer.server.rest.util.ValueWithDefault.Value;
import webhook.teamcity.server.rest.data.WebHookTemplateItemConfigWrapper.WebHookTemplateItemRest;
import webhook.teamcity.server.rest.request.TemplateRequest;
import webhook.teamcity.server.rest.request.WebHooksRequest;
import webhook.teamcity.server.rest.util.webhook.WebHookManager;
import webhook.teamcity.settings.WebHookConfig;
import webhook.teamcity.settings.config.WebHookTemplateConfig;

/**
* Adds the WebHooks urls into the resolver.
* @author netwolfuk
*
*/
public class WebHookApiUrlBuilder {

private PathTransformer myPathTransformer;

public WebHookApiUrlBuilder(@NotNull final PathTransformer pathTransformer) {
myPathTransformer = pathTransformer;
}

public String getHref(final WebHookTemplateConfig template) {
return myPathTransformer.transform(TemplateRequest.getTemplateHref(template));
}

public String getHref(String projectExternalId, WebHookConfig config) {
return myPathTransformer.transform(WebHooksRequest.getWebHookHref(projectExternalId, config));
}

public String getTemplateDefaultItemHref(WebHookTemplateConfig WebHookTemplateConfig) {
return myPathTransformer.transform(TemplateRequest.getTemplateDefaultItemHref(WebHookTemplateConfig));
}

public String getTemplateItemHref(WebHookTemplateConfig WebHookTemplateConfig, WebHookTemplateItemRest webHookTemplateItem) {
return myPathTransformer.transform(TemplateRequest.getTemplateItemHref(WebHookTemplateConfig, webHookTemplateItem));
}

public String getDefaultTemplateTextHref(final WebHookTemplateConfig template) {
return myPathTransformer.transform(TemplateRequest.getDefaultTemplateTextHref(template));
}

public String getDefaultBranchTemplateTextHref(final WebHookTemplateConfig template) {
return myPathTransformer.transform(TemplateRequest.getDefaultBranchTemplateTextHref(template));
}

public String getTemplateItemTextHref(WebHookTemplateConfig WebHookTemplateConfig, WebHookTemplateItemRest webHookTemplateItem) {
return myPathTransformer.transform(TemplateRequest.getTemplateItemTextHref(WebHookTemplateConfig, webHookTemplateItem));
}

public String getTemplateItemBranchTextHref(WebHookTemplateConfig WebHookTemplateConfig, WebHookTemplateItemRest webHookTemplateItem) {
return myPathTransformer.transform(TemplateRequest.getTemplateItemBranchTextHref(WebHookTemplateConfig, webHookTemplateItem));
}

public String transformRelativePath(final String internalRelativePath) {
return myPathTransformer.transform(internalRelativePath);
}

public String getWebHookTemplateStateUrl(WebHookTemplateConfig template, String state) {
return myPathTransformer.transform(TemplateRequest.getTemplateStateHref(template, state));
}

public String getWebHookTemplateItemStateUrl(WebHookTemplateConfig template, WebHookTemplateItemRest templateItem, String state) {
return myPathTransformer.transform(TemplateRequest.getTemplateItemStateHref(template, templateItem, state));
}

}
package webhook.teamcity.server.rest;

import org.jetbrains.annotations.NotNull;

import jetbrains.buildServer.server.rest.PathTransformer;
import jetbrains.buildServer.server.rest.util.ValueWithDefault.Value;
import webhook.teamcity.server.rest.data.WebHookTemplateItemConfigWrapper.WebHookTemplateItemRest;
import webhook.teamcity.server.rest.request.TemplateRequest;
import webhook.teamcity.server.rest.request.WebHooksRequest;
import webhook.teamcity.server.rest.util.webhook.WebHookManager;
import webhook.teamcity.settings.WebHookConfig;
import webhook.teamcity.settings.config.WebHookTemplateConfig;

/**
* Adds the WebHooks urls into the resolver.
* @author netwolfuk
*
*/
public class WebHookApiUrlBuilder {

private PathTransformer myPathTransformer;

public WebHookApiUrlBuilder(@NotNull final PathTransformer pathTransformer) {
myPathTransformer = pathTransformer;
}

public String getHref(final WebHookTemplateConfig template) {
return myPathTransformer.transform(TemplateRequest.getTemplateHref(template));
}

public String getHref(String projectExternalId, WebHookConfig config) {
return myPathTransformer.transform(WebHooksRequest.getWebHookHref(projectExternalId, config));
}

public String getTemplateDefaultItemHref(WebHookTemplateConfig WebHookTemplateConfig) {
return myPathTransformer.transform(TemplateRequest.getTemplateDefaultItemHref(WebHookTemplateConfig));
}

public String getTemplateItemHref(WebHookTemplateConfig WebHookTemplateConfig, WebHookTemplateItemRest webHookTemplateItem) {
return myPathTransformer.transform(TemplateRequest.getTemplateItemHref(WebHookTemplateConfig, webHookTemplateItem));
}

public String getDefaultTemplateTextHref(final WebHookTemplateConfig template) {
return myPathTransformer.transform(TemplateRequest.getDefaultTemplateTextHref(template));
}

public String getDefaultBranchTemplateTextHref(final WebHookTemplateConfig template) {
return myPathTransformer.transform(TemplateRequest.getDefaultBranchTemplateTextHref(template));
}

public String getTemplateItemTextHref(WebHookTemplateConfig WebHookTemplateConfig, WebHookTemplateItemRest webHookTemplateItem) {
return myPathTransformer.transform(TemplateRequest.getTemplateItemTextHref(WebHookTemplateConfig, webHookTemplateItem));
}

public String getTemplateItemBranchTextHref(WebHookTemplateConfig WebHookTemplateConfig, WebHookTemplateItemRest webHookTemplateItem) {
return myPathTransformer.transform(TemplateRequest.getTemplateItemBranchTextHref(WebHookTemplateConfig, webHookTemplateItem));
}

public String transformRelativePath(final String internalRelativePath) {
return myPathTransformer.transform(internalRelativePath);
}

public String getWebHookTemplateStateUrl(WebHookTemplateConfig template, String state) {
return myPathTransformer.transform(TemplateRequest.getTemplateStateHref(template, state));
}

public String getWebHookTemplateItemStateUrl(WebHookTemplateConfig template, WebHookTemplateItemRest templateItem, String state) {
return myPathTransformer.transform(TemplateRequest.getTemplateItemStateHref(template, templateItem, state));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import jetbrains.buildServer.server.rest.data.PermissionChecker;
import jetbrains.buildServer.serverSide.ProjectManager;
import jetbrains.buildServer.serverSide.SBuildServer;
import jetbrains.buildServer.serverSide.auth.SecurityContext;
import webhook.teamcity.ProjectIdResolver;
import webhook.teamcity.payload.WebHookPayloadManager;
import webhook.teamcity.payload.WebHookTemplateManager;
import webhook.teamcity.payload.WebHookTemplateManager.TemplateState;
Expand All @@ -26,6 +28,9 @@ public class DataProvider {
@NotNull private final ProjectManager myProjectManager;
@NotNull private final WebHookManager myWebHookManager;
@NotNull private final WebHookFinder myWebHookFinder;
@NotNull private final ProjectIdResolver myProjectIdResolver;
@NotNull private final SecurityContext mySecurityContext;


public DataProvider(@NotNull final SBuildServer server,
@NotNull final RootUrlHolder rootUrlHolder,
Expand All @@ -35,7 +40,10 @@ public DataProvider(@NotNull final SBuildServer server,
@NotNull final TemplateFinder templateFinder,
@NotNull final ProjectManager projectManager,
@NotNull final WebHookManager webHookManager,
@NotNull final WebHookFinder webHookFinder){
@NotNull final WebHookFinder webHookFinder,
@NotNull final ProjectIdResolver projectIdResolver,
@NotNull final SecurityContext securityContext
){

this.myServer = server;
this.myRootUrlHolder = rootUrlHolder;
Expand All @@ -46,13 +54,16 @@ public DataProvider(@NotNull final SBuildServer server,
this.myProjectManager = projectManager;
this.myWebHookManager = webHookManager;
this.myWebHookFinder = webHookFinder;
this.myProjectIdResolver = projectIdResolver;
this.mySecurityContext = securityContext;

}

public List<WebHookTemplateConfigWrapper> getWebHookTemplates(){
List<WebHookTemplateConfigWrapper> templates = new ArrayList<>();
for (WebHookTemplateConfig template : this.myTemplateManager.getRegisteredTemplateConfigs()){
templates.add(new WebHookTemplateConfigWrapper(template,
for (WebHookTemplateConfig template : this.myTemplateManager.getRegisteredPermissionedTemplateConfigs()){
templates.add(new WebHookTemplateConfigWrapper(template,
myProjectIdResolver.getExternalProjectId(template.getProjectInternalId()),
this.myTemplateManager.getTemplateState(template.getId(), TemplateState.BEST),
WebHookTemplateStates.build(template)
)
Expand Down Expand Up @@ -88,4 +99,12 @@ public WebHookManager getWebHookManager() {
public WebHookFinder getWebHookFinder() {
return myWebHookFinder;
}

public ProjectIdResolver getProjectIdResolver() {
return myProjectIdResolver;
}

public SecurityContext getSecurityContext() {
return mySecurityContext;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import jetbrains.buildServer.server.rest.errors.BadRequestException;
import jetbrains.buildServer.server.rest.errors.NotFoundException;
import jetbrains.buildServer.util.StringUtil;
import webhook.teamcity.ProjectIdResolver;
import webhook.teamcity.payload.WebHookTemplateManager;
import webhook.teamcity.payload.WebHookTemplateManager.TemplateState;
import webhook.teamcity.server.rest.data.WebHookTemplateItemConfigWrapper.WebHookTemplateItemRest;
Expand All @@ -17,14 +18,21 @@
public class TemplateFinder {

@NotNull private final WebHookTemplateManager myTemplateManager;
@NotNull private final ProjectIdResolver myProjectIdResolver;

public TemplateFinder(@NotNull final WebHookTemplateManager templateManager){

public TemplateFinder(@NotNull final WebHookTemplateManager templateManager, @NotNull final ProjectIdResolver projectIdResolver){
myTemplateManager = templateManager;
myProjectIdResolver = projectIdResolver;
}

public static String getLocator(final WebHookTemplateConfig template) {
return Locator.createEmptyLocator().setDimension("id", template.getId()).getStringRepresentation();
}

public static String getProjectLocator(final String projectId) {
return Locator.createEmptyLocator().setDimension("project", projectId).getStringRepresentation();
}

public static String getTemplateTextLocator(final String id){
return Locator.createEmptyLocator().setDimension("id", id).getStringRepresentation();
Expand All @@ -47,9 +55,12 @@ public WebHookTemplateConfigWrapper findTemplateById(String templateLocator) {
final String singleValue = locator.getSingleValue();
template = myTemplateManager.getTemplateConfig(singleValue, TemplateState.BEST);
if (template != null) {
return new WebHookTemplateConfigWrapper(template,
myTemplateManager.getTemplateState(template.getId(), TemplateState.BEST),
WebHookTemplateStates.build(template));
return new WebHookTemplateConfigWrapper(
template,
myProjectIdResolver.getExternalProjectId(template.getProjectInternalId()),
myTemplateManager.getTemplateState(template.getId(), TemplateState.BEST),
WebHookTemplateStates.build(template)
);
}
throw new NotFoundException(
"No template found by id '"
Expand All @@ -61,10 +72,12 @@ public WebHookTemplateConfigWrapper findTemplateById(String templateLocator) {
@NotNull final TemplateState templateState = getTemplateStateDimension(locator);
template = myTemplateManager.getTemplateConfig(templateId, templateState);
if (template != null) {
return new WebHookTemplateConfigWrapper(template,
myTemplateManager.getTemplateState(template.getId(), templateState),
WebHookTemplateStates.build(template)
);
return new WebHookTemplateConfigWrapper(
template,
myProjectIdResolver.getExternalProjectId(template.getProjectInternalId()),
myTemplateManager.getTemplateState(template.getId(), templateState),
WebHookTemplateStates.build(template)
);
}
throw new NotFoundException(
"No template found by id '"
Expand All @@ -77,10 +90,12 @@ public WebHookTemplateConfigWrapper findTemplateById(String templateLocator) {

template = myTemplateManager.getTemplateConfig(templateName, templateState);
if (template != null) {
return new WebHookTemplateConfigWrapper(template,
myTemplateManager.getTemplateState(template.getId(), templateState),
WebHookTemplateStates.build(template)
);
return new WebHookTemplateConfigWrapper(
template,
myProjectIdResolver.getExternalProjectId(template.getProjectInternalId()),
myTemplateManager.getTemplateState(template.getId(), templateState),
WebHookTemplateStates.build(template)
);
}
throw new NotFoundException(
"No template found by name '"
Expand Down Expand Up @@ -136,7 +151,7 @@ private WebHookTemplateItemConfigWrapper buildWebHookTemplateItemConfigWrapper(
templateId,
null
);
return new WebHookTemplateItemConfigWrapper(defaultTemplateItem, templateConfigWrapper.getBuildStatesWithTemplate());
return new WebHookTemplateItemConfigWrapper(templateConfigWrapper.getExternalProjectId(), defaultTemplateItem, templateConfigWrapper.getBuildStatesWithTemplate());
} else {
throw new NotFoundException(
"This template does not have a default template '"
Expand Down
Loading

0 comments on commit 9cfb3e7

Please sign in to comment.