mirror of
https://github.com/theonedev/onedev.git
synced 2025-12-08 18:26:30 +00:00
feat: Use natural language for various queries (OD-2600)
This commit is contained in:
parent
706b2dbdba
commit
c6a74dc008
@ -1,6 +1,7 @@
|
||||
package io.onedev.server.ai;
|
||||
|
||||
import static io.onedev.server.ai.QueryDescriptions.getBuildQueryDescription;
|
||||
import static io.onedev.server.ai.QueryDescriptions.getPackQueryDescription;
|
||||
import static javax.ws.rs.core.Response.Status.NOT_ACCEPTABLE;
|
||||
import static org.unbescape.html.HtmlEscape.escapeHtml5;
|
||||
|
||||
@ -66,6 +67,7 @@ import io.onedev.server.model.IssueSchedule;
|
||||
import io.onedev.server.model.IssueWork;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.LabelSpec;
|
||||
import io.onedev.server.model.Pack;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.PullRequest;
|
||||
import io.onedev.server.model.PullRequestAssignment;
|
||||
@ -95,6 +97,7 @@ 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.pack.PackQuery;
|
||||
import io.onedev.server.search.entity.pullrequest.PullRequestQuery;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.service.BuildParamService;
|
||||
@ -107,6 +110,7 @@ import io.onedev.server.service.IssueWorkService;
|
||||
import io.onedev.server.service.IterationService;
|
||||
import io.onedev.server.service.LabelSpecService;
|
||||
import io.onedev.server.service.LinkSpecService;
|
||||
import io.onedev.server.service.PackService;
|
||||
import io.onedev.server.service.ProjectService;
|
||||
import io.onedev.server.service.PullRequestAssignmentService;
|
||||
import io.onedev.server.service.PullRequestChangeService;
|
||||
@ -166,6 +170,9 @@ public class McpHelperResource {
|
||||
|
||||
private final BuildService buildService;
|
||||
|
||||
@Inject
|
||||
private PackService packService;
|
||||
|
||||
private final JobService jobService;
|
||||
|
||||
private final GitService gitService;
|
||||
@ -623,6 +630,28 @@ public class McpHelperResource {
|
||||
|
||||
inputSchemas.put("editPullRequest", editPullRequestInputSchema);
|
||||
|
||||
var queryPacksInputSchema = new HashMap<String, Object>();
|
||||
var queryPacksProperties = new HashMap<String, Object>();
|
||||
|
||||
queryPacksProperties.put("project", Map.of(
|
||||
"type", "string",
|
||||
"description", "Project to query packages in. Leave empty to query in current project"));
|
||||
queryPacksProperties.put("query", Map.of(
|
||||
"type", "string",
|
||||
"description", escapeHtml5(getPackQueryDescription())));
|
||||
queryPacksProperties.put("offset", Map.of(
|
||||
"type", "integer",
|
||||
"description", "start position for the query (optional, defaults to 0)"));
|
||||
queryPacksProperties.put("count", Map.of(
|
||||
"type", "integer",
|
||||
"description", "number of packages to return (optional, defaults to 25, max 100)"));
|
||||
|
||||
queryPacksInputSchema.put("Type", "object");
|
||||
queryPacksInputSchema.put("Properties", queryPacksProperties);
|
||||
queryPacksInputSchema.put("Required", new ArrayList<>());
|
||||
|
||||
inputSchemas.put("queryPacks", queryPacksInputSchema);
|
||||
|
||||
return inputSchemas;
|
||||
}
|
||||
|
||||
@ -1946,6 +1975,53 @@ public class McpHelperResource {
|
||||
}
|
||||
}
|
||||
|
||||
@Path("/query-packs")
|
||||
@GET
|
||||
public List<Map<String, Object>> queryPacks(
|
||||
@QueryParam("currentProject") @NotNull String currentProjectPath,
|
||||
@QueryParam("project") String projectPath,
|
||||
@QueryParam("query") String query,
|
||||
@QueryParam("offset") int offset,
|
||||
@QueryParam("count") int count) {
|
||||
var subject = SecurityUtils.getSubject();
|
||||
if (SecurityUtils.getUser(subject) == 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<Pack> parsedQuery;
|
||||
if (query != null) {
|
||||
parsedQuery = PackQuery.parse(projectInfo.project, query, true);
|
||||
} else {
|
||||
parsedQuery = new PackQuery();
|
||||
}
|
||||
|
||||
var packs = new ArrayList<Map<String, Object>>();
|
||||
for (var pack : packService.query(subject, projectInfo.project, parsedQuery, false, offset, count)) {
|
||||
var packMap = getPackMap(projectInfo.currentProject, pack);
|
||||
packMap.put("link", urlService.urlFor(pack, true));
|
||||
packs.add(packMap);
|
||||
}
|
||||
return packs;
|
||||
}
|
||||
|
||||
private Map<String, Object> getPackMap(Project currentProject, Pack pack) {
|
||||
var typeReference = new TypeReference<LinkedHashMap<String, Object>>() {};
|
||||
var packMap = objectMapper.convertValue(pack, typeReference);
|
||||
packMap.remove("id");
|
||||
packMap.remove("userId");
|
||||
packMap.put("user", pack.getUser().getName());
|
||||
packMap.remove("buildId");
|
||||
if (pack.getBuild() != null)
|
||||
packMap.put("build", pack.getBuild().getReference().toString(currentProject));
|
||||
packMap.put("project", pack.getProject().getPath());
|
||||
|
||||
return packMap;
|
||||
}
|
||||
|
||||
private void normalizePullRequestData(Map<String, Serializable> data) {
|
||||
for (var entry : data.entrySet()) {
|
||||
if (entry.getValue() instanceof String)
|
||||
|
||||
@ -1,29 +1,63 @@
|
||||
package io.onedev.server.ai;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.model.Agent;
|
||||
import io.onedev.server.model.Build;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.LabelSpec;
|
||||
import io.onedev.server.model.Pack;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.PullRequest;
|
||||
import io.onedev.server.model.support.issue.field.spec.BooleanField;
|
||||
import io.onedev.server.model.support.issue.field.spec.BuildChoiceField;
|
||||
import io.onedev.server.model.support.issue.field.spec.CommitField;
|
||||
import io.onedev.server.model.support.issue.field.spec.DateField;
|
||||
import io.onedev.server.model.support.issue.field.spec.DateTimeField;
|
||||
import io.onedev.server.model.support.issue.field.spec.FloatField;
|
||||
import io.onedev.server.model.support.issue.field.spec.GroupChoiceField;
|
||||
import io.onedev.server.model.support.issue.field.spec.IntegerField;
|
||||
import io.onedev.server.model.support.issue.field.spec.IssueChoiceField;
|
||||
import io.onedev.server.model.support.issue.field.spec.IterationChoiceField;
|
||||
import io.onedev.server.model.support.issue.field.spec.PullRequestChoiceField;
|
||||
import io.onedev.server.model.support.issue.field.spec.TextField;
|
||||
import io.onedev.server.model.support.issue.field.spec.choicefield.ChoiceField;
|
||||
import io.onedev.server.model.support.issue.field.spec.userchoicefield.UserChoiceField;
|
||||
import io.onedev.server.model.support.pullrequest.MergeStrategy;
|
||||
import io.onedev.server.service.BuildParamService;
|
||||
import io.onedev.server.service.BuildService;
|
||||
import io.onedev.server.pack.PackSupport;
|
||||
import io.onedev.server.service.AgentAttributeService;
|
||||
import io.onedev.server.service.AgentService;
|
||||
import io.onedev.server.service.LabelSpecService;
|
||||
import io.onedev.server.service.LinkSpecService;
|
||||
import io.onedev.server.service.SettingService;
|
||||
|
||||
public class QueryDescriptions {
|
||||
|
||||
private static String REACTION_CRITERIAS = """
|
||||
| '"Reaction: Thumbs Up Count"' 'is' ('not')? '"'Number'"'
|
||||
| '"Reaction: Thumbs Up Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
|
||||
| '"Reaction: Thumbs Down Count"' 'is' ('not')? '"'Number'"'
|
||||
| '"Reaction: Thumbs Down Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
|
||||
| '"Reaction: Smile Count"' 'is' ('not')? '"'Number'"'
|
||||
| '"Reaction: Smile Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
|
||||
| '"Reaction: Tada Count"' 'is' ('not')? '"'Number'"'
|
||||
| '"Reaction: Tada Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
|
||||
| '"Reaction: Confused Count"' 'is' ('not')? '"'Number'"'
|
||||
| '"Reaction: Confused Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
|
||||
| '"Reaction: Heart Count"' 'is' ('not')? '"'Number'"'
|
||||
| '"Reaction: Heart Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
|
||||
| '"Reaction: Rocket Count"' 'is' ('not')? '"'Number'"'
|
||||
| '"Reaction: Rocket Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
|
||||
| '"Reaction: Eyes Count"' 'is' ('not')? '"'Number'"'
|
||||
| '"Reaction: Eyes Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
|
||||
""".trim();
|
||||
|
||||
private static SettingService getSettingService() {
|
||||
return OneDev.getInstance(SettingService.class);
|
||||
}
|
||||
@ -31,270 +65,575 @@ public class QueryDescriptions {
|
||||
private static LinkSpecService getLinkSpecService() {
|
||||
return OneDev.getInstance(LinkSpecService.class);
|
||||
}
|
||||
|
||||
private static LabelSpecService getLabelSpecService() {
|
||||
return OneDev.getInstance(LabelSpecService.class);
|
||||
}
|
||||
|
||||
private static BuildService getBuildService() {
|
||||
return OneDev.getInstance(BuildService.class);
|
||||
}
|
||||
|
||||
private static BuildParamService getBuildParamService() {
|
||||
return OneDev.getInstance(BuildParamService.class);
|
||||
}
|
||||
|
||||
public static String getIssueQueryDescription() {
|
||||
var settingService = getSettingService();
|
||||
var linkSpecService = getLinkSpecService();
|
||||
|
||||
var stateNames = new StringBuilder();
|
||||
for (var state: settingService.getIssueSetting().getStateSpecs()) {
|
||||
stateNames.append(" - ");
|
||||
stateNames.append(state.getName());
|
||||
if (state.getDescription() != null) {
|
||||
stateNames.append(": ").append(state.getDescription().replace("\n", " "));
|
||||
}
|
||||
stateNames.append("\n");
|
||||
}
|
||||
var fieldCriterias = new StringBuilder();
|
||||
var fieldCriterias = new ArrayList<String>();
|
||||
var choiceFieldValueRules = new ArrayList<String>();
|
||||
int choiceFieldValueRuleIndex = 0;
|
||||
for (var field: settingService.getIssueSetting().getFieldSpecs()) {
|
||||
if (field instanceof ChoiceField) {
|
||||
var choiceField = (ChoiceField) field;
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase() + " criteria in form of: \""
|
||||
+ field.getName() + "\" is \"<" + field.getName().toLowerCase()
|
||||
+ " value>\" (quotes are required), where <" + field.getName().toLowerCase()
|
||||
+ " value> is one of below:\n");
|
||||
for (var choice : choiceField.getPossibleValues())
|
||||
fieldCriterias.append(" - " + choice).append("\n");
|
||||
if (field instanceof ChoiceField) {
|
||||
var choiceField = (ChoiceField) field;
|
||||
var fieldValueRuleName = "ChoiceFieldValue" + (choiceFieldValueRuleIndex++);
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'" + fieldValueRuleName + "'\"'");
|
||||
choiceFieldValueRules.add(choiceField.getPossibleValues().stream().map(it->"'" + it.replace("'", "\\'") + "'").collect(joining("\n | ")));
|
||||
} else if (field instanceof UserChoiceField) {
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase() + " criteria in form of: \""
|
||||
+ field.getName() + "\" is \"<login name of a user>\" (quotes are required)\n");
|
||||
fieldCriterias.append(
|
||||
"- " + field.getName().toLowerCase() + " criteria for current user in form of: \""
|
||||
+ field.getName() + "\" is me (quotes are required)\n");
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'LoginNameOfUser'\"'");
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? 'me'");
|
||||
} else if (field instanceof GroupChoiceField) {
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase() + " criteria in form of: \""
|
||||
+ field.getName() + "\" is \"<group name>\" (quotes are required)\n");
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'GroupName'\"'");
|
||||
} else if (field instanceof BooleanField) {
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase() + " is true criteria in form of: \""
|
||||
+ field.getName() + "\" is \"true\" (quotes are required)\n");
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase() + " is false criteria in form of: \""
|
||||
+ field.getName() + "\" is \"false\" (quotes are required)\n");
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'('true'|'false')'\"'");
|
||||
} else if (field instanceof DateField) {
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase()
|
||||
+ " is before certain date criteria in form of: \"" + field.getName()
|
||||
+ "\" is before \"<date>\" (quotes are required), where <date> is of format YYYY-MM-DD\n");
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase()
|
||||
+ " is after certain date criteria in form of: \"" + field.getName()
|
||||
+ "\" is after \"<date>\" (quotes are required), where <date> is of format YYYY-MM-DD\n");
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('before'|'after') '\"'DateDescription'\"'");
|
||||
} else if (field instanceof DateTimeField) {
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase()
|
||||
+ " is before certain date time criteria in form of: \"" + field.getName()
|
||||
+ "\" is before \"<date time>\" (quotes are required), where <date time> is of format YYYY-MM-DD HH:mm\n");
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase()
|
||||
+ " is after certain date time criteria in form of: \"" + field.getName()
|
||||
+ "\" is after \"<date time>\" (quotes are required), where <date time> is of format YYYY-MM-DD HH:mm\n");
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('before'|'after') '\"'DateTimeDescription'\"'");
|
||||
} else if (field instanceof IntegerField) {
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase()
|
||||
+ " is equal to certain integer criteria in form of: \"" + field.getName()
|
||||
+ "\" is \"<integer>\" (quotes are required), where <integer> is an integer\n");
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase()
|
||||
+ " is greater than certain integer criteria in form of: \"" + field.getName()
|
||||
+ "\" is greater than \"<integer>\" (quotes are required), where <integer> is an integer\n");
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase()
|
||||
+ " is less than certain integer criteria in form of: \"" + field.getName()
|
||||
+ "\" is less than \"<integer>\" (quotes are required), where <integer> is an integer\n");
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'Integer'\"'");
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('greater'|'less') 'than' '\"'Integer'\"'");
|
||||
} else if (field instanceof FloatField) {
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('greater'|'less') 'than' '\"'Float'\"'");
|
||||
} else if (field instanceof TextField) {
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'Text'\"'");
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'contains' '\"'Text'\"'");
|
||||
} else if (field instanceof PullRequestChoiceField || field instanceof BuildChoiceField || field instanceof IssueChoiceField) {
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'EntityReference'\"'");
|
||||
} else if (field instanceof CommitField) {
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'CommitReference'\"'");
|
||||
} else if (field instanceof IterationChoiceField) {
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? '\"'IterationNameOrPattern'\"'");
|
||||
}
|
||||
fieldCriterias.append("- " + field.getName().toLowerCase() + " is not set criteria in form of: \""
|
||||
+ field.getName() + "\" is empty (quotes are required)\n");
|
||||
}
|
||||
var linkCriterias = new StringBuilder();
|
||||
for (var linkSpec: linkSpecService.query()) {
|
||||
linkCriterias.append("- criteria to list issues with any " + linkSpec.getName().toLowerCase()
|
||||
+ " matching certain criteria in form of: any \"" + linkSpec.getName()
|
||||
+ "\" matching(another criteria) (quotes are required)\n");
|
||||
linkCriterias.append("- criteria to list issues with all " + linkSpec.getName().toLowerCase()
|
||||
+ " matching certain criteria in form of: all \"" + linkSpec.getName()
|
||||
+ "\" matching(another criteria) (quotes are required)\n");
|
||||
linkCriterias.append("- criteria to list issues with some " + linkSpec.getName().toLowerCase()
|
||||
+ " in form of: has any \"" + linkSpec.getName() + "\" (quotes are required)\n");
|
||||
if (linkSpec.getOpposite() != null) {
|
||||
linkCriterias.append("- criteria to list issues with any "
|
||||
+ linkSpec.getOpposite().getName().toLowerCase()
|
||||
+ " matching certain criteria in form of: any \"" + linkSpec.getOpposite().getName()
|
||||
+ "\" matching(another criteria) (quotes are required)\n");
|
||||
linkCriterias.append("- criteria to list issues with all "
|
||||
+ linkSpec.getOpposite().getName().toLowerCase()
|
||||
+ " matching certain criteria in form of: all \"" + linkSpec.getOpposite().getName()
|
||||
+ "\" matching(another criteria) (quotes are required)\n");
|
||||
linkCriterias.append("- criteria to list issues with some " + linkSpec.getOpposite().getName().toLowerCase()
|
||||
+ " in form of: has any \"" + linkSpec.getOpposite().getName() + "\" (quotes are required)\n");
|
||||
}
|
||||
}
|
||||
var orderFields = new StringBuilder();
|
||||
for (var field: Issue.SORT_FIELDS.keySet()) {
|
||||
orderFields.append("- ").append(field).append("\n");
|
||||
fieldCriterias.add("'\"" + field.getName() + "\"' 'is' ('not')? 'empty'");
|
||||
}
|
||||
|
||||
var description =
|
||||
"A query string is one of below criteria:\n\n" +
|
||||
"- issue with specified number in form of: \"Number\" is \"#<issue number>\", or in form of: \"Number\" is \"<project key>-<issue number>\" (quotes are required)\n" +
|
||||
"- state criteria in form of: \"State\" is \"<state name>\" (quotes are required), where <state name> is one of below:\n" +
|
||||
stateNames +
|
||||
fieldCriterias +
|
||||
linkCriterias +
|
||||
"- 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" +
|
||||
"- submitted 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" +
|
||||
"- submitted 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" +
|
||||
"- updated before certain date criteria in form of: \"Last Activity Date\" is until \"<date>\" (quotes are required), where <date> is of format YYYY-MM-DD HH:mm\n" +
|
||||
"- updated after certain date criteria in form of: \"Last Activity Date\" is since \"<date>\" (quotes are required), where <date> is of format YYYY-MM-DD HH:mm\n" +
|
||||
"- confidential criteria in form of: confidential\n" +
|
||||
"- iteration criteria in form of: \"Iteration\" is \"<iteration name>\" (quotes are required)\n" +
|
||||
"- title contains specified text criteria in form of: \"Title\" contains \"<containing text>\" (quotes are required)\n" +
|
||||
"- description contains specified text criteria in form of: \"Description\" contains \"<containing text>\" (quotes are required)\n" +
|
||||
"- comment contains specified text criteria in form of: \"Comment\" contains \"<containing text>\" (quotes are required)\n" +
|
||||
"- project criteria in form of: \"Project\" is \"<project path pattern>\" (quotes are required)\n" +
|
||||
"- and criteria in form of <criteria1> and <criteria2>\n" +
|
||||
"- or criteria in form of <criteria1> or <criteria2>\n" +
|
||||
"- operator 'and' takes precedence over 'or' when used together, unless parentheses are used to group 'or' criterias\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\n" +
|
||||
orderFields + "\n" +
|
||||
"Issue, build or pull request can be referenced by their number in form of: #<number>, <project path>#<number> or <project key>-<number>\n" +
|
||||
"\n" +
|
||||
"Leave empty to list all accessible issues";
|
||||
var linkCriterias = new ArrayList<String>();
|
||||
for (var linkSpec: linkSpecService.query()) {
|
||||
linkCriterias.add("'any' '\"" + linkSpec.getName() + "\"' 'matching' '('criteria')'");
|
||||
linkCriterias.add("'all' '\"" + linkSpec.getName() + "\"' 'matching' '('criteria')'");
|
||||
linkCriterias.add("'has any' '\"" + linkSpec.getName() + "\"'");
|
||||
if (linkSpec.getOpposite() != null) {
|
||||
linkCriterias.add("'any' '\"" + linkSpec.getOpposite().getName() + "\"' 'matching' '('criteria')'");
|
||||
linkCriterias.add("'all' '\"" + linkSpec.getOpposite().getName() + "\"' 'matching' '('criteria')'");
|
||||
linkCriterias.add("'has any' '\"" + linkSpec.getOpposite().getName() + "\"'");
|
||||
}
|
||||
}
|
||||
|
||||
var description = String.format("""
|
||||
A structured query should conform to below ANTLR grammar:
|
||||
|
||||
issueQuery
|
||||
: criteria ('order by' '"'OrderField'"' ('asc'|'desc') (',' '"'OrderField'"' ('asc'|'desc'))*)?
|
||||
;
|
||||
|
||||
criteria
|
||||
: '"Number"' 'is' ('not')? '"'EntityReference'"'
|
||||
| '"Number"' 'is' ('greater'|'less') 'than' '"'EntityReference'"'
|
||||
| '"State"' 'is' ('not')? '"'StateName'"'
|
||||
| '"State"' 'is' ('after'|'before') '"'StateName'"'
|
||||
%s
|
||||
%s
|
||||
| 'submitted by' '"'LoginNameOfUser'"'
|
||||
| 'submitted by me'
|
||||
| 'watched by' '"'LoginNameOfUser'"'
|
||||
| 'watched by me'
|
||||
| 'ignored by' '"'LoginNameOfUser'"'
|
||||
| 'ignored by me'
|
||||
| 'commented by' '"'LoginNameOfUser'"'
|
||||
| 'commented by me'
|
||||
| 'mentioned' '"'LoginNameOfUser'"'
|
||||
| 'mentioned me'
|
||||
| 'fixed in commit' '"'CommitReference'"'
|
||||
| 'fixed in current commit'
|
||||
| 'fixed in build' '"'EntityReference'"'
|
||||
| 'fixed in current build'
|
||||
| 'fixed in pull request' '"'EntityReference'"'
|
||||
| 'fixed in current pull request'
|
||||
| 'fixed between' revision 'and' revision
|
||||
| '"Submit Date"' 'is' ('until'|'since') '"'DateDescription'"'
|
||||
| '"Last Activity Date"' 'is' ('until'|'since') '"'DateDescription'"'
|
||||
| 'confidential'
|
||||
| '"Spent Time"' 'is' ('greater'|'less') 'than' '"'TimePeriodDescription'"'
|
||||
| '"Spent Time"' 'is' ('not')? '"'TimePeriodDescription'"'
|
||||
| '"Estimated Time"' 'is' ('greater'|'less') 'than' '"'TimePeriodDescription'"'
|
||||
| '"Estimated Time"' 'is' ('not')? '"'TimePeriodDescription'"'
|
||||
| '"Progress"' 'is' ('greater'|'less') 'than' '"'Float'"'
|
||||
| '"Iteration"' 'is' ('not')? '"'IterationNameOrPattern'"'
|
||||
| '"Iteration"' 'is' ('not')? 'empty'
|
||||
| '"Title"' 'contains' '"'Text'"'
|
||||
| '"Description"' 'contains' '"'Text'"'
|
||||
| '"Comment"' 'contains' '"'Text'"'
|
||||
| '"Comment Count"' 'is' ('not')? '"'Number'"'
|
||||
| '"Comment Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
|
||||
| '"Vote Count"' 'is' ('not')? '"'Number'"'
|
||||
| '"Vote Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
|
||||
%s
|
||||
| '"Project"' 'is current'
|
||||
| '"Project"' 'is' ('not')? '"'ProjectPathOrPattern'"'
|
||||
| criteria 'and' criteria
|
||||
| criteria 'or' criteria
|
||||
| 'not('criteria')'
|
||||
| '('criteria')'
|
||||
;
|
||||
|
||||
StateName
|
||||
: %s
|
||||
;
|
||||
|
||||
%s
|
||||
|
||||
revision
|
||||
: 'commit' '"'CommitReference'"'
|
||||
| 'build' '"'EntityReference'"'
|
||||
| 'branch' '"'BranchReference'"'
|
||||
| 'tag' '"'TagReference'"'
|
||||
;
|
||||
|
||||
EntityReference
|
||||
: '#'Number
|
||||
| ProjectPath'#'Number
|
||||
| ProjectKey'-'Number
|
||||
;
|
||||
|
||||
CommitReference
|
||||
: (ProjectPath':')?CommitHash
|
||||
;
|
||||
|
||||
BranchReference
|
||||
: (ProjectPath':')?BranchName
|
||||
;
|
||||
|
||||
TagReference
|
||||
: (ProjectPath':')?TagName
|
||||
;
|
||||
|
||||
OrderField
|
||||
: %s
|
||||
;
|
||||
|
||||
WS
|
||||
: [ ]+ -> skip
|
||||
;
|
||||
|
||||
Please note:
|
||||
1. "LoginNameOfUser" should be retrieved via tool 'getLoginName' if available, with parameter set to user name
|
||||
2. Use an empty query to list all accessible issues""",
|
||||
fieldCriterias.stream().map(it->" | " + it + "\n").collect(joining("")).trim(),
|
||||
linkCriterias.stream().map(it->" | " + it + "\n").collect(joining("")).trim(),
|
||||
REACTION_CRITERIAS,
|
||||
settingService.getIssueSetting().getStateSpecs().stream().map(it->"'" + it.getName() + "'").collect(joining("\n | ")).trim(),
|
||||
IntStream.range(0, choiceFieldValueRules.size()).mapToObj(i -> "ChoiceFieldValue" + i + "\n : " + choiceFieldValueRules.get(i) + "\n ;\n\n").collect(joining("")).trim(),
|
||||
Issue.SORT_FIELDS.keySet().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim());
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
public static String getPullRequestQueryDescription() {
|
||||
var labelSpecService = getLabelSpecService();
|
||||
|
||||
var orderFields = new StringBuilder();
|
||||
for (var field : PullRequest.SORT_FIELDS.keySet()) {
|
||||
orderFields.append("- ").append(field).append("\n");
|
||||
}
|
||||
var description = String.format("""
|
||||
A structured query should conform to below ANTLR grammar:
|
||||
|
||||
var labelNames = labelSpecService.query().stream().map(LabelSpec::getName).collect(Collectors.joining(", "));
|
||||
var mergeStrategyNames = Arrays.stream(MergeStrategy.values()).map(MergeStrategy::name).collect(Collectors.joining(", "));
|
||||
pullRequestQuery
|
||||
: criteria ('order by' '"'OrderField'"' ('asc'|'desc') (',' '"'OrderField'"' ('asc'|'desc'))*)?
|
||||
;
|
||||
|
||||
var description =
|
||||
"A query string is one of below criteria:\n\n" +
|
||||
"- pull request with specified number in form of: \"Number\" is \"#<pull request number>\", or in form of: \"Number\" is \"<project key>-<pull request number>\" (quotes are required)\n" +
|
||||
"- open criteria in form of: open\n" +
|
||||
"- merged criteria in form of: merged\n" +
|
||||
"- discarded criteria in form of: discarded\n" +
|
||||
"- source branch criteria in form of: \"Source Branch\" is \"<branch name>\" (quotes are required)\n" +
|
||||
"- target branch criteria in form of: \"Target Branch\" is \"<branch name>\" (quotes are required)\n" +
|
||||
"- merge strategy criteria in form of: \"Merge Strategy\" is \"<merge strategy>\" (quotes are required), where <merge strategy> is one of: " + mergeStrategyNames + "\n" +
|
||||
"- label criteria in form of: \"Label\" is \"<label name>\" (quotes are required), where <label name> is one of: " + labelNames + "\n" +
|
||||
"- ready to merge criteria in form of: ready to merge\n" +
|
||||
"- waiting for someone to review criteria in form of: has pending reviews\n" +
|
||||
"- some builds are unsuccessful criteria in form of: has unsuccessful builds\n" +
|
||||
"- some builds are not finished criteria in form of: has unfinished builds\n" +
|
||||
"- has merge conflicts criteria in form of: has merge conflicts\n" +
|
||||
"- assigned to specified user criteria in form of: assigned to \"<login name of a user>\" (quotes are required)\n" +
|
||||
"- approved by specified user criteria in form of: approved by \"<login name of a user>\" (quotes are required)\n" +
|
||||
"- to be reviewed by specified user criteria in form of: to be reviewed by \"<login name of a user>\" (quotes are required)\n" +
|
||||
"- to be changed by specified user criteria in form of: to be changed by \"<login name of a user>\" (quotes are required)\n" +
|
||||
"- to be merged by specified user criteria in form of: to be merged by \"<login name of a user>\" (quotes are required)\n" +
|
||||
"- requested for changes by specified user in form of: requested for changes by \"<login name of a user>\" (quotes are required)\n" +
|
||||
"- need action of specified user criteria in form of: need action by \"<login name of a user>\" (quotes are required)\n" +
|
||||
"- assigned to current user criteria in form of: assigned to me\n" +
|
||||
"- approved by current user criteria in form of: approved by me\n" +
|
||||
"- to be reviewed by current user criteria in form of: to be reviewed by me\n" +
|
||||
"- to be changed by current user criteria in form of: to be changed by me\n" +
|
||||
"- to be merged by current user criteria in form of: to be merged by me\n" +
|
||||
"- requested for changes by current user in form of: requested for changes by me\n" +
|
||||
"- requested for changes by any user criteria in form of: someone requested for changes\n" +
|
||||
"- need action of current user criteria in form of: need my action\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" +
|
||||
"- submitted 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" +
|
||||
"- submitted 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" +
|
||||
"- updated before certain date criteria in form of: \"Last Activity Date\" is until \"<date>\" (quotes are required), where <date> is of format YYYY-MM-DD HH:mm\n" +
|
||||
"- updated after certain date criteria in form of: \"Last Activity Date\" is since \"<date>\" (quotes are required), where <date> is of format YYYY-MM-DD HH:mm\n" +
|
||||
"- closed (merged or discarded) before certain date criteria in form of: \"Close Date\" is until \"<date>\" (quotes are required), where <date> is of format YYYY-MM-DD HH:mm\n" +
|
||||
"- closed (merged or discarded) after certain date criteria in form of: \"Close Date\" is since \"<date>\" (quotes are required), where <date> is of format YYYY-MM-DD HH:mm\n" +
|
||||
"- includes specified issue criteria in form of: includes issue \"<issue reference>\" (quotes are required)\n" +
|
||||
"- includes specified commit criteria in form of: includes commit \"<commit hash>\" (quotes are required)\n" +
|
||||
"- title contains specified text criteria in form of: \"Title\" contains \"<containing text>\" (quotes are required)\n" +
|
||||
"- description contains specified text criteria in form of: \"Description\" contains \"<containing text>\" (quotes are required)\n" +
|
||||
"- comment contains specified text criteria in form of: \"Comment\" contains \"<containing text>\" (quotes are required)\n" +
|
||||
"- project criteria in form of: \"Project\" is \"<project path pattern>\" (quotes are required)\n" +
|
||||
"- and criteria in form of <criteria1> and <criteria2>\n" +
|
||||
"- or criteria in form of <criteria1> or <criteria2>\n" +
|
||||
"- operator 'and' takes precedence over 'or' when used together, unless parentheses are used to group 'or' criterias\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\n" +
|
||||
orderFields + "\n" +
|
||||
"Issue, build or pull request can be referenced by their number in form of: #<number>, <project path>#<number> or <project key>-<number>\n" +
|
||||
"\n" +
|
||||
"Leave empty to list all accessible pull requests";
|
||||
criteria
|
||||
: '"Number"' 'is' ('not')? '"'EntityReference'"'
|
||||
| '"Number"' 'is' ('greater'|'less') 'than' '"'EntityReference'"'
|
||||
| 'open'
|
||||
| 'merged'
|
||||
| 'discarded'
|
||||
| '"Source Branch"' 'is' ('not')? '"'BranchNameOrPattern'"'
|
||||
| '"Souce Project"' 'is' ('not')? '"'ProjectPathOrPattern'"'
|
||||
| '"Target Branch"' 'is' ('not')? '"'BranchNameOrPattern'"'
|
||||
| '"Merge Strategy"' 'is' ('not')? '"'MergeStrategy'"'
|
||||
| '"Label"' 'is' ('not')? '"'LabelName'"'
|
||||
| 'ready to merge'
|
||||
| 'has pending reviews'
|
||||
| 'has unsuccessful builds'
|
||||
| 'has unfinished builds'
|
||||
| 'has merge conflicts'
|
||||
| 'assigned to' '"'LoginNameOfUser'"'
|
||||
| 'approved by' '"'LoginNameOfUser'"'
|
||||
| 'to be reviewed by' '"'LoginNameOfUser'"'
|
||||
| 'to be changed by' '"'LoginNameOfUser'"'
|
||||
| 'to be merged by' '"'LoginNameOfUser'"'
|
||||
| 'requested for changes by' '"'LoginNameOfUser'"'
|
||||
| 'need action of' '"'LoginNameOfUser'"'
|
||||
| 'assigned to me'
|
||||
| 'approved by me'
|
||||
| 'to be reviewed by me'
|
||||
| 'to be changed by me'
|
||||
| 'to be merged by me'
|
||||
| 'requested for changes by me'
|
||||
| 'someone requested for changes'
|
||||
| 'mentioned' '"'LoginNameOfUser'"'
|
||||
| 'mentioned me'
|
||||
| 'need action of' '"'LoginNameOfUser'"'
|
||||
| 'need my action'
|
||||
| 'submitted by' '"'LoginNameOfUser'"'
|
||||
| 'submitted by me'
|
||||
| '"Submit Date"' 'is' ('until'|'since') '"'DateDescription'"'
|
||||
| '"Last Activity Date"' 'is' ('until'|'since') '"'DateDescription'"'
|
||||
| '"Close Date"' 'is' ('until'|'since') '"'DateDescription'"'
|
||||
| 'includes issue' '"'EntityReference'"'
|
||||
| 'includes commit' '"'CommitReference'"'
|
||||
| '"Title"' 'contains' '"'Text'"'
|
||||
| '"Description"' 'contains' '"'Text'"'
|
||||
| '"Comment"' 'contains' '"'Text'"'
|
||||
| '"Comment Count"' 'is' ('not')? '"'Number'"'
|
||||
| '"Comment Count"' 'is' ('greater'|'less') 'than' '"'Number'"'
|
||||
%s
|
||||
| '"Project"' 'is' ('not')? '"'ProjectPathOrPattern'"'
|
||||
| 'watched by' '"'LoginNameOfUser'"'
|
||||
| 'watched by me'
|
||||
| 'ignored by' '"'LoginNameOfUser'"'
|
||||
| 'ignored by me'
|
||||
| 'commented by' '"'LoginNameOfUser'"'
|
||||
| 'commented by me'
|
||||
| criteria 'and' criteria
|
||||
| criteria 'or' criteria
|
||||
| 'not('criteria')'
|
||||
| '('criteria')'
|
||||
;
|
||||
|
||||
EntityReference
|
||||
: '#'Number
|
||||
| ProjectPath'#'Number
|
||||
| ProjectKey'-'Number
|
||||
;
|
||||
|
||||
CommitReference
|
||||
: (ProjectPath':')?CommitHash
|
||||
;
|
||||
|
||||
BranchReference
|
||||
: (ProjectPath':')?BranchName
|
||||
;
|
||||
|
||||
TagReference
|
||||
: (ProjectPath':')?TagName
|
||||
;
|
||||
|
||||
MergeStrategy
|
||||
: %s
|
||||
;
|
||||
|
||||
LabelName
|
||||
: %s
|
||||
;
|
||||
|
||||
OrderField
|
||||
: %s
|
||||
;
|
||||
|
||||
WS
|
||||
: [ ]+ -> skip
|
||||
;
|
||||
|
||||
Please note:
|
||||
1. "LoginNameOfUser" should be retrieved via tool 'getLoginName' if available, with parameter set to user name
|
||||
2. Use an empty query to list all accessible pull requests""",
|
||||
REACTION_CRITERIAS,
|
||||
Arrays.stream(MergeStrategy.values()).map(it->"'" + it.name() + "'").collect(joining("\n | ")).trim(),
|
||||
getLabelSpecs().stream().map(it->"'" + it.getName() + "'").collect(joining("\n | ")).trim(),
|
||||
PullRequest.SORT_FIELDS.keySet().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim());
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
public static String getBuildQueryDescription() {
|
||||
var buildService = getBuildService();
|
||||
var buildParamService = getBuildParamService();
|
||||
var labelSpecService = getLabelSpecService();
|
||||
|
||||
var orderFields = new StringBuilder();
|
||||
for (var field : Build.SORT_FIELDS.keySet()) {
|
||||
orderFields.append("- ").append(field).append("\n");
|
||||
}
|
||||
var description = String.format("""
|
||||
A structured query should conform to below ANTLR grammar:
|
||||
|
||||
var jobNames = buildService.getJobNames(null).stream().collect(Collectors.joining(", "));
|
||||
var paramNames = buildParamService.getParamNames(null).stream().collect(Collectors.joining(", "));
|
||||
var labelNames = labelSpecService.query().stream().map(LabelSpec::getName).collect(Collectors.joining(", "));
|
||||
buildQuery
|
||||
: criteria ('order by' '"'OrderField'"' ('asc'|'desc') (',' '"'OrderField'"' ('asc'|'desc'))*)?
|
||||
;
|
||||
|
||||
var description =
|
||||
"A query string is one of below criteria:\n\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" +
|
||||
"- project criteria in form of: \"Project\" is \"<project path pattern>\" (quotes are required)\n" +
|
||||
"- and criteria in form of <criteria1> and <criteria2>\n" +
|
||||
"- or criteria in form of <criteria1> or <criteria2>\n" +
|
||||
"- operator 'and' takes precedence over 'or' when used together, unless parentheses are used to group 'or' criterias\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\n" +
|
||||
orderFields + "\n" +
|
||||
"Issue, build or pull request can be referenced by their number in form of: #<number>, <project path>#<number> or <project key>-<number>\n" +
|
||||
"\n" +
|
||||
"Leave empty to list all accessible builds";
|
||||
criteria
|
||||
: '"Number"' 'is' ('not')? '"'EntityReference'"'
|
||||
| '"Number"' 'is' ('greater'|'less') 'than' '"'EntityReference'"'
|
||||
| 'sucessful'
|
||||
| 'failed'
|
||||
| 'cancelled'
|
||||
| 'timed out'
|
||||
| 'finished'
|
||||
| 'running'
|
||||
| 'waiting'
|
||||
| 'pending'
|
||||
| 'submitted by' '"'LoginNameOfUser'"'
|
||||
| 'submitted by me'
|
||||
| 'cancelled by' '"'LoginNameOfUser'"'
|
||||
| 'cancelled by me'
|
||||
| 'depends on' '"'EntityReference'"'
|
||||
| 'dependencies of' '"'EntityReference'"'
|
||||
| 'ran on' '"'AgentName'"'
|
||||
| 'fixed issue' '"'EntityReference'"'
|
||||
| '"Project"' 'is' ('not')? '"'ProjectPathOrPattern'"'
|
||||
| '"Job"' 'is' ('not')? '"'JobNameOrPattern'"'
|
||||
| '"Version"' 'is' ('not')? '"'VersionNameOrPattern'"'
|
||||
| '"Branch"' 'is' ('not')? '"'BranchNameOrPattern'"'
|
||||
| '"Tag"' 'is' ('not')? '"'TagNameOrPattern'"'
|
||||
| '"Param"' 'is' ('not')? '"'ParamName'"'
|
||||
| '"Label"' 'is' ('not')? '"'LabelName'"'
|
||||
| '"Pull Request"' 'is' ('not')? '"'EntityReference'"'
|
||||
| '"Commit"' 'is' ('not')? '"'CommitReference'"'
|
||||
| '"Submit Date"' 'is' ('until'|'since') '"'DateDescription'"'
|
||||
| '"Pending Date"' 'is' ('until'|'since') '"'DateDescription'"'
|
||||
| '"Running Date"' 'is' ('until'|'since') '"'DateDescription'"'
|
||||
| '"Finish Date"' 'is' ('until'|'since') '"'DateDescription'"'
|
||||
| criteria 'and' criteria
|
||||
| criteria 'or' criteria
|
||||
| 'not('criteria')'
|
||||
| '('criteria')'
|
||||
;
|
||||
|
||||
EntityReference
|
||||
: '#'Number
|
||||
| ProjectPath'#'Number
|
||||
| ProjectKey'-'Number
|
||||
;
|
||||
|
||||
CommitReference
|
||||
: (ProjectPath':')?CommitHash
|
||||
;
|
||||
|
||||
LabelName
|
||||
: %s
|
||||
;
|
||||
|
||||
OrderField
|
||||
: %s
|
||||
;
|
||||
|
||||
WS
|
||||
: [ ]+ -> skip
|
||||
;
|
||||
|
||||
Please note:
|
||||
1. "LoginNameOfUser" should be retrieved via tool 'getLoginName' if available, with parameter set to user name
|
||||
2. Use an empty query to list all accessible builds""",
|
||||
getLabelSpecs().stream().map(it->"'" + it.getName() + "'").collect(joining("\n | ")).trim(),
|
||||
Build.SORT_FIELDS.keySet().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim());
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
private static List<LabelSpec> getLabelSpecs() {
|
||||
return OneDev.getInstance(LabelSpecService.class).query();
|
||||
}
|
||||
|
||||
public static String getPackQueryDescription() {
|
||||
var packSupports = new ArrayList<>(OneDev.getExtensions(PackSupport.class));
|
||||
|
||||
var description = String.format("""
|
||||
A structured query should conform to below ANTLR grammar:
|
||||
|
||||
packQuery
|
||||
: criteria ('order by' '"'OrderField'"' ('asc'|'desc') (',' '"'OrderField'"' ('asc'|'desc'))*)?
|
||||
;
|
||||
|
||||
criteria
|
||||
: 'published by me'
|
||||
| 'published by user' '"'LoginNameOfUser'"'
|
||||
| 'published by build' '"'EntityReference'"'
|
||||
| 'published by project' '"'ProjectPathOrPattern'"'
|
||||
| '"Project"' 'is' ('not')? '"'ProjectPathOrPattern'"'
|
||||
| '"Type"' 'is' ('not')? '"'PackType'"'
|
||||
| '"Name"' 'is' ('not')? '"'PackName'"'
|
||||
| '"Version"' 'is' ('not')? '"'PackVersion'"'
|
||||
| '"Label"' 'is' ('not')? '"'LabelName'"'
|
||||
| '"Publish Date"' 'is' ('until'|'since') '"'DateDescription'"'
|
||||
| criteria 'and' criteria
|
||||
| criteria 'or' criteria
|
||||
| 'not('criteria')'
|
||||
| '('criteria')'
|
||||
;
|
||||
|
||||
EntityReference
|
||||
: '#'Number
|
||||
| ProjectPath'#'Number
|
||||
| ProjectKey'-'Number
|
||||
;
|
||||
|
||||
PackType
|
||||
: %s
|
||||
;
|
||||
|
||||
LabelName
|
||||
: %s
|
||||
;
|
||||
|
||||
OrderField
|
||||
: %s
|
||||
;
|
||||
|
||||
WS
|
||||
: [ ]+ -> skip
|
||||
;
|
||||
|
||||
Please note:
|
||||
1. "LoginNameOfUser" should be retrieved via tool 'getLoginName' if available, with parameter set to user name
|
||||
2. Use an empty query to list all accessible packages""",
|
||||
packSupports.stream().map(it->"'" + it.getPackType() + "'").collect(joining("\n | ")).trim(),
|
||||
getLabelSpecs().stream().map(it->"'" + it.getName() + "'").collect(joining("\n | ")).trim(),
|
||||
Pack.SORT_FIELDS.keySet().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim());
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
public static String getCommitQueryDescription() {
|
||||
var description = """
|
||||
A structured query should conform to below ANTLR grammar:
|
||||
|
||||
commitQuery
|
||||
: criteria+
|
||||
;
|
||||
|
||||
criteria
|
||||
: ('before('|'after(') DateDescription ')'
|
||||
| 'committer(' CommitterNameAndEmail ')' // committer is specified user
|
||||
| 'author(' AuthorNameAndEmail ')' // author is specified user
|
||||
| 'message(' Text ')' // commit message contains specified text
|
||||
| 'path(' FilePath ')' // commit touches specified file
|
||||
| 'authored-by-me'
|
||||
| 'committed-by-me'
|
||||
| ('until')? revision // until specified revision
|
||||
| 'since' revision // since specified revision
|
||||
;
|
||||
|
||||
revision
|
||||
: 'commit(' CommitHash ')'
|
||||
| 'build(' '#'Number ')'
|
||||
| 'branch(' BranchName ')'
|
||||
| 'tag(' TagName ')'
|
||||
| 'default-branch'
|
||||
;
|
||||
|
||||
WS
|
||||
: [ ]+ -> skip
|
||||
;
|
||||
""";
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
public static String getProjectQueryDescription() {
|
||||
var description = String.format("""
|
||||
A structured query should conform to below ANTLR grammar:
|
||||
|
||||
projectQuery
|
||||
: criteria ('order by' '"'OrderField'"' ('asc'|'desc') (',' '"'OrderField'"' ('asc'|'desc'))*)?
|
||||
;
|
||||
|
||||
criteria
|
||||
: 'owned by' '"'LoginNameOfUser'"'
|
||||
| 'owned by me'
|
||||
| 'owned by none'
|
||||
| 'has outdated replicas'
|
||||
| 'without enough replicas'
|
||||
| 'missing storage'
|
||||
| 'children of' '"'ProjectPathOrPattern'"'
|
||||
| 'forks of' '"'ProjectPathOrPattern'"'
|
||||
| 'roots'
|
||||
| 'leafs'
|
||||
| 'fork roots'
|
||||
| '"Name"' 'is' ('not')? '"'ProjectNameOrPattern'"'
|
||||
| '"Key"' 'is' ('not')? '"'ProjectKeyOrPattern'"'
|
||||
| '"Path"' 'is' ('not')? '"'ProjectPathOrPattern'"'
|
||||
| '"Label"' 'is' ('not')? '"'LabelName'"'
|
||||
| '"Description"' 'contains' '"'Text'"'
|
||||
| '"Id"' 'is' ('not')? '"'Number'"'
|
||||
| '"Id"' 'is' ('greater'|'less') 'than' '"'Number'"'
|
||||
| '"Service Desk Email Address"' 'is' ('not')? '"'EmailAddressOrPattern'"'
|
||||
| '"Last Activity Date"' 'is' ('until'|'since') '"'DateDescription'"'
|
||||
| criteria 'and' criteria
|
||||
| criteria 'or' criteria
|
||||
| 'not('criteria')'
|
||||
| '('criteria')'
|
||||
;
|
||||
|
||||
LabelName
|
||||
: %s
|
||||
;
|
||||
|
||||
OrderField
|
||||
: %s
|
||||
;
|
||||
|
||||
WS
|
||||
: [ ]+ -> skip
|
||||
;
|
||||
|
||||
Please note:
|
||||
1. "LoginNameOfUser" should be retrieved via tool 'getLoginName' if available, with parameter set to user name
|
||||
2. Use an empty query to list all accessible projects""",
|
||||
getLabelSpecs().stream().map(it->"'" + it.getName() + "'").collect(joining("\n | ")).trim(),
|
||||
Project.SORT_FIELDS.keySet().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim());
|
||||
|
||||
return description;
|
||||
}
|
||||
|
||||
public static String getAgentQueryDescription() {
|
||||
var agentService = OneDev.getInstance(AgentService.class);
|
||||
var attributeService = OneDev.getInstance(AgentAttributeService.class);
|
||||
|
||||
var description = String.format("""
|
||||
A structured query should conform to below ANTLR grammar:
|
||||
|
||||
agentQuery
|
||||
: criteria ('order by' '"'OrderField'"' ('asc'|'desc') (',' '"'OrderField'"' ('asc'|'desc'))*)?
|
||||
;
|
||||
|
||||
criteria
|
||||
: 'online'
|
||||
| 'offline'
|
||||
| 'paused'
|
||||
| 'has running builds'
|
||||
| 'has attribute' '"'AttributeName'"'
|
||||
| 'not used since' '"'DateDescription'"'
|
||||
| 'ever used since' '"'DateDescription'"'
|
||||
| 'ran build' '"'EntityReference'"'
|
||||
| '"Name"' 'is' ('not')? '"'AgentNameOrPattern'"'
|
||||
| '"Ip Address"' 'is' ('not')? '"'IPAddressOrPattern'"'
|
||||
| '"Os"' 'is' ('not')? '"'(OsName|OsNamePattern)'"'
|
||||
| '"Os Arch"' 'is' ('not')? '"'(OsArch|OsArchPattern)'"'
|
||||
| '"Os Version"' 'is' ('not')? '"'OsVersionOrPattern'"'
|
||||
| '"'AttributeName'"' 'is' ('not')? '"'AttributeValue'"'
|
||||
| criteria 'and' criteria
|
||||
| criteria 'or' criteria
|
||||
| 'not('criteria')'
|
||||
| '('criteria')'
|
||||
;
|
||||
|
||||
OsName
|
||||
: %s
|
||||
;
|
||||
|
||||
OsArch
|
||||
: %s
|
||||
;
|
||||
|
||||
AttributeName
|
||||
: %s
|
||||
;
|
||||
|
||||
OrderField
|
||||
: %s
|
||||
;
|
||||
|
||||
WS
|
||||
: [ ]+ -> skip
|
||||
;
|
||||
|
||||
Please note:
|
||||
1. "LoginNameOfUser" should be retrieved via tool 'getLoginName' if available, with parameter set to user name
|
||||
2. Use an empty query to list all accessible agents""",
|
||||
agentService.getOsNames().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim(),
|
||||
agentService.getOsArchs().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim(),
|
||||
attributeService.getAttributeNames().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim(),
|
||||
Agent.SORT_FIELDS.keySet().stream().map(it->"'" + it + "'").collect(joining("\n | ")).trim());
|
||||
|
||||
return description;
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +87,15 @@ public abstract class EntityReference implements Serializable {
|
||||
else
|
||||
throw new ValidationException("Reference project not found with key: " + projectKey);
|
||||
}
|
||||
throw new ValidationException("Invalid entity reference: " + referenceString);
|
||||
try {
|
||||
var number = Long.valueOf(referenceString);
|
||||
if (currentProject != null)
|
||||
return EntityReference.of(type, currentProject, number);
|
||||
else
|
||||
throw new ValidationException("Reference project not specified: " + referenceString);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new ValidationException("Invalid entity reference: " + referenceString);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -7,17 +7,31 @@ import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.validation.ValidationException;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
|
||||
import io.onedev.commons.codeassist.FenceAware;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.entityreference.BuildReference;
|
||||
import io.onedev.server.entityreference.IssueReference;
|
||||
import io.onedev.server.entityreference.PullRequestReference;
|
||||
import io.onedev.server.model.AbstractEntity;
|
||||
import io.onedev.server.model.Build;
|
||||
import io.onedev.server.model.Group;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.LabelSpec;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.PullRequest;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.service.BuildService;
|
||||
import io.onedev.server.service.GroupService;
|
||||
import io.onedev.server.service.IssueService;
|
||||
import io.onedev.server.service.IterationService;
|
||||
import io.onedev.server.service.LabelSpecService;
|
||||
@ -25,17 +39,6 @@ import io.onedev.server.service.ProjectService;
|
||||
import io.onedev.server.service.PullRequestService;
|
||||
import io.onedev.server.service.SettingService;
|
||||
import io.onedev.server.service.UserService;
|
||||
import io.onedev.server.entityreference.BuildReference;
|
||||
import io.onedev.server.entityreference.IssueReference;
|
||||
import io.onedev.server.entityreference.PullRequestReference;
|
||||
import io.onedev.server.model.AbstractEntity;
|
||||
import io.onedev.server.model.Build;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.Iteration;
|
||||
import io.onedev.server.model.LabelSpec;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.model.PullRequest;
|
||||
import io.onedev.server.model.User;
|
||||
import io.onedev.server.util.DateUtils;
|
||||
import io.onedev.server.util.ProjectScopedCommit;
|
||||
import io.onedev.server.util.ProjectScopedRevision;
|
||||
@ -139,6 +142,13 @@ public abstract class EntityQuery<T extends AbstractEntity> implements Serializa
|
||||
throw new ExplicitException("Unable to find user with login: " + loginName);
|
||||
return user;
|
||||
}
|
||||
|
||||
public static Group getGroup(String groupName) {
|
||||
Group group = OneDev.getInstance(GroupService.class).find(groupName);
|
||||
if (group == null)
|
||||
throw new ExplicitException("Unable to find group: " + groupName);
|
||||
return group;
|
||||
}
|
||||
|
||||
public static Project getProject(String projectPath) {
|
||||
Project project = OneDev.getInstance(ProjectService.class).findByPath(projectPath);
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
package io.onedev.server.search.entity.issue;
|
||||
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.From;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueField;
|
||||
|
||||
public class FloatFieldCriteria extends FieldCriteria {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final float value;
|
||||
|
||||
private final int operator;
|
||||
|
||||
public FloatFieldCriteria(String name, float value, int operator) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Predicate getValuePredicate(From<Issue, Issue> issueFrom, From<IssueField, IssueField> fieldFrom, CriteriaBuilder builder) {
|
||||
Path<Float> attribute = fieldFrom.get(IssueField.PROP_ORDINAL);
|
||||
if (operator == IssueQueryLexer.IsGreaterThan)
|
||||
return builder.greaterThan(attribute, value);
|
||||
else
|
||||
return builder.lessThan(attribute, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Issue issue) {
|
||||
Float fieldValue = (Float) issue.getFieldValue(getFieldName());
|
||||
if (operator == IssueQueryLexer.IsGreaterThan)
|
||||
return fieldValue != null && fieldValue > value;
|
||||
else
|
||||
return fieldValue != null && fieldValue < value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringWithoutParens() {
|
||||
return quote(getFieldName()) + " "
|
||||
+ IssueQuery.getRuleName(operator) + " "
|
||||
+ quote(String.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fill(Issue issue) {
|
||||
}
|
||||
|
||||
}
|
||||
@ -10,8 +10,7 @@ import javax.persistence.criteria.Predicate;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueField;
|
||||
|
||||
|
||||
public class NumericFieldCriteria extends FieldCriteria {
|
||||
public class IntegerFieldCriteria extends FieldCriteria {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -19,7 +18,7 @@ public class NumericFieldCriteria extends FieldCriteria {
|
||||
|
||||
private final int operator;
|
||||
|
||||
public NumericFieldCriteria(String name, int value, int operator) {
|
||||
public IntegerFieldCriteria(String name, int value, int operator) {
|
||||
super(name);
|
||||
this.value = value;
|
||||
this.operator = operator;
|
||||
@ -1,5 +1,6 @@
|
||||
package io.onedev.server.search.entity.issue;
|
||||
|
||||
import static io.onedev.server.model.AbstractEntity.NAME_NUMBER;
|
||||
import static io.onedev.server.model.Issue.NAME_COMMENT;
|
||||
import static io.onedev.server.model.Issue.NAME_COMMENT_COUNT;
|
||||
import static io.onedev.server.model.Issue.NAME_CONFUSED_COUNT;
|
||||
@ -56,8 +57,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.antlr.v4.runtime.BailErrorStrategy;
|
||||
import org.antlr.v4.runtime.BaseErrorListener;
|
||||
import org.antlr.v4.runtime.CharStream;
|
||||
@ -65,11 +64,11 @@ import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.CommonTokenStream;
|
||||
import org.antlr.v4.runtime.RecognitionException;
|
||||
import org.antlr.v4.runtime.Recognizer;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import io.onedev.commons.codeassist.AntlrUtils;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.service.SettingService;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueSchedule;
|
||||
import io.onedev.server.model.Project;
|
||||
@ -80,6 +79,7 @@ import io.onedev.server.model.support.issue.field.spec.CommitField;
|
||||
import io.onedev.server.model.support.issue.field.spec.DateField;
|
||||
import io.onedev.server.model.support.issue.field.spec.DateTimeField;
|
||||
import io.onedev.server.model.support.issue.field.spec.FieldSpec;
|
||||
import io.onedev.server.model.support.issue.field.spec.FloatField;
|
||||
import io.onedev.server.model.support.issue.field.spec.GroupChoiceField;
|
||||
import io.onedev.server.model.support.issue.field.spec.IntegerField;
|
||||
import io.onedev.server.model.support.issue.field.spec.IssueChoiceField;
|
||||
@ -106,6 +106,7 @@ import io.onedev.server.search.entity.issue.IssueQueryParser.ParensCriteriaConte
|
||||
import io.onedev.server.search.entity.issue.IssueQueryParser.QueryContext;
|
||||
import io.onedev.server.search.entity.issue.IssueQueryParser.ReferenceCriteriaContext;
|
||||
import io.onedev.server.search.entity.issue.IssueQueryParser.RevisionCriteriaContext;
|
||||
import io.onedev.server.service.SettingService;
|
||||
import io.onedev.server.util.criteria.AndCriteria;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
import io.onedev.server.util.criteria.NotCriteria;
|
||||
@ -363,7 +364,7 @@ public class IssueQuery extends EntityQuery<Issue> {
|
||||
criterias.add(new RocketCountCriteria(getIntValue(value), operator));
|
||||
} else if (fieldName.equals(NAME_EYES_COUNT)) {
|
||||
criterias.add(new EyesCountCriteria(getIntValue(value), operator));
|
||||
} else if (fieldName.equals(Issue.NAME_NUMBER)) {
|
||||
} else if (fieldName.equals(NAME_NUMBER)) {
|
||||
criterias.add(new ReferenceCriteria(project, value, operator));
|
||||
} else if (fieldName.equals(NAME_ESTIMATED_TIME)) {
|
||||
int intValue = timeTrackingSetting.parseWorkingPeriod(value);
|
||||
@ -384,13 +385,16 @@ public class IssueQuery extends EntityQuery<Issue> {
|
||||
} else if (field instanceof BooleanField) {
|
||||
criterias.add(new BooleanFieldCriteria(fieldName, getBooleanValue(value), operator));
|
||||
} else if (field instanceof IntegerField) {
|
||||
criterias.add(new NumericFieldCriteria(fieldName, getIntValue(value), operator));
|
||||
criterias.add(new IntegerFieldCriteria(fieldName, getIntValue(value), operator));
|
||||
} else if (field instanceof ChoiceField) {
|
||||
long ordinal = getValueOrdinal((ChoiceField) field, value);
|
||||
criterias.add(new ChoiceFieldCriteria(fieldName, value, ordinal, operator, field.isAllowMultiple()));
|
||||
} else if (field instanceof UserChoiceField
|
||||
|| field instanceof GroupChoiceField) {
|
||||
criterias.add(new ChoiceFieldCriteria(fieldName, value, -1, operator, field.isAllowMultiple()));
|
||||
} else if (field instanceof UserChoiceField) {
|
||||
// call getUser(value) to validate the user name
|
||||
criterias.add(new ChoiceFieldCriteria(fieldName, getUser(value).getName(), -1, operator, field.isAllowMultiple()));
|
||||
} else if (field instanceof GroupChoiceField) {
|
||||
// call getGroup(value) to validate the group name
|
||||
criterias.add(new ChoiceFieldCriteria(fieldName, getGroup(value).getName(), -1, operator, field.isAllowMultiple()));
|
||||
} else if (field instanceof IterationChoiceField) {
|
||||
criterias.add(new IterationFieldCriteria(fieldName, value, operator, field.isAllowMultiple()));
|
||||
} else {
|
||||
@ -443,10 +447,15 @@ public class IssueQuery extends EntityQuery<Issue> {
|
||||
var floatValue = getFloatValue(value);
|
||||
criterias.add(new ProgressCriteria(floatValue, operator));
|
||||
break;
|
||||
case NAME_NUMBER:
|
||||
criterias.add(new ReferenceCriteria(project, value, operator));
|
||||
break;
|
||||
default:
|
||||
FieldSpec field = getGlobalIssueSetting().getFieldSpec(fieldName);
|
||||
if (field instanceof IntegerField) {
|
||||
criterias.add(new NumericFieldCriteria(fieldName, getIntValue(value), operator));
|
||||
criterias.add(new IntegerFieldCriteria(fieldName, getIntValue(value), operator));
|
||||
} else if (field instanceof FloatField) {
|
||||
criterias.add(new FloatFieldCriteria(fieldName, getFloatValue(value), operator));
|
||||
} else {
|
||||
long ordinal;
|
||||
if (validate)
|
||||
@ -600,7 +609,7 @@ public class IssueQuery extends EntityQuery<Issue> {
|
||||
&& !fieldName.equals(Issue.NAME_HEART_COUNT)
|
||||
&& !fieldName.equals(Issue.NAME_ROCKET_COUNT)
|
||||
&& !fieldName.equals(Issue.NAME_EYES_COUNT)
|
||||
&& !fieldName.equals(Issue.NAME_NUMBER)
|
||||
&& !fieldName.equals(NAME_NUMBER)
|
||||
&& !fieldName.equals(IssueSchedule.NAME_ITERATION)
|
||||
&& !(fieldSpec instanceof IssueChoiceField)
|
||||
&& !(fieldSpec instanceof PullRequestChoiceField)
|
||||
@ -636,8 +645,9 @@ public class IssueQuery extends EntityQuery<Issue> {
|
||||
&& !fieldName.equals(Issue.NAME_HEART_COUNT)
|
||||
&& !fieldName.equals(Issue.NAME_ROCKET_COUNT)
|
||||
&& !fieldName.equals(Issue.NAME_EYES_COUNT)
|
||||
&& !fieldName.equals(Issue.NAME_NUMBER)
|
||||
&& !fieldName.equals(NAME_NUMBER)
|
||||
&& !(fieldSpec instanceof IntegerField)
|
||||
&& !(fieldSpec instanceof FloatField)
|
||||
&& !(fieldSpec instanceof ChoiceField && !fieldSpec.isAllowMultiple()))
|
||||
throw newOperatorException(fieldName, operator);
|
||||
break;
|
||||
|
||||
@ -16,7 +16,6 @@ import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.util.ProjectScope;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
|
||||
|
||||
public class SpentTimeCriteria extends Criteria<Issue> {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -8,7 +8,6 @@ import javax.persistence.criteria.Predicate;
|
||||
import io.onedev.server.model.Issue;
|
||||
import io.onedev.server.model.IssueField;
|
||||
|
||||
|
||||
public class StringFieldCriteria extends FieldCriteria {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
package io.onedev.server.search.entity.project;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.From;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.util.ProjectScope;
|
||||
import io.onedev.server.util.criteria.Criteria;
|
||||
@ -25,18 +27,27 @@ public class IdCriteria extends Criteria<Project> {
|
||||
|
||||
@Override
|
||||
public Predicate getPredicate(@Nullable ProjectScope projectScope, CriteriaQuery<?> query, From<Project, Project> from, CriteriaBuilder builder) {
|
||||
Path<Long> attribute = from.get(Project.PROP_ID);
|
||||
if (operator == ProjectQueryLexer.Is)
|
||||
return builder.equal(from.get(Project.PROP_ID), value);
|
||||
return builder.equal(attribute, value);
|
||||
else if (operator == ProjectQueryLexer.IsNot)
|
||||
return builder.not(builder.equal(attribute, value));
|
||||
else if (operator == ProjectQueryLexer.IsGreaterThan)
|
||||
return builder.greaterThan(attribute, value);
|
||||
else
|
||||
return builder.not(builder.equal(from.get(Project.PROP_ID), value));
|
||||
return builder.lessThan(attribute, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Project project) {
|
||||
if (operator == ProjectQueryLexer.Is)
|
||||
return project.getId().equals(value);
|
||||
else
|
||||
else if (operator == ProjectQueryLexer.IsNot)
|
||||
return !project.getId().equals(value);
|
||||
else if (operator == ProjectQueryLexer.IsGreaterThan)
|
||||
return project.getId() > value;
|
||||
else
|
||||
return project.getId() < value;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -9,7 +9,7 @@ query
|
||||
criteria
|
||||
: operator=(Roots|Leafs|ForkRoots|OwnedByMe|OwnedByNone|HasOutdatedReplicas|WithoutEnoughReplicas|MissingStorage) #OperatorCriteria
|
||||
| operator=(OwnedBy|ForksOf|ChildrenOf) WS+ criteriaValue=multipleQuoted #OperatorValueCriteria
|
||||
| criteriaField=Quoted WS+ operator=(Is|IsNot|Contains|IsUntil|IsSince) WS+ criteriaValue=multipleQuoted #FieldOperatorValueCriteria
|
||||
| criteriaField=Quoted WS+ operator=(Is|IsNot|IsGreaterThan|IsLessThan|Contains|IsUntil|IsSince) WS+ criteriaValue=multipleQuoted #FieldOperatorValueCriteria
|
||||
| criteria WS+ And WS+ criteria #AndCriteria
|
||||
| criteria WS+ Or WS+ criteria #OrCriteria
|
||||
| Not WS* LParens WS* criteria WS* RParens #NotCriteria
|
||||
@ -37,6 +37,14 @@ IsNot
|
||||
: 'is' WS+ 'not'
|
||||
;
|
||||
|
||||
IsGreaterThan
|
||||
: 'is' WS+ 'greater' WS+ 'than'
|
||||
;
|
||||
|
||||
IsLessThan
|
||||
: 'is' WS+ 'less' WS+ 'than'
|
||||
;
|
||||
|
||||
OwnedBy
|
||||
: 'owned' WS+ 'by'
|
||||
;
|
||||
|
||||
@ -16,6 +16,8 @@ import static io.onedev.server.search.entity.project.ProjectQueryParser.Leafs;
|
||||
import static io.onedev.server.search.entity.project.ProjectQueryParser.MissingStorage;
|
||||
import static io.onedev.server.search.entity.project.ProjectQueryParser.OwnedByMe;
|
||||
import static io.onedev.server.search.entity.project.ProjectQueryParser.OwnedByNone;
|
||||
import static io.onedev.server.search.entity.project.ProjectQueryParser.IsGreaterThan;
|
||||
import static io.onedev.server.search.entity.project.ProjectQueryParser.IsLessThan;
|
||||
import static io.onedev.server.search.entity.project.ProjectQueryParser.Roots;
|
||||
import static io.onedev.server.search.entity.project.ProjectQueryParser.WithoutEnoughReplicas;
|
||||
|
||||
@ -177,6 +179,10 @@ public class ProjectQuery extends EntityQuery<Project> {
|
||||
criterias.add(new LabelCriteria(getLabelSpec(value), operator));
|
||||
}
|
||||
break;
|
||||
case IsGreaterThan:
|
||||
case IsLessThan:
|
||||
criterias.add(new IdCriteria(getLongValue(value), operator));
|
||||
break;
|
||||
case Contains:
|
||||
criterias.add(new DescriptionCriteria(value));
|
||||
break;
|
||||
@ -266,6 +272,12 @@ public class ProjectQuery extends EntityQuery<Project> {
|
||||
throw newOperatorException(fieldName, operator);
|
||||
}
|
||||
break;
|
||||
case IsGreaterThan:
|
||||
case IsLessThan:
|
||||
if (!fieldName.equals(Project.NAME_ID)) {
|
||||
throw newOperatorException(fieldName, operator);
|
||||
}
|
||||
break;
|
||||
case IsUntil:
|
||||
case IsSince:
|
||||
if (!fieldName.equals(Project.NAME_LAST_ACTIVITY_DATE)
|
||||
|
||||
@ -1,7 +1,16 @@
|
||||
package io.onedev.server.web.behavior;
|
||||
|
||||
import static io.onedev.server.web.translation.Translation._T;
|
||||
import static java.util.Collections.sort;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import io.onedev.commons.codeassist.FenceAware;
|
||||
import io.onedev.commons.codeassist.InputCompletion;
|
||||
import io.onedev.commons.codeassist.InputSuggestion;
|
||||
@ -11,23 +20,19 @@ import io.onedev.commons.codeassist.parser.ParseExpect;
|
||||
import io.onedev.commons.codeassist.parser.TerminalExpect;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.service.AgentAttributeService;
|
||||
import io.onedev.server.service.AgentService;
|
||||
import io.onedev.server.ai.QueryDescriptions;
|
||||
import io.onedev.server.model.Agent;
|
||||
import io.onedev.server.search.entity.agent.AgentQuery;
|
||||
import io.onedev.server.search.entity.agent.AgentQueryLexer;
|
||||
import io.onedev.server.search.entity.agent.AgentQueryParser;
|
||||
import io.onedev.server.service.AgentAttributeService;
|
||||
import io.onedev.server.service.AgentService;
|
||||
import io.onedev.server.service.SettingService;
|
||||
import io.onedev.server.util.DateUtils;
|
||||
import io.onedev.server.web.behavior.inputassist.ANTLRAssistBehavior;
|
||||
import io.onedev.server.web.behavior.inputassist.InputAssistBehavior;
|
||||
import io.onedev.server.web.behavior.inputassist.NaturalLanguageTranslator;
|
||||
import io.onedev.server.web.util.SuggestionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.onedev.server.web.translation.Translation._T;
|
||||
import static java.util.Collections.sort;
|
||||
|
||||
public class AgentQueryBehavior extends ANTLRAssistBehavior {
|
||||
|
||||
@ -48,11 +53,11 @@ public class AgentQueryBehavior extends ANTLRAssistBehavior {
|
||||
@Override
|
||||
protected List<InputSuggestion> match(String matchWith) {
|
||||
AgentService agentService = OneDev.getInstance(AgentService.class);
|
||||
AgentAttributeService attributeManager = OneDev.getInstance(AgentAttributeService.class);
|
||||
AgentAttributeService attributeService = OneDev.getInstance(AgentAttributeService.class);
|
||||
ParseExpect criteriaValueExpect;
|
||||
if ("criteriaField".equals(spec.getLabel())) {
|
||||
var fields = new ArrayList<>(Agent.QUERY_FIELDS);
|
||||
var attributeNames = new ArrayList<>(attributeManager.getAttributeNames());
|
||||
var attributeNames = new ArrayList<>(attributeService.getAttributeNames());
|
||||
sort(attributeNames);
|
||||
fields.addAll(attributeNames);
|
||||
return SuggestionUtils.suggest(fields, matchWith);
|
||||
@ -69,7 +74,7 @@ public class AgentQueryBehavior extends ANTLRAssistBehavior {
|
||||
if (operator == AgentQueryLexer.EverUsedSince || operator == AgentQueryLexer.NotUsedSince) {
|
||||
return SuggestionUtils.suggest(DateUtils.RELAX_DATE_EXAMPLES, matchWith);
|
||||
} else if (operator == AgentQueryLexer.HasAttribute) {
|
||||
var attributeNames = new ArrayList<>(attributeManager.getAttributeNames());
|
||||
var attributeNames = new ArrayList<>(attributeService.getAttributeNames());
|
||||
sort(attributeNames);
|
||||
return SuggestionUtils.suggest(attributeNames, matchWith);
|
||||
} else if (operator == AgentQueryLexer.RanBuild) {
|
||||
@ -184,6 +189,27 @@ public class AgentQueryBehavior extends ANTLRAssistBehavior {
|
||||
return hints;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NaturalLanguageTranslator getNaturalLanguageTranslator() {
|
||||
var liteModel = getSettingService().getAISetting().getLiteModel();
|
||||
if (liteModel != null) {
|
||||
return new NaturalLanguageTranslator(liteModel) {
|
||||
|
||||
@Override
|
||||
public String getQueryDescription() {
|
||||
return QueryDescriptions.getAgentQueryDescription();
|
||||
}
|
||||
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private SettingService getSettingService() {
|
||||
return OneDev.getInstance(SettingService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isFuzzySuggestion(InputCompletion suggestion) {
|
||||
return suggestion.getLabel().startsWith("~")
|
||||
|
||||
@ -25,12 +25,15 @@ import io.onedev.commons.utils.StringUtils;
|
||||
import io.onedev.commons.utils.match.PatternApplied;
|
||||
import io.onedev.commons.utils.match.WildcardUtils;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.ai.QueryDescriptions;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.search.commit.CommitQueryParser;
|
||||
import io.onedev.server.service.SettingService;
|
||||
import io.onedev.server.util.DateUtils;
|
||||
import io.onedev.server.util.NameAndEmail;
|
||||
import io.onedev.server.web.behavior.inputassist.ANTLRAssistBehavior;
|
||||
import io.onedev.server.web.behavior.inputassist.InputAssistBehavior;
|
||||
import io.onedev.server.web.behavior.inputassist.NaturalLanguageTranslator;
|
||||
import io.onedev.server.web.util.SuggestionUtils;
|
||||
import io.onedev.server.xodus.CommitInfoService;
|
||||
|
||||
@ -194,6 +197,27 @@ public class CommitQueryBehavior extends ANTLRAssistBehavior {
|
||||
return Optional.fromNullable(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NaturalLanguageTranslator getNaturalLanguageTranslator() {
|
||||
var liteModel = getSettingService().getAISetting().getLiteModel();
|
||||
if (liteModel != null) {
|
||||
return new NaturalLanguageTranslator(liteModel) {
|
||||
|
||||
@Override
|
||||
public String getQueryDescription() {
|
||||
return QueryDescriptions.getCommitQueryDescription();
|
||||
}
|
||||
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private SettingService getSettingService() {
|
||||
return OneDev.getInstance(SettingService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isFuzzySuggestion(InputCompletion suggestion) {
|
||||
return suggestion.getLabel().startsWith("~")
|
||||
|
||||
@ -1,7 +1,24 @@
|
||||
package io.onedev.server.web.behavior;
|
||||
|
||||
import static io.onedev.server.model.Pack.NAME_LABEL;
|
||||
import static io.onedev.server.model.Pack.NAME_NAME;
|
||||
import static io.onedev.server.model.Pack.NAME_TYPE;
|
||||
import static io.onedev.server.model.Pack.NAME_VERSION;
|
||||
import static io.onedev.server.model.Pack.PROP_NAME;
|
||||
import static io.onedev.server.model.Pack.PROP_VERSION;
|
||||
import static io.onedev.server.web.translation.Translation._T;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.wicket.Component;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import io.onedev.commons.codeassist.AntlrUtils;
|
||||
import io.onedev.commons.codeassist.FenceAware;
|
||||
import io.onedev.commons.codeassist.InputCompletion;
|
||||
@ -11,25 +28,19 @@ import io.onedev.commons.codeassist.parser.Element;
|
||||
import io.onedev.commons.codeassist.parser.ParseExpect;
|
||||
import io.onedev.commons.codeassist.parser.TerminalExpect;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.ai.QueryDescriptions;
|
||||
import io.onedev.server.model.Pack;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.search.entity.pack.PackQuery;
|
||||
import io.onedev.server.search.entity.pack.PackQueryLexer;
|
||||
import io.onedev.server.search.entity.pack.PackQueryParser;
|
||||
import io.onedev.server.service.SettingService;
|
||||
import io.onedev.server.util.DateUtils;
|
||||
import io.onedev.server.web.behavior.inputassist.ANTLRAssistBehavior;
|
||||
import io.onedev.server.web.behavior.inputassist.InputAssistBehavior;
|
||||
import io.onedev.server.web.behavior.inputassist.NaturalLanguageTranslator;
|
||||
import io.onedev.server.web.util.SuggestionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.wicket.Component;
|
||||
import org.apache.wicket.model.IModel;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static io.onedev.server.model.Pack.*;
|
||||
import static io.onedev.server.web.translation.Translation._T;
|
||||
|
||||
public class PackQueryBehavior extends ANTLRAssistBehavior {
|
||||
|
||||
@ -218,6 +229,27 @@ public class PackQueryBehavior extends ANTLRAssistBehavior {
|
||||
return hints;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NaturalLanguageTranslator getNaturalLanguageTranslator() {
|
||||
var liteModel = getSettingService().getAISetting().getLiteModel();
|
||||
if (liteModel != null) {
|
||||
return new NaturalLanguageTranslator(liteModel) {
|
||||
|
||||
@Override
|
||||
public String getQueryDescription() {
|
||||
return QueryDescriptions.getPackQueryDescription();
|
||||
}
|
||||
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private SettingService getSettingService() {
|
||||
return OneDev.getInstance(SettingService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isFuzzySuggestion(InputCompletion suggestion) {
|
||||
return suggestion.getLabel().startsWith("~")
|
||||
|
||||
@ -1,7 +1,22 @@
|
||||
package io.onedev.server.web.behavior;
|
||||
|
||||
import static io.onedev.server.search.entity.project.ProjectQuery.getRuleName;
|
||||
import static io.onedev.server.search.entity.project.ProjectQueryParser.ChildrenOf;
|
||||
import static io.onedev.server.search.entity.project.ProjectQueryParser.HasOutdatedReplicas;
|
||||
import static io.onedev.server.search.entity.project.ProjectQueryParser.Roots;
|
||||
import static io.onedev.server.search.entity.project.ProjectQueryParser.WithoutEnoughReplicas;
|
||||
import static io.onedev.server.web.translation.Translation._T;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.base.Preconditions;
|
||||
|
||||
import io.onedev.commons.codeassist.FenceAware;
|
||||
import io.onedev.commons.codeassist.InputCompletion;
|
||||
import io.onedev.commons.codeassist.InputSuggestion;
|
||||
@ -11,29 +26,21 @@ import io.onedev.commons.codeassist.parser.ParseExpect;
|
||||
import io.onedev.commons.codeassist.parser.TerminalExpect;
|
||||
import io.onedev.commons.utils.ExplicitException;
|
||||
import io.onedev.server.OneDev;
|
||||
import io.onedev.server.ai.QueryDescriptions;
|
||||
import io.onedev.server.cluster.ClusterService;
|
||||
import io.onedev.server.service.ProjectService;
|
||||
import io.onedev.server.service.SettingService;
|
||||
import io.onedev.server.model.Project;
|
||||
import io.onedev.server.search.entity.project.ProjectQuery;
|
||||
import io.onedev.server.search.entity.project.ProjectQueryLexer;
|
||||
import io.onedev.server.search.entity.project.ProjectQueryParser;
|
||||
import io.onedev.server.security.SecurityUtils;
|
||||
import io.onedev.server.security.permission.AccessProject;
|
||||
import io.onedev.server.service.ProjectService;
|
||||
import io.onedev.server.service.SettingService;
|
||||
import io.onedev.server.util.DateUtils;
|
||||
import io.onedev.server.util.facade.ProjectCache;
|
||||
import io.onedev.server.web.behavior.inputassist.ANTLRAssistBehavior;
|
||||
import io.onedev.server.web.behavior.inputassist.NaturalLanguageTranslator;
|
||||
import io.onedev.server.web.util.SuggestionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static io.onedev.server.search.entity.project.ProjectQuery.getRuleName;
|
||||
import static io.onedev.server.search.entity.project.ProjectQueryParser.*;
|
||||
import static io.onedev.server.web.translation.Translation._T;
|
||||
|
||||
public class ProjectQueryBehavior extends ANTLRAssistBehavior {
|
||||
|
||||
@ -221,6 +228,27 @@ public class ProjectQueryBehavior extends ANTLRAssistBehavior {
|
||||
return hints;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected NaturalLanguageTranslator getNaturalLanguageTranslator() {
|
||||
var liteModel = getSettingService().getAISetting().getLiteModel();
|
||||
if (liteModel != null) {
|
||||
return new NaturalLanguageTranslator(liteModel) {
|
||||
|
||||
@Override
|
||||
public String getQueryDescription() {
|
||||
return QueryDescriptions.getProjectQueryDescription();
|
||||
}
|
||||
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private SettingService getSettingService() {
|
||||
return OneDev.getInstance(SettingService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isFuzzySuggestion(InputCompletion suggestion) {
|
||||
return suggestion.getLabel().startsWith("~")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user