Features: Return publishing notes for the Form and Versions API#1823
Features: Return publishing notes for the Form and Versions API#1823sadiqkhoja wants to merge 2 commits into
Conversation
d1d9ad0 to
466f7e4
Compare
I like |
| publishingNotes: | ||
| type: string | ||
| description: The notes provided when this version was published (via the `X-Action-Notes` header). Only returned to users with `form.update` permission. |
There was a problem hiding this comment.
How about adding an example, I like the string from above:
example: Fixed validation rules for required fields| description: The number of Public Links that can submit to the Form. This does not include Public Links that have been revoked. | ||
| publishingNotes: | ||
| type: string | ||
| description: The notes provided when this version was published (via the `X-Action-Notes` header). Only returned to users with `form.update` permission. |
There was a problem hiding this comment.
| description: The notes provided when this version was published (via the `X-Action-Notes` header). Only returned to users with `form.update` permission. | |
| description: The notes provided when the current Form version was published (via the `X-Action-Notes` header). Only returned to users with `form.update` permission. |
| ## ODK Central v2026.2 | ||
|
|
||
| **Changed**: | ||
| - The [Getting Form Details](/central-api-form-management/#getting-form-details), [Listing Published Form Versions](/central-api-form-management/#listing-published-form-versions) and [Getting Form Version Details](/central-api-form-management/#getting-form-version-details) endpoints now return a `publishingNotes` field (conditionally) in extended metadata. |
There was a problem hiding this comment.
I would either remove "(conditionally)" (it's explained below) or explain here what the condition is (maybe in a follow-up sentence). I think the sentence is slightly unclear as-is.
| const canReadForm = (auth, form) => { | ||
| const _checkActorPermission = (authOrVerbs, requiredVerbs, form) => { | ||
| if (authOrVerbs instanceof Array) { | ||
| const requiredVerbsArray = typeof requiredVerbs === 'string' ? [requiredVerbs] : requiredVerbs; |
There was a problem hiding this comment.
Could we require requiredVerbs to be an array? Since it's only called from canReadForm(), I feel like that'd be reasonable. I think there's enough going on in _checkActorPermission() that it'd be nice to simplify that aspect of it and remove this line. I know it's just one line though. 😅
| }; | ||
|
|
||
| const canReadForm = (auth, form) => { | ||
| const _checkActorPermission = (authOrVerbs, requiredVerbs, form) => { |
There was a problem hiding this comment.
| const _checkActorPermission = (authOrVerbs, requiredVerbs, form) => { | |
| const _checkActorVerbs = (authOrVerbs, requiredVerbs, form) => { |
Just thinking that we don't use the term "permission" very much in the codebase.
| }; | ||
|
|
||
| const canReadForm = (auth, form) => { | ||
| const _checkActorPermission = (authOrVerbs, requiredVerbs, form) => { |
There was a problem hiding this comment.
What do you think about always passing _checkActorPermission() verbs, not auth? It'd be easy for canReadForm() to convert auth to verbs. I feel like _checkActorPermission() is an important function, but it's slightly on the complicated side right now. Sometimes it calls auth.canOrReject(), while other times it reimplements some of the logic from auth.can().
I'm thinking of something like:
const checkActorVerbs = (actorVerbs, requiredVerbs) => {
const actorVerbsSet = new Set(actorVerbs);
if (requiredVerbs.every(v => !actorVerbsSet.has(v))) {
return reject(Problem.user.insufficientRights());
}
return true;
};
const canReadForm = async (authOrVerbs, form) => {
const verbs = authOrVerbs instanceof Array
? authOrVerbs
: (await auth.verbsOn(form));
if (form.def?.publishedAt == null) {
await checkActorVerbs(verbs, ['form.update']);
} else if (form.state === 'closed') {
await checkActorVerbs(verbs, ['form.read']);
} else {
await checkActorVerbs(verbs, ['open_form.read', 'form.read']);
}
return form;
};I can definitely see the reasoning behind _checkActorPermission(). It prevents a second call to auth.can() when checking whether the user can read publishingNotes. I'm just wondering whether there's a way to make the auth logic crystal clear.
| }))))); | ||
| }); | ||
|
|
||
| describe('publishing notes', () => { |
There was a problem hiding this comment.
This describe() block doesn't check public links, while the describe() block above does. Personally, I feel like that's OK. Authz is driven by verbs, not actor type.
There was a problem hiding this comment.
Love this scenarios file!
9f4b569 to
cf051a9
Compare
Towards getodk/central#1834
What has been done to verify that this works as intended?
Added test, checked with frontend.
Why is this the best possible solution? Were any other approaches considered?
It is small and simple change to achieve the objective. I thought about introducing symbol/attribute in our
Frameframework forrestrictedReadonlyfield but that would have been a complicated change. We may consider that approach if there is proliferation of conditional fields in our APIs.I added
publishingNotesfield for the/forms/:xmlFormIdas well because we need to show notes for the "Published" Form page as well.Is
publishingNotesa right name for this field? We can call this justnotes.How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?
Once related frontend PRs are merged, QA team should test all Form and Form version functionality.
Does this change require updates to the API documentation? If so, please update docs/api.yaml as part of this PR.
Added.
Before submitting this PR, please make sure you have:
make testand confirmed all checks still pass, or witnessed Github completing all checks with success