/** * Evaluate the submit rules. * * @return List of {@link SubmitRecord} objects returned from the evaluated rules, including any * errors. */ public List<SubmitRecord> evaluate() { Change c = control.getChange(); if (!allowClosed && c.getStatus().isClosed()) { SubmitRecord rec = new SubmitRecord(); rec.status = SubmitRecord.Status.CLOSED; return Collections.singletonList(rec); } if (!allowDraft) { if (c.getStatus() == Change.Status.DRAFT) { return cannotSubmitDraft(); } try { initPatchSet(); } catch (OrmException e) { return ruleError("Error looking up patch set " + control.getChange().currentPatchSetId()); } if (patchSet.isDraft()) { return cannotSubmitDraft(); } } List<Term> results; try { results = evaluateImpl( "locate_submit_rule", "can_submit", "locate_submit_filter", "filter_submit_results", control.getUser()); } catch (RuleEvalException e) { return ruleError(e.getMessage(), e); } if (results.isEmpty()) { // This should never occur. A well written submit rule will always produce // at least one result informing the caller of the labels that are // required for this change to be submittable. Each label will indicate // whether or not that is actually possible given the permissions. return ruleError( String.format( "Submit rule '%s' for change %s of %s has " + "no solution.", getSubmitRuleName(), cd.getId(), getProjectName())); } return resultsToSubmitRecord(getSubmitRule(), results); }
public static List<SubmitRecord> createRuleError(String err) { SubmitRecord rec = new SubmitRecord(); rec.status = SubmitRecord.Status.RULE_ERROR; rec.errorMessage = err; return Collections.singletonList(rec); }
/** * Convert the results from Prolog Cafe's format to Gerrit's common format. * * <p>can_submit/1 terminates when an ok(P) record is found. Therefore walk the results backwards, * using only that ok(P) record if it exists. This skips partial results that occur early in the * output. Later after the loop the out collection is reversed to restore it to the original * ordering. */ private List<SubmitRecord> resultsToSubmitRecord(Term submitRule, List<Term> results) { List<SubmitRecord> out = new ArrayList<>(results.size()); for (int resultIdx = results.size() - 1; 0 <= resultIdx; resultIdx--) { Term submitRecord = results.get(resultIdx); SubmitRecord rec = new SubmitRecord(); out.add(rec); if (!(submitRecord instanceof StructureTerm) || 1 != submitRecord.arity()) { return invalidResult(submitRule, submitRecord); } if ("ok".equals(submitRecord.name())) { rec.status = SubmitRecord.Status.OK; } else if ("not_ready".equals(submitRecord.name())) { rec.status = SubmitRecord.Status.NOT_READY; } else { return invalidResult(submitRule, submitRecord); } // Unpack the one argument. This should also be a structure with one // argument per label that needs to be reported on to the caller. // submitRecord = submitRecord.arg(0); if (!(submitRecord instanceof StructureTerm)) { return invalidResult(submitRule, submitRecord); } rec.labels = new ArrayList<>(submitRecord.arity()); for (Term state : ((StructureTerm) submitRecord).args()) { if (!(state instanceof StructureTerm) || 2 != state.arity() || !"label".equals(state.name())) { return invalidResult(submitRule, submitRecord); } SubmitRecord.Label lbl = new SubmitRecord.Label(); rec.labels.add(lbl); lbl.label = state.arg(0).name(); Term status = state.arg(1); try { if ("ok".equals(status.name())) { lbl.status = SubmitRecord.Label.Status.OK; appliedBy(lbl, status); } else if ("reject".equals(status.name())) { lbl.status = SubmitRecord.Label.Status.REJECT; appliedBy(lbl, status); } else if ("need".equals(status.name())) { lbl.status = SubmitRecord.Label.Status.NEED; } else if ("may".equals(status.name())) { lbl.status = SubmitRecord.Label.Status.MAY; } else if ("impossible".equals(status.name())) { lbl.status = SubmitRecord.Label.Status.IMPOSSIBLE; } else { return invalidResult(submitRule, submitRecord); } } catch (UserTermExpected e) { return invalidResult(submitRule, submitRecord, e.getMessage()); } } if (rec.status == SubmitRecord.Status.OK) { break; } } Collections.reverse(out); return out; }