mirror of
https://github.com/theonedev/onedev.git
synced 2025-12-08 18:26:30 +00:00
chore: Add MCP build support
This commit is contained in:
parent
2a1a2a5a43
commit
2cb6b6b7fa
@ -40,6 +40,8 @@ import com.google.common.base.Splitter;
|
||||
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.server.SubscriptionManager;
|
||||
import io.onedev.server.entitymanager.BuildManager;
|
||||
import io.onedev.server.entitymanager.BuildParamManager;
|
||||
import io.onedev.server.entitymanager.IssueChangeManager;
|
||||
import io.onedev.server.entitymanager.IssueCommentManager;
|
||||
import io.onedev.server.entitymanager.IssueLinkManager;
|
||||
@ -57,13 +59,17 @@ import io.onedev.server.entitymanager.PullRequestManager;
|
||||
import io.onedev.server.entitymanager.PullRequestReviewManager;
|
||||
import io.onedev.server.entitymanager.SettingManager;
|
||||
import io.onedev.server.entitymanager.UserManager;
|
||||
import io.onedev.server.entityreference.BuildReference;
|
||||
import io.onedev.server.entityreference.IssueReference;
|
||||
import io.onedev.server.entityreference.PullRequestReference;
|
||||
import io.onedev.server.exception.InvalidIssueFieldsException;
|
||||
import io.onedev.server.exception.InvalidReferenceException;
|
||||
import io.onedev.server.exception.IssueLinkValidationException;
|
||||
import io.onedev.server.exception.PullRequestReviewRejectedException;
|
||||
import io.onedev.server.git.GitUtils;
|
||||
import io.onedev.server.git.service.GitService;
|
||||
import io.onedev.server.job.JobManager;
|
||||
import io.onedev.server.model.Build;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueComment;
|
||||
import io.onedev.server.model.IssueLink;
|
||||
@ -96,6 +102,7 @@ import io.onedev.server.model.support.pullrequest.changedata.PullRequestRequeste
|
||||
import io.onedev.server.rest.annotation.Api;
|
||||
import io.onedev.server.rest.resource.support.RestConstants;
|
||||
import io.onedev.server.search.entity.EntityQuery;
|
||||
import io.onedev.server.search.entity.build.BuildQuery;
|
||||
import io.onedev.server.search.entity.issue.IssueQuery;
|
||||
import io.onedev.server.search.entity.issue.IssueQueryParseOption;
|
||||
import io.onedev.server.search.entity.pullrequest.PullRequestQuery;
|
||||
@ -148,6 +155,12 @@ public class McpHelperResource {
|
||||
|
||||
private final PullRequestCommentManager pullRequestCommentManager;
|
||||
|
||||
private final BuildManager buildManager;
|
||||
|
||||
private final BuildParamManager buildParamManager;
|
||||
|
||||
private final JobManager jobManager;
|
||||
|
||||
private final GitService gitService;
|
||||
|
||||
private final LabelSpecManager labelSpecManager;
|
||||
@ -165,7 +178,8 @@ public class McpHelperResource {
|
||||
LabelSpecManager labelSpecManager, PullRequestReviewManager pullRequestReviewManager,
|
||||
PullRequestAssignmentManager pullRequestAssignmentManager,
|
||||
PullRequestLabelManager pullRequestLabelManager, UrlManager urlManager,
|
||||
PullRequestCommentManager pullRequestCommentManager) {
|
||||
PullRequestCommentManager pullRequestCommentManager, BuildManager buildManager,
|
||||
BuildParamManager buildParamManager, JobManager jobManager) {
|
||||
this.objectMapper = objectMapper;
|
||||
this.settingManager = settingManager;
|
||||
this.issueManager = issueManager;
|
||||
@ -187,6 +201,9 @@ public class McpHelperResource {
|
||||
this.pullRequestLabelManager = pullRequestLabelManager;
|
||||
this.urlManager = urlManager;
|
||||
this.pullRequestCommentManager = pullRequestCommentManager;
|
||||
this.buildManager = buildManager;
|
||||
this.buildParamManager = buildParamManager;
|
||||
this.jobManager = jobManager;
|
||||
}
|
||||
|
||||
private String getIssueQueryStringDescription() {
|
||||
@ -369,6 +386,57 @@ public class McpHelperResource {
|
||||
return HtmlEscape.escapeHtml5(description);
|
||||
}
|
||||
|
||||
private String getBuildQueryStringDescription() {
|
||||
var orderFields = new StringBuilder();
|
||||
for (var field : Build.SORT_FIELDS.keySet()) {
|
||||
orderFields.append("- ").append(field).append("\n");
|
||||
}
|
||||
|
||||
var jobNames = buildManager.getJobNames(null).stream().collect(Collectors.joining(", "));
|
||||
var paramNames = buildParamManager.getParamNames(null).stream().collect(Collectors.joining(", "));
|
||||
var labelNames = labelSpecManager.query().stream().map(LabelSpec::getName).collect(Collectors.joining(", "));
|
||||
|
||||
var description =
|
||||
"A query string is one of below criteria:\n" +
|
||||
"- build with specified number in form of: \"Number\" is \"#<build number>\", or in form of: \"Number\" is \"<project key>-<build number>\" (quotes are required)\n" +
|
||||
"- criteria to check if version/job contains specified text in form of: ~<containing text>~\n" +
|
||||
"- sucessful criteria in form of: sucessful\n" +
|
||||
"- failed criteria in form of: failed\n" +
|
||||
"- cancelled criteria in form of: cancelled\n" +
|
||||
"- timed out criteria in form of: timed out\n" +
|
||||
"- finished criteria in form of: finished\n" +
|
||||
"- running criteria in form of: running\n" +
|
||||
"- waiting criteria in form of: waiting\n" +
|
||||
"- pending criteria in form of: pending\n" +
|
||||
"- submitted by specified user criteria in form of: submitted by \"<login name of a user>\" (quotes are required)\n" +
|
||||
"- submitted by current user criteria in form of: submitted by me (quotes are required)\n" +
|
||||
"- cancelled by specified user criteria in form of: cancelled by \"<login name of a user>\" (quotes are required)\n" +
|
||||
"- cancelled by current user criteria in form of: cancelled by me (quotes are required)\n" +
|
||||
"- depends on specified build criteria in form of: depends on \"<build reference>\" (quotes are required)\n" +
|
||||
"- dependencies of specified build criteria in form of: dependencies of \"<build reference>\" (quotes are required)\n" +
|
||||
"- fixed specified issue criteria in form of: fixed issue \"<issue reference>\" (quotes are required)\n" +
|
||||
"- job criteria in form of: \"Job\" is \"<job name>\" (quotes are required), where <job name> is one of: " + jobNames + "\n" +
|
||||
"- version criteria in form of: \"Version\" is \"<version>\" (quotes are required)\n" +
|
||||
"- branch criteria in form of: \"Branch\" is \"<branch name>\" (quotes are required)\n" +
|
||||
"- tag criteria in form of: \"Tag\" is \"<tag name>\" (quotes are required)\n" +
|
||||
"- param criteria in form of: \"<param name>\" is \"<param value>\" (quotes are required), where <param name> is one of: " + paramNames + "\n" +
|
||||
"- label criteria in form of: \"Label\" is \"<label name>\" (quotes are required), where <label name> is one of: " + labelNames + "\n" +
|
||||
"- pull request criteria in form of: \"Pull Request\" is \"<pull request reference>\" (quotes are required)\n" +
|
||||
"- commit criteria in form of: \"Commit\" is \"<commit hash>\" (quotes are required)\n" +
|
||||
"- before certain date criteria in form of: \"Submit Date\" is until \"<date>\" (quotes are required), where <date> is of format YYYY-MM-DD HH:mm\n" +
|
||||
"- after certain date criteria in form of: \"Submit Date\" is since \"<date>\" (quotes are required), where <date> is of format YYYY-MM-DD HH:mm\n" +
|
||||
"- and criteria in form of <criteria1> and <criteria2>\n" +
|
||||
"- or criteria in form of <criteria1> or <criteria2>. Note that \"and criteria\" takes precedence over \"or criteria\", use braces to group \"or criteria\" like \"(criteria1 or criteria2) and criteria3\" if you want to override precedence\n" +
|
||||
"- not criteria in form of not(<criteria>)\n" +
|
||||
"\n" +
|
||||
"And can optionally add order clause at end of query string in form of: order by \"<field1>\" <asc|desc>,\"<field2>\" <asc|desc>,... (quotes are required), where <field> is one of below:\n" +
|
||||
orderFields +
|
||||
"\n" +
|
||||
"Leave empty to list all pull requests";
|
||||
|
||||
return HtmlEscape.escapeHtml5(description);
|
||||
}
|
||||
|
||||
private String getToolParamName(String fieldName) {
|
||||
return fieldName.replace(" ", "_");
|
||||
}
|
||||
@ -649,6 +717,28 @@ public class McpHelperResource {
|
||||
|
||||
inputSchemas.put("queryPullRequests", queryPullRequestsInputSchema);
|
||||
|
||||
var queryBuildsInputSchema = new HashMap<String, Object>();
|
||||
var queryBuildsProperties = new HashMap<String, Object>();
|
||||
|
||||
queryBuildsProperties.put("project", Map.of(
|
||||
"type", "string",
|
||||
"description", "Project to query builds in. Leave empty to query in current project"));
|
||||
queryBuildsProperties.put("query", Map.of(
|
||||
"type", "string",
|
||||
"description", getBuildQueryStringDescription()));
|
||||
queryBuildsProperties.put("offset", Map.of(
|
||||
"type", "integer",
|
||||
"description", "start position for the query (optional, defaults to 0)"));
|
||||
queryBuildsProperties.put("count", Map.of(
|
||||
"type", "integer",
|
||||
"description", "number of builds to return (optional, defaults to 25, max 100)"));
|
||||
|
||||
queryBuildsInputSchema.put("Type", "object");
|
||||
queryBuildsInputSchema.put("Properties", queryBuildsProperties);
|
||||
queryBuildsInputSchema.put("Required", new ArrayList<>());
|
||||
|
||||
inputSchemas.put("queryBuilds", queryBuildsInputSchema);
|
||||
|
||||
var createPullRequestInputSchema = new HashMap<String, Object>();
|
||||
var createPullRequestProperties = new HashMap<String, Object>();
|
||||
createPullRequestProperties.put("targetProject", Map.of(
|
||||
@ -869,7 +959,6 @@ public class McpHelperResource {
|
||||
issueMap.put("reference", issue.getReference().toString(currentProject));
|
||||
issueMap.remove("submitterId");
|
||||
issueMap.put("submitter", issue.getSubmitter().getName());
|
||||
issueMap.remove("projectId");
|
||||
issueMap.put("Project", issue.getProject().getPath());
|
||||
issueMap.remove("lastActivity");
|
||||
for (var it = issueMap.entrySet().iterator(); it.hasNext();) {
|
||||
@ -1251,9 +1340,7 @@ public class McpHelperResource {
|
||||
pullRequestMap.put("reference", pullRequest.getReference().toString(currentProject));
|
||||
pullRequestMap.remove("submitterId");
|
||||
pullRequestMap.put("submitter", pullRequest.getSubmitter().getName());
|
||||
pullRequestMap.remove("targetProjectId");
|
||||
pullRequestMap.put("targetProject", pullRequest.getTarget().getProject().getPath());
|
||||
pullRequestMap.remove("sourceProjectId");
|
||||
if (pullRequest.getSourceProject() != null)
|
||||
pullRequestMap.put("sourceProject", pullRequest.getSourceProject().getPath());
|
||||
pullRequestMap.remove("codeCommentsUpdateDate");
|
||||
@ -1301,6 +1388,82 @@ public class McpHelperResource {
|
||||
return pullRequests;
|
||||
}
|
||||
|
||||
@Path("/query-builds")
|
||||
@GET
|
||||
public List<Map<String, Object>> queryBuilds(
|
||||
@QueryParam("currentProject") @NotNull String currentProjectPath,
|
||||
@QueryParam("project") String projectPath,
|
||||
@QueryParam("query") String query,
|
||||
@QueryParam("offset") int offset,
|
||||
@QueryParam("count") int count) {
|
||||
if (SecurityUtils.getAuthUser() == null)
|
||||
throw new UnauthenticatedException();
|
||||
|
||||
var projectInfo = getProjectInfo(projectPath, currentProjectPath);
|
||||
|
||||
if (count > RestConstants.MAX_PAGE_SIZE)
|
||||
throw new NotAcceptableException("Count should not be greater than " + RestConstants.MAX_PAGE_SIZE);
|
||||
|
||||
EntityQuery<Build> parsedQuery;
|
||||
if (query != null) {
|
||||
parsedQuery = BuildQuery.parse(projectInfo.project, query, true, true);
|
||||
} else {
|
||||
parsedQuery = new BuildQuery();
|
||||
}
|
||||
|
||||
var builds = new ArrayList<Map<String, Object>>();
|
||||
for (var build : buildManager.query(projectInfo.project, parsedQuery, false, offset, count)) {
|
||||
var buildMap = getBuildMap(projectInfo.currentProject, build);
|
||||
buildMap.put("link", urlManager.urlFor(build, true));
|
||||
builds.add(buildMap);
|
||||
}
|
||||
return builds;
|
||||
}
|
||||
|
||||
@Path("/get-build")
|
||||
@GET
|
||||
public Map<String, Object> getBuild(
|
||||
@QueryParam("currentProject") @NotNull String currentProjectPath,
|
||||
@QueryParam("reference") @NotNull String buildReference) {
|
||||
if (SecurityUtils.getAuthUser() == null)
|
||||
throw new UnauthenticatedException();
|
||||
|
||||
var currentProject = getProject(currentProjectPath);
|
||||
|
||||
var build = getBuild(currentProject, buildReference);
|
||||
|
||||
var buildMap = getBuildMap(currentProject, build);
|
||||
buildMap.put("params", build.getParamMap());
|
||||
buildMap.put("labels", build.getLabels().stream().map(it->it.getSpec().getName()).collect(Collectors.toList()));
|
||||
buildMap.put("link", urlManager.urlFor(build, true));
|
||||
|
||||
return buildMap;
|
||||
}
|
||||
|
||||
@Path("/get-previous-successful-build")
|
||||
@GET
|
||||
public Map<String, Object> getPreviousSuccessfulBuild(
|
||||
@QueryParam("currentProject") @NotNull String currentProjectPath,
|
||||
@QueryParam("reference") @NotNull String buildReference) {
|
||||
if (SecurityUtils.getAuthUser() == null)
|
||||
throw new UnauthenticatedException();
|
||||
|
||||
var currentProject = getProject(currentProjectPath);
|
||||
|
||||
var build = getBuild(currentProject, buildReference);
|
||||
|
||||
var previousSuccessfulBuild = buildManager.findStreamPrevious(build, Build.Status.SUCCESSFUL);
|
||||
if (previousSuccessfulBuild != null) {
|
||||
var buildMap = getBuildMap(currentProject, previousSuccessfulBuild);
|
||||
buildMap.put("params", previousSuccessfulBuild.getParamMap());
|
||||
buildMap.put("labels", previousSuccessfulBuild.getLabels().stream().map(it->it.getSpec().getName()).collect(Collectors.toList()));
|
||||
buildMap.put("link", urlManager.urlFor(previousSuccessfulBuild, true));
|
||||
return buildMap;
|
||||
} else {
|
||||
throw new NotFoundException("Previous successful build not found");
|
||||
}
|
||||
}
|
||||
|
||||
@Path("/get-pull-request")
|
||||
@GET
|
||||
public Map<String, Object> getPullRequest(
|
||||
@ -1836,13 +1999,133 @@ public class McpHelperResource {
|
||||
|
||||
return "Commented on pull request " + pullRequestReference;
|
||||
}
|
||||
|
||||
private Map<String, Object> getBuildMap(Project currentProject, Build build) {
|
||||
var typeReference = new TypeReference<LinkedHashMap<String, Object>>() {};
|
||||
var buildMap = objectMapper.convertValue(build, typeReference);
|
||||
buildMap.remove("id");
|
||||
buildMap.remove("uuid");
|
||||
buildMap.remove("numberScopeId");
|
||||
buildMap.remove("workspacePath");
|
||||
buildMap.remove("checkoutPaths");
|
||||
buildMap.remove("submitSequence");
|
||||
buildMap.remove("finishTimeGroups");
|
||||
buildMap.put("reference", build.getReference().toString(currentProject));
|
||||
buildMap.remove("submitterId");
|
||||
buildMap.put("submitter", build.getSubmitter().getName());
|
||||
buildMap.remove("cancellerId");
|
||||
if (build.getCanceller() != null)
|
||||
buildMap.put("canceller", build.getCanceller().getName());
|
||||
buildMap.remove("requestId");
|
||||
if (build.getRequest() != null)
|
||||
buildMap.put("pullRequest", build.getRequest().getReference().toString(currentProject));
|
||||
buildMap.remove("issueId");
|
||||
if (build.getIssue() != null)
|
||||
buildMap.put("issue", build.getIssue().getReference().toString(currentProject));
|
||||
buildMap.remove("agentId");
|
||||
if (build.getAgent() != null)
|
||||
buildMap.put("agent", build.getAgent().getName());
|
||||
|
||||
buildMap.put("project", build.getProject().getPath());
|
||||
return buildMap;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Path("/run-job")
|
||||
@POST
|
||||
public Map<String, Object> runJob(
|
||||
@QueryParam("currentProject") @NotNull String currentProjectPath,
|
||||
@QueryParam("project") String projectPath,
|
||||
@NotNull @Valid Map<String, Serializable> data) {
|
||||
if (SecurityUtils.getAuthUser() == null)
|
||||
throw new UnauthenticatedException();
|
||||
|
||||
var projectInfo = getProjectInfo(projectPath, currentProjectPath);
|
||||
|
||||
var jobName = StringUtils.trimToNull((String)data.get("jobName"));
|
||||
if (jobName == null)
|
||||
throw new NotAcceptableException("Job name is required");
|
||||
|
||||
var project = projectInfo.project;
|
||||
if (!SecurityUtils.canRunJob(project, jobName))
|
||||
throw new UnauthorizedException();
|
||||
|
||||
String refName;
|
||||
var branch = StringUtils.trimToNull((String)data.get("branch"));
|
||||
var tag = StringUtils.trimToNull((String)data.get("tag"));
|
||||
var commitHash = StringUtils.trimToNull((String)data.get("commitHash"));
|
||||
if (commitHash != null) {
|
||||
refName = StringUtils.trimToNull((String)data.get("refName"));
|
||||
if (refName == null) {
|
||||
throw new NotAcceptableException("Ref name is required when commit hash is specified");
|
||||
}
|
||||
} else if (branch != null) {
|
||||
refName = GitUtils.branch2ref(branch);
|
||||
} else if (tag != null) {
|
||||
refName = GitUtils.tag2ref(tag);
|
||||
} else {
|
||||
throw new NotAcceptableException("Either commit hash, branch or tag should be specified");
|
||||
}
|
||||
if (commitHash == null)
|
||||
commitHash = project.getRevCommit(refName, true).name();
|
||||
|
||||
Map<String, List<String>> params;
|
||||
var paramData = data.get("params");
|
||||
if (paramData instanceof List) {
|
||||
params = new HashMap<String, List<String>>();
|
||||
List<String> paramPairs = (List<String>) paramData;
|
||||
if (paramPairs != null) {
|
||||
for (var paramPair: paramPairs) {
|
||||
var paramName = StringUtils.trimToNull(StringUtils.substringBefore(paramPair, "="));
|
||||
var paramValue = StringUtils.trimToNull(StringUtils.substringAfter(paramPair, "="));
|
||||
if (paramName != null && paramValue != null)
|
||||
params.computeIfAbsent(paramName, k -> new ArrayList<>()).add(paramValue);
|
||||
}
|
||||
}
|
||||
} else if (paramData instanceof Map) {
|
||||
params = (Map<String, List<String>>) paramData;
|
||||
} else {
|
||||
params = new HashMap<String, List<String>>();
|
||||
}
|
||||
|
||||
var reason = StringUtils.trimToNull((String)data.get("reason"));
|
||||
if (reason == null)
|
||||
throw new NotAcceptableException("Reason is required");
|
||||
|
||||
Build build = jobManager.submit(project, ObjectId.fromString(commitHash), jobName,
|
||||
params, refName, SecurityUtils.getUser(), null,
|
||||
null, reason);
|
||||
if (build.isFinished())
|
||||
jobManager.resubmit(build, reason);
|
||||
|
||||
var buildMap = getBuildMap(projectInfo.currentProject, build);
|
||||
buildMap.put("id", build.getId());
|
||||
return buildMap;
|
||||
}
|
||||
|
||||
private Build getBuild(Project currentProject, String referenceString) {
|
||||
BuildReference buildReference;
|
||||
try {
|
||||
buildReference = BuildReference.of(referenceString, currentProject);
|
||||
} catch (InvalidReferenceException e) {
|
||||
throw new NotAcceptableException(e.getMessage());
|
||||
}
|
||||
var build = buildManager.find(buildReference.getProject(), buildReference.getNumber());
|
||||
if (build != null) {
|
||||
if (!SecurityUtils.canAccessBuild(build))
|
||||
throw new UnauthorizedException("No permission to access build: " + referenceString);
|
||||
return build;
|
||||
} else {
|
||||
throw new NotFoundException("Build not found: " + referenceString);
|
||||
}
|
||||
}
|
||||
|
||||
private void normalizePullRequestData(Map<String, Serializable> data) {
|
||||
for (var entry : data.entrySet()) {
|
||||
if (entry.getValue() instanceof String)
|
||||
entry.setValue(StringUtils.trimToNull((String) entry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private NotAcceptableException newNotAcceptableException(InvalidIssueFieldsException e) {
|
||||
var invaliParams = new ArrayList<String>();
|
||||
|
||||
@ -4,7 +4,6 @@ import java.util.Collection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import io.onedev.server.model.Build;
|
||||
import io.onedev.server.model.BuildParam;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.dao.EntityManager;
|
||||
@ -13,8 +12,6 @@ public interface BuildParamManager extends EntityManager<BuildParam> {
|
||||
|
||||
void create(BuildParam param);
|
||||
|
||||
void deleteParams(Build build);
|
||||
|
||||
Collection<String> getParamNames(@Nullable Project project);
|
||||
|
||||
}
|
||||
|
||||
@ -45,8 +45,6 @@ import javax.inject.Singleton;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.From;
|
||||
import javax.persistence.criteria.Join;
|
||||
import javax.persistence.criteria.JoinType;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.criteria.Selection;
|
||||
@ -322,7 +320,9 @@ public class DefaultBuildManager extends BaseEntityManager<Build> implements Bui
|
||||
predicates.add(builder.isNull(root.get(Build.PROP_ISSUE)));
|
||||
}
|
||||
|
||||
predicates.addAll(getPredicates(root, builder, params));
|
||||
var paramPredicate = getPredicate(root, builder, params);
|
||||
if (paramPredicate != null)
|
||||
predicates.add(paramPredicate);
|
||||
|
||||
query.where(predicates.toArray(new Predicate[0]));
|
||||
return getSession().createQuery(query).list();
|
||||
@ -377,31 +377,53 @@ public class DefaultBuildManager extends BaseEntityManager<Build> implements Bui
|
||||
predicates.add(builder.isNull(root.get(Build.PROP_ISSUE)));
|
||||
}
|
||||
|
||||
predicates.addAll(getPredicates(root, builder, params));
|
||||
|
||||
var paramPredicate = getPredicate(root, builder, params);
|
||||
if (paramPredicate != null)
|
||||
predicates.add(paramPredicate);
|
||||
|
||||
query.where(predicates.toArray(new Predicate[0]));
|
||||
return getSession().createQuery(query).list();
|
||||
}
|
||||
|
||||
private List<Predicate> getPredicates(From<Build, Build> root, CriteriaBuilder builder,
|
||||
Map<String, List<String>> params) {
|
||||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, List<String>> entry: params.entrySet()) {
|
||||
if (!entry.getValue().isEmpty()) {
|
||||
for (String value: entry.getValue()) {
|
||||
Join<?, ?> join = root.join(Build.PROP_PARAMS, JoinType.INNER);
|
||||
predicates.add(builder.equal(join.get(BuildParam.PROP_NAME), entry.getKey()));
|
||||
predicates.add(builder.equal(join.get(BuildParam.PROP_VALUE), value));
|
||||
@Nullable
|
||||
private Predicate getPredicate(From<Build, Build> root, CriteriaBuilder builder, Map<String, List<String>> params) {
|
||||
if (!params.isEmpty()) {
|
||||
// Create a single exists subquery to check if build has all required params with same name and values
|
||||
var subquery = builder.createQuery().subquery(Long.class);
|
||||
var paramRoot = subquery.from(BuildParam.class);
|
||||
subquery.select(builder.count(paramRoot));
|
||||
|
||||
List<Predicate> paramPredicates = new ArrayList<>();
|
||||
int expectedParamCount = 0;
|
||||
|
||||
for (Map.Entry<String, List<String>> entry: params.entrySet()) {
|
||||
if (!entry.getValue().isEmpty()) {
|
||||
for (String value: entry.getValue()) {
|
||||
paramPredicates.add(builder.and(
|
||||
builder.equal(paramRoot.get(BuildParam.PROP_NAME), entry.getKey()),
|
||||
builder.equal(paramRoot.get(BuildParam.PROP_VALUE), value)
|
||||
));
|
||||
expectedParamCount++;
|
||||
}
|
||||
} else {
|
||||
paramPredicates.add(builder.and(
|
||||
builder.equal(paramRoot.get(BuildParam.PROP_NAME), entry.getKey()),
|
||||
builder.isNull(paramRoot.get(BuildParam.PROP_VALUE))
|
||||
));
|
||||
expectedParamCount++;
|
||||
}
|
||||
} else {
|
||||
Join<?, ?> join = root.join(Build.PROP_PARAMS, JoinType.INNER);
|
||||
predicates.add(builder.equal(join.get(BuildParam.PROP_NAME), entry.getKey()));
|
||||
predicates.add(builder.isNull(join.get(BuildParam.PROP_VALUE)));
|
||||
}
|
||||
|
||||
subquery.where(
|
||||
builder.equal(paramRoot.get(BuildParam.PROP_BUILD), root),
|
||||
builder.or(paramPredicates.toArray(new Predicate[0]))
|
||||
);
|
||||
|
||||
// Check that the count of matching params equals the expected count
|
||||
return builder.equal(subquery, (long) expectedParamCount);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return predicates;
|
||||
}
|
||||
|
||||
@Sessional
|
||||
|
||||
@ -1,13 +1,27 @@
|
||||
package io.onedev.server.entitymanager.impl;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.hibernate.query.Query;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import io.onedev.server.cluster.ClusterManager;
|
||||
import io.onedev.server.entitymanager.BuildParamManager;
|
||||
import io.onedev.server.event.Listen;
|
||||
import io.onedev.server.event.entity.EntityPersisted;
|
||||
import io.onedev.server.event.entity.EntityRemoved;
|
||||
import io.onedev.server.event.system.SystemStarting;
|
||||
import io.onedev.server.model.Build;
|
||||
import io.onedev.server.model.BuildParam;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.persistence.TransactionManager;
|
||||
@ -15,14 +29,6 @@ import io.onedev.server.persistence.annotation.Sessional;
|
||||
import io.onedev.server.persistence.annotation.Transactional;
|
||||
import io.onedev.server.persistence.dao.BaseEntityManager;
|
||||
import io.onedev.server.persistence.dao.Dao;
|
||||
import org.hibernate.query.Query;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.*;
|
||||
|
||||
@Singleton
|
||||
public class DefaultBuildParamManager extends BaseEntityManager<BuildParam> implements BuildParamManager {
|
||||
@ -42,15 +48,6 @@ public class DefaultBuildParamManager extends BaseEntityManager<BuildParam> impl
|
||||
this.clusterManager = clusterManager;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
@Override
|
||||
public void deleteParams(Build build) {
|
||||
Query<?> query = getSession().createQuery("delete from BuildParam where build = :build");
|
||||
query.setParameter("build", build);
|
||||
query.executeUpdate();
|
||||
build.getParams().clear();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Sessional
|
||||
@Listen
|
||||
|
||||
@ -13,6 +13,11 @@ public class InvalidIssueFieldsException extends ExplicitException {
|
||||
this(buildMessage(invalidFields), invalidFields);
|
||||
}
|
||||
|
||||
public InvalidIssueFieldsException(String message, Map<String, String> invalidFields) {
|
||||
super(message);
|
||||
this.invalidFields = invalidFields;
|
||||
}
|
||||
|
||||
public static String buildMessage(Map<String, String> invalidFields) {
|
||||
var fieldErrors = new ArrayList<String>();
|
||||
for (var entry: invalidFields.entrySet()) {
|
||||
@ -21,11 +26,6 @@ public class InvalidIssueFieldsException extends ExplicitException {
|
||||
return "Invalid fields: " + String.join(", ", fieldErrors);
|
||||
}
|
||||
|
||||
public InvalidIssueFieldsException(String message, Map<String, String> invalidFields) {
|
||||
super(message);
|
||||
this.invalidFields = invalidFields;
|
||||
}
|
||||
|
||||
public Map<String, String> getInvalidFields() {
|
||||
return invalidFields;
|
||||
}
|
||||
|
||||
@ -106,7 +106,6 @@ import io.onedev.server.cluster.ClusterManager;
|
||||
import io.onedev.server.cluster.ClusterTask;
|
||||
import io.onedev.server.entitymanager.AccessTokenManager;
|
||||
import io.onedev.server.entitymanager.BuildManager;
|
||||
import io.onedev.server.entitymanager.BuildParamManager;
|
||||
import io.onedev.server.entitymanager.IssueManager;
|
||||
import io.onedev.server.entitymanager.ProjectManager;
|
||||
import io.onedev.server.entitymanager.PullRequestManager;
|
||||
@ -219,8 +218,6 @@ public class DefaultJobManager implements JobManager, Runnable, CodePullAuthoriz
|
||||
|
||||
private final ExecutorService executorService;
|
||||
|
||||
private final BuildParamManager buildParamManager;
|
||||
|
||||
private final TaskScheduler taskScheduler;
|
||||
|
||||
private final Validator validator;
|
||||
@ -253,10 +250,10 @@ public class DefaultJobManager implements JobManager, Runnable, CodePullAuthoriz
|
||||
public DefaultJobManager(BuildManager buildManager, AccessTokenManager accessTokenManager, UserManager userManager,
|
||||
ListenerRegistry listenerRegistry, SettingManager settingManager, TransactionManager transactionManager,
|
||||
LogManager logManager, ExecutorService executorService, SessionManager sessionManager,
|
||||
BuildParamManager buildParamManager, ProjectManager projectManager, Validator validator,
|
||||
TaskScheduler taskScheduler, ClusterManager clusterManager,
|
||||
PullRequestManager pullRequestManager, IssueManager issueManager, GitService gitService,
|
||||
SSLFactory sslFactory, Dao dao, BatchWorkManager batchWorkManager, WorkExecutor workExecutor) {
|
||||
ProjectManager projectManager, Validator validator, TaskScheduler taskScheduler,
|
||||
ClusterManager clusterManager, PullRequestManager pullRequestManager, IssueManager issueManager,
|
||||
GitService gitService, SSLFactory sslFactory, Dao dao, BatchWorkManager batchWorkManager,
|
||||
WorkExecutor workExecutor) {
|
||||
this.dao = dao;
|
||||
this.settingManager = settingManager;
|
||||
this.buildManager = buildManager;
|
||||
@ -267,7 +264,6 @@ public class DefaultJobManager implements JobManager, Runnable, CodePullAuthoriz
|
||||
this.logManager = logManager;
|
||||
this.executorService = executorService;
|
||||
this.sessionManager = sessionManager;
|
||||
this.buildParamManager = buildParamManager;
|
||||
this.projectManager = projectManager;
|
||||
this.validator = validator;
|
||||
this.taskScheduler = taskScheduler;
|
||||
@ -886,31 +882,6 @@ public class DefaultJobManager implements JobManager, Runnable, CodePullAuthoriz
|
||||
build.setAgent(null);
|
||||
build.getCheckoutPaths().clear();
|
||||
|
||||
buildParamManager.deleteParams(build);
|
||||
for (Map.Entry<String, List<String>> entry : build.getParamMap().entrySet()) {
|
||||
ParamSpec paramSpec = build.getJob().getParamSpecMap().get(entry.getKey());
|
||||
checkNotNull(paramSpec);
|
||||
String type = paramSpec.getType();
|
||||
List<String> values = entry.getValue();
|
||||
if (!values.isEmpty()) {
|
||||
for (String value : values) {
|
||||
BuildParam param = new BuildParam();
|
||||
param.setBuild(build);
|
||||
param.setName(entry.getKey());
|
||||
param.setType(type);
|
||||
param.setValue(value);
|
||||
build.getParams().add(param);
|
||||
buildParamManager.create(param);
|
||||
}
|
||||
} else {
|
||||
BuildParam param = new BuildParam();
|
||||
param.setBuild(build);
|
||||
param.setName(paramSpec.getName());
|
||||
param.setType(type);
|
||||
build.getParams().add(param);
|
||||
buildParamManager.create(param);
|
||||
}
|
||||
}
|
||||
buildManager.update(build);
|
||||
buildSubmitted(build);
|
||||
} catch (ValidationException e) {
|
||||
|
||||
@ -820,7 +820,7 @@ public class Project extends AbstractEntity implements LabelSupport<ProjectLabel
|
||||
objectIdCache.put(revision, optional);
|
||||
}
|
||||
if (mustExist && !optional.isPresent())
|
||||
throw new ObjectNotFoundException("Unable to find object '" + revision + "'");
|
||||
throw new ObjectNotFoundException("Unable to find revision '" + revision + "'");
|
||||
return optional.orNull();
|
||||
}
|
||||
|
||||
|
||||
@ -106,7 +106,7 @@ public class JobRunResource {
|
||||
jobRun.getParams(), refName, SecurityUtils.getUser(), request,
|
||||
null, jobRun.getReason());
|
||||
if (build.isFinished())
|
||||
jobManager.resubmit(build, "Rebuild via restful api");
|
||||
jobManager.resubmit(build, jobRun.getReason());
|
||||
return build.getId();
|
||||
}
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.NotAcceptableException;
|
||||
import javax.ws.rs.NotFoundException;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
@ -108,6 +109,20 @@ public class ProjectResource {
|
||||
throw new UnauthorizedException();
|
||||
return ProjectData.from(project);
|
||||
}
|
||||
|
||||
@Api(order=125)
|
||||
@Path("/ids/{path:.*}")
|
||||
@GET
|
||||
public Long getProjectId(@PathParam("path") String path) {
|
||||
var project = projectManager.findByPath(path);
|
||||
if (project != null) {
|
||||
if (!SecurityUtils.canAccessProject(project))
|
||||
throw new NotFoundException("Project not found or inaccessible: " + path);
|
||||
return project.getId();
|
||||
} else {
|
||||
throw new NotFoundException("Project not found or inaccessible: " + path);
|
||||
}
|
||||
}
|
||||
|
||||
@Api(order=150)
|
||||
@Path("/{projectId}/clone-url")
|
||||
|
||||
@ -7,7 +7,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -109,8 +108,6 @@ public abstract class NewIssueEditor extends FormComponentPanel<Issue> implement
|
||||
Class<?> fieldBeanClass = FieldUtils.getFieldBeanClass();
|
||||
Serializable fieldBean = issue.getFieldBean(fieldBeanClass, true);
|
||||
|
||||
System.out.println(FieldUtils.getFieldValues(null, fieldBean, Set.of("Assignees")));
|
||||
|
||||
var fieldNames = getIssueSetting().getPromptFieldsUponIssueOpen(getProject());
|
||||
issue.setFieldValues(FieldUtils.getFieldValues(new ComponentContext(this), fieldBean,
|
||||
FieldUtils.getEditableFields(getProject(), fieldNames)));
|
||||
|
||||
@ -58,7 +58,7 @@ public class RawBlobResource extends AbstractResource {
|
||||
String projectPath = params.get(ProjectMapperUtils.PARAM_PROJECT).toString();
|
||||
Project project = getProjectManager().findByPath(projectPath);
|
||||
if (project == null)
|
||||
throw new EntityNotFoundException();
|
||||
throw new EntityNotFoundException("Project not found: " + projectPath);
|
||||
|
||||
List<String> revisionAndPathSegments = new ArrayList<>();
|
||||
for (int i = 0; i < params.getIndexedCount(); i++) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user