Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make attending authors and primary attendees configurable #4584

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions anet-dictionary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,14 @@ fields:
label: Location
placeholder: Search for the engagement location…
filter: [POINT_LOCATION, VIRTUAL_LOCATION]
reportPeople:
optionalAttendingAuthor: false
optionalPrimaryAdvisor: false
optionalPrimaryPrincipal: true
attendeeGroups:
- label: Linguists
filter:
orgUuid: 70193ee9-05b4-4aac-80b5-75609825db9f
customFields:
relatedReport:
type: anet_object
Expand Down Expand Up @@ -508,10 +516,6 @@ fields:
typeError: Qty must be a number
label: Qty
visibleWhen: $[?(@ && @.multipleButtons && (@.multipleButtons.indexOf('assist') != -1 || @.multipleButtons.indexOf('other') != -1))]
attendeeGroups:
- label: Linguists
filter:
orgUuid: 70193ee9-05b4-4aac-80b5-75609825db9f

person:
status:
Expand Down
79 changes: 45 additions & 34 deletions client/src/models/Report.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,20 +162,10 @@ export default class Report extends Model {
.array()
.nullable()
.when("cancelled", ([cancelled], schema) =>
// Only do validation warning when engagement not cancelled
cancelled
? schema.nullable()
: schema // Only do validation warning when engagement not cancelled
.test(
"primary-advisor",
"primary advisor error",
(reportPeople, testContext) => {
const message = Report.checkPrimaryAttendee(
reportPeople,
Person.ROLE.ADVISOR
)
return message ? testContext.createError({ message }) : true
}
)
? schema
: Report.testPrimaryAttendees(schema, false)
.test(
"no-author",
"no author error",
Expand Down Expand Up @@ -281,20 +271,8 @@ export default class Report extends Model {
.array()
.nullable()
.when("cancelled", ([cancelled], schema) =>
cancelled
? schema.nullable()
: schema // Only do validation warning when engagement not cancelled
.test(
"primary-principal",
"primary principal error",
(reportPeople, testContext) => {
const message = Report.checkPrimaryAttendee(
reportPeople,
Person.ROLE.PRINCIPAL
)
return message ? testContext.createError({ message }) : true
}
)
// Only do validation warning when engagement not cancelled
cancelled ? schema : Report.testPrimaryAttendees(schema, true)
),
reportSensitiveInformation: yup.object().nullable().default({}),
authorizationGroups: yup
Expand All @@ -317,6 +295,36 @@ export default class Report extends Model {
)
})

static testPrimaryAttendees(schema, asWarning) {
return schema
.test(
"primary-advisor",
"primary advisor error",
(reportPeople, testContext) => {
const message = Report.checkPrimaryAttendee(
reportPeople,
Person.ROLE.ADVISOR,
Settings.fields.report.reportPeople?.optionalPrimaryAdvisor,
asWarning
)
return message ? testContext.createError({ message }) : true
}
)
.test(
"primary-principal",
"primary principal error",
(reportPeople, testContext) => {
const message = Report.checkPrimaryAttendee(
reportPeople,
Person.ROLE.PRINCIPAL,
Settings.fields.report.reportPeople?.optionalPrimaryPrincipal,
asWarning
)
return message ? testContext.createError({ message }) : true
}
)
}

static autocompleteQuery = "uuid, intent, authors { uuid, name, rank, role }"

constructor(props) {
Expand Down Expand Up @@ -404,13 +412,13 @@ export default class Report extends Model {
return this.intent || "None"
}

static checkPrimaryAttendee(reportPeople, role) {
static checkPrimaryAttendee(reportPeople, role, optional, asWarning) {
const primaryAttendee = Report.getPrimaryAttendee(reportPeople, role)
const roleName = Person.humanNameOfRole(role)
if (!primaryAttendee && role === Person.ROLE.ADVISOR) {
return `You must provide the primary ${roleName} for the Engagement`
} else if (!primaryAttendee && role === Person.ROLE.PRINCIPAL) {
return `No primary ${roleName} has been provided for the Engagement`
if (!primaryAttendee) {
if ((optional && asWarning) || (!optional && !asWarning)) {
return `No primary ${roleName} has been provided for the Engagement`
}
} else if (primaryAttendee.status !== Model.STATUS.ACTIVE) {
return `The primary ${roleName} - ${primaryAttendee.name} - needs to have an active profile`
} else if (
Expand All @@ -426,8 +434,11 @@ export default class Report extends Model {
}

static checkAttendingAuthor(reportPeople) {
if (!reportPeople?.some(rp => rp.author && rp.attendee)) {
return "You must provide at least 1 attending author"
const optionalAttendingAuthor =
Settings.fields.report.reportPeople?.optionalAttendingAuthor
const attendingAuthor = reportPeople?.some(rp => rp.author && rp.attendee)
if (!attendingAuthor && !optionalAttendingAuthor) {
return "You must provide at least 1 attending author(s)"
}
}

Expand Down
3 changes: 2 additions & 1 deletion client/src/pages/reports/Form.js
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,8 @@ const ReportForm = ({
}

// Add attendee groups defined in the dictionary
const attendeeGroups = Settings.fields.report.attendeeGroups ?? []
const attendeeGroups =
Settings.fields.report.reportPeople?.attendeeGroups ?? []
attendeeGroups.forEach(({ label, filter: queryVars }) => {
reportPeopleFilters[label] = { label, queryVars }
})
Expand Down
19 changes: 15 additions & 4 deletions src/main/java/mil/dds/anet/resources/ReportResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -398,15 +398,26 @@ public int submitReport(@GraphQLRootContext Map<String, Object> context,

if (r.getAdvisorOrgUuid() == null) {
final ReportPerson advisor = r.loadPrimaryAdvisor(engine.getContext()).join();
final Boolean optionalPrimaryAdvisor =
(Boolean) config.getDictionaryEntry("fields.report.reportPeople.optionalPrimaryAdvisor");
if (advisor == null) {
throw new WebApplicationException("Report missing primary advisor", Status.BAD_REQUEST);
if (!Boolean.TRUE.equals(optionalPrimaryAdvisor)) {
throw new WebApplicationException("Report missing primary advisor", Status.BAD_REQUEST);
}
} else {
r.setAdvisorOrg(
engine.getOrganizationForPerson(engine.getContext(), advisor.getUuid()).join());
}
r.setAdvisorOrg(
engine.getOrganizationForPerson(engine.getContext(), advisor.getUuid()).join());
}
if (r.getPrincipalOrgUuid() == null) {
final ReportPerson principal = r.loadPrimaryPrincipal(engine.getContext()).join();
if (principal != null) {
final Boolean optionalPrimaryPrincipal = (Boolean) config
.getDictionaryEntry("fields.report.reportPeople.optionalPrimaryPrincipal");
if (principal == null) {
if (!Boolean.TRUE.equals(optionalPrimaryPrincipal)) {
throw new WebApplicationException("Report missing primary principal", Status.BAD_REQUEST);
}
} else {
r.setPrincipalOrg(
engine.getOrganizationForPerson(engine.getContext(), principal.getUuid()).join());
}
Expand Down
53 changes: 34 additions & 19 deletions src/main/resources/anet-schema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -640,29 +640,44 @@ properties:
type: string
enum:
[VIRTUAL_LOCATION, PHYSICAL_LOCATION, GEOGRAPHICAL_AREA, POINT_LOCATION, ADVISOR_LOCATION, PRINCIPAL_LOCATION]
reportPeople:
required: [optionalAttendingAuthor, optionalPrimaryAdvisor, optionalPrimaryPrincipal]
properties:
optionalAttendingAuthor:
type: boolean
default: false
description: Defines if having an attending author for a report is optional
optionalPrimaryAdvisor:
type: boolean
default: false
description: Defines if having a primary advisor for a report is optional
optionalPrimaryPrincipal:
type: boolean
default: false
description: Defines if having a primary principal for a report is optional
attendeeGroups:
type: array
uniqueItems: true
items:
type: object
additionalProperties: false
title: Attendee-group
required: [label, filter]
properties:
label:
type: string
title: The label of the attendee group
filter:
type: object
title: Attendee group filters
properties:
orgUuid:
type: string
title: UUID of organisation membership to form
customFields:
type: object
additionalProperties:
"$ref": "#/$defs/customField"
attendeeGroups:
type: array
uniqueItems: true
items:
type: object
additionalProperties: false
title: Attendee-group
required: [label, filter]
properties:
label:
type: string
title: The label of the attendee group
filter:
type: object
title: Attendee group filters
properties:
orgUuid:
type: string
title: UUID of organisation membership to form

person:
type: object
Expand Down
12 changes: 8 additions & 4 deletions testDictionaries/no-custom-fields.yml
Original file line number Diff line number Diff line change
Expand Up @@ -363,10 +363,14 @@ fields:
label: Location
placeholder: Search for the engagement location…
filter: [POINT_LOCATION, VIRTUAL_LOCATION]
attendeeGroups:
- label: Linguists
filter:
orgUuid: 70193ee9-05b4-4aac-80b5-75609825db9f
reportPeople:
optionalAttendingAuthor: false
optionalPrimaryAdvisor: false
optionalPrimaryPrincipal: true
attendeeGroups:
- label: Linguists
filter:
orgUuid: 70193ee9-05b4-4aac-80b5-75609825db9f

person:
status:
Expand Down
Loading