|
1154 | 1154 | (:report/close-reason word-result) (assoc :report/close-reason |
1155 | 1155 | (:report/close-reason word-result))))))) |
1156 | 1156 |
|
| 1157 | +(def carrier-eligible-ids |
| 1158 | + "Command ids whose intent is unambiguously cross-report: when carried |
| 1159 | + by a mail that also creates a new report, they apply to that new |
| 1160 | + report rather than the thread parent. Restricted to two relation |
| 1161 | + annotations with explicit external mids: |
| 1162 | + - :supersedes -- 'this new report supersedes <old>' |
| 1163 | + - :related-to -- 'this new report is related to <other>' |
| 1164 | +
|
| 1165 | + Excluded by design: |
| 1166 | + - :superseded-by / :duplicate-of (no one opens a new report just to |
| 1167 | + declare it's superseded or a duplicate); |
| 1168 | + - state-change triggers (close/ack/own) -- ambiguous on a brand-new |
| 1169 | + report; |
| 1170 | + - non-relation annotations (urgent/important/topic/deadline/expiry) |
| 1171 | + -- the intent is usually 'this thread', not the new report. |
| 1172 | +
|
| 1173 | + Unsets (:unsupersedes, :unrelated-to) are intentionally NOT in the |
| 1174 | + carrier set: there is nothing to undo on a freshly-created report." |
| 1175 | + #{:supersedes :related-to}) |
| 1176 | + |
1157 | 1177 | (defn apply-commands! |
1158 | 1178 | "Apply commands from `email` against `report-eid`. |
1159 | 1179 | A reply shipping patch content also fires an implicit `Acked. Owned.`, |
1160 | 1180 | gated by :patch-triggers? and report-type ∈ #{:bug :request}. |
1161 | | - Returns true if anything was applied." |
1162 | | - [conn report-eid report-type email source-map roles delivery] |
1163 | | - (let [body-text (common/email-body-text email) |
1164 | | - db (d/db conn) |
1165 | | - from-addr (:email/author-address email) |
1166 | | - eid (:db/id email) |
1167 | | - report-mid (:report/message-id (d/entity db report-eid)) |
1168 | | - src-name (d/q '[:find ?src . :in $ ?rid |
1169 | | - :where [?rid :report/email ?e] [?e :email/source ?src]] db report-eid) |
1170 | | - source-cfg (when-let [cfg (get source-map src-name)] |
1171 | | - (periods/source-cfg-at-date cfg (:email/date-sent email))) |
1172 | | - src-cmds (build-source-commands source-cfg) |
1173 | | - overrides (:overrides src-cmds) |
1174 | | - is-maint? (common/maintainer? roles from-addr (:email/date-sent email)) |
1175 | | - fail-ctx (when (and from-addr src-name) |
1176 | | - {:source src-name |
1177 | | - :from-addr from-addr |
1178 | | - :email-date (:email/date-sent email) |
1179 | | - :report-mid report-mid}) |
1180 | | - body-words (when body-text |
1181 | | - (detect-words report-type body-text src-cmds)) |
1182 | | - ;; A reply shipping patch content credits its author on the parent |
1183 | | - ;; bug/request -- NOT on a parent patch report (a v2 reply would |
1184 | | - ;; otherwise spuriously mark v1 as owned by v2's author; super- |
1185 | | - ;; session handles patch->patch ownership transfer separately). |
1186 | | - ;; The reply-guard prevents a root [BUG]+.patch from self-crediting. |
1187 | | - implicit (when (and (contains? #{:bug :request} report-type) |
1188 | | - (common/patch-triggers? source-cfg) |
1189 | | - (:email/in-reply-to email) |
1190 | | - (detect/has-patch-content? email)) |
1191 | | - {:report/acked true :report/owned true}) |
1192 | | - ;; Acked is a second-party validation: the reporter cannot ack |
1193 | | - ;; their own report. Owned, by contrast, may stay with the |
1194 | | - ;; reporter ("I filed this and I'll fix it"). |
1195 | | - reporter (some-> (d/pull db [{:report/email [:email/author-address]}] |
1196 | | - report-eid) |
1197 | | - :report/email :email/author-address str/lower-case) |
1198 | | - self-ack? (and from-addr reporter (= (str/lower-case from-addr) reporter)) |
1199 | | - word-result (cond-> (merge implicit body-words) |
1200 | | - self-ack? (dissoc :report/acked) |
1201 | | - :always (filter-words-by-scope overrides is-maint? fail-ctx)) |
1202 | | - lines (when body-text |
1203 | | - (->> (detect-lines report-type body-text overrides |
1204 | | - (:email/date-sent email) |
1205 | | - (:line-patterns src-cmds)) |
1206 | | - (remove (fn [d] |
1207 | | - (and (= :set (:action d)) |
1208 | | - (= :report/acked (:attr d)) |
1209 | | - reporter |
1210 | | - (= (some-> (:email-address d) str/lower-case) |
1211 | | - reporter)))) |
1212 | | - vec)) |
1213 | | - closed? (some? (:report/closed (d/pull db [:report/closed] report-eid)))] |
| 1181 | + Returns true if anything was applied. |
| 1182 | +
|
| 1183 | + `line-filter` is one of: |
| 1184 | + nil -- process every command, no filtering. |
| 1185 | + :carrier-only -- process ONLY lines whose id is in |
| 1186 | + `carrier-eligible-ids`; skip words, votes, |
| 1187 | + and implicit ack/own. Used when this email |
| 1188 | + is a reply that also creates a new report: |
| 1189 | + Supersedes:/Related-to: in its body apply to |
| 1190 | + the new report unambiguously, the rest goes |
| 1191 | + to the thread parent via a separate call. |
| 1192 | + :no-carrier -- process everything EXCEPT carrier-eligible |
| 1193 | + lines. Used for the thread-parent call in the |
| 1194 | + same scenario, so carrier lines are not |
| 1195 | + double-applied." |
| 1196 | + [conn report-eid report-type email source-map roles delivery line-filter] |
| 1197 | + (let [carrier-only? (= :carrier-only line-filter) |
| 1198 | + no-carrier? (= :no-carrier line-filter) |
| 1199 | + body-text (common/email-body-text email) |
| 1200 | + db (d/db conn) |
| 1201 | + from-addr (:email/author-address email) |
| 1202 | + eid (:db/id email) |
| 1203 | + report-mid (:report/message-id (d/entity db report-eid)) |
| 1204 | + src-name (d/q '[:find ?src . :in $ ?rid |
| 1205 | + :where [?rid :report/email ?e] [?e :email/source ?src]] db report-eid) |
| 1206 | + source-cfg (when-let [cfg (get source-map src-name)] |
| 1207 | + (periods/source-cfg-at-date cfg (:email/date-sent email))) |
| 1208 | + src-cmds (build-source-commands source-cfg) |
| 1209 | + overrides (:overrides src-cmds) |
| 1210 | + is-maint? (common/maintainer? roles from-addr (:email/date-sent email)) |
| 1211 | + fail-ctx (when (and from-addr src-name) |
| 1212 | + {:source src-name |
| 1213 | + :from-addr from-addr |
| 1214 | + :email-date (:email/date-sent email) |
| 1215 | + :report-mid report-mid}) |
| 1216 | + ;; In :carrier-only mode we don't credit anyone on the brand- |
| 1217 | + ;; new report -- words, votes, implicit ack/own all go to the |
| 1218 | + ;; thread parent through the sibling :no-carrier call. |
| 1219 | + body-words (when (and body-text (not carrier-only?)) |
| 1220 | + (detect-words report-type body-text src-cmds)) |
| 1221 | + implicit (when (and (not carrier-only?) |
| 1222 | + (contains? #{:bug :request} report-type) |
| 1223 | + (common/patch-triggers? source-cfg) |
| 1224 | + (:email/in-reply-to email) |
| 1225 | + (detect/has-patch-content? email)) |
| 1226 | + {:report/acked true :report/owned true}) |
| 1227 | + reporter (some-> (d/pull db [{:report/email [:email/author-address]}] |
| 1228 | + report-eid) |
| 1229 | + :report/email :email/author-address str/lower-case) |
| 1230 | + self-ack? (and from-addr reporter (= (str/lower-case from-addr) reporter)) |
| 1231 | + word-result (cond-> (merge implicit body-words) |
| 1232 | + self-ack? (dissoc :report/acked) |
| 1233 | + :always (filter-words-by-scope overrides is-maint? fail-ctx)) |
| 1234 | + line-id-ok? (cond |
| 1235 | + carrier-only? carrier-eligible-ids |
| 1236 | + no-carrier? (complement carrier-eligible-ids) |
| 1237 | + :else (constantly true)) |
| 1238 | + lines (when body-text |
| 1239 | + (->> (detect-lines report-type body-text overrides |
| 1240 | + (:email/date-sent email) |
| 1241 | + (:line-patterns src-cmds)) |
| 1242 | + (filter #(line-id-ok? (:id %))) |
| 1243 | + (remove (fn [d] |
| 1244 | + (and (= :set (:action d)) |
| 1245 | + (= :report/acked (:attr d)) |
| 1246 | + reporter |
| 1247 | + (= (some-> (:email-address d) str/lower-case) |
| 1248 | + reporter)))) |
| 1249 | + vec)) |
| 1250 | + closed? (some? (:report/closed (d/pull db [:report/closed] report-eid)))] |
1214 | 1251 | (if closed? |
1215 | 1252 | (do (when (seq lines) |
1216 | 1253 | (try-unclosed! conn report-eid lines eid is-maint? from-addr fail-ctx)) |
1217 | 1254 | (boolean (seq lines))) |
1218 | | - (let [voted? (when-let [vote (and (= :request report-type) from-addr body-text |
1219 | | - (detect-vote body-text))] |
1220 | | - (apply-vote! conn report-eid from-addr vote email delivery source-cfg) |
1221 | | - true)] |
| 1255 | + (let [voted? (when (and (not carrier-only?) body-text) |
| 1256 | + (when-let [vote (and (= :request report-type) from-addr |
| 1257 | + (detect-vote body-text))] |
| 1258 | + (apply-vote! conn report-eid from-addr vote email delivery source-cfg) |
| 1259 | + true))] |
1222 | 1260 | (apply-words! conn report-eid word-result eid (:email/message-id email) from-addr |
1223 | 1261 | source-cfg) |
1224 | 1262 | (apply-lines! conn report-eid lines eid from-addr is-maint? fail-ctx) |
|
0 commit comments