@anthusai/papyrus 2.0.0-next.3 → 2.0.0-next.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/amplify/data/resource.js +22 -3
- package/amplify/data/resource.ts +24 -3
- package/amplify/data/schema.js +2029 -1967
- package/amplify/data/schema.ts +72 -2
- package/amplify/functions/assignment-action/data-access.js +88 -0
- package/amplify/functions/assignment-action/data-access.ts +87 -0
- package/amplify/functions/content-actions/handler.py +33 -0
- package/amplify/functions/content-actions/requirements.txt +1 -0
- package/amplify/functions/content-actions/resource.js +1 -1
- package/amplify/functions/content-actions/resource.ts +1 -1
- package/amplify/functions/editorial-checks/handler.py +14 -0
- package/amplify/functions/editorial-checks/requirements.txt +1 -0
- package/amplify/functions/editorial-checks/resource.js +43 -0
- package/amplify/functions/editorial-checks/resource.ts +60 -0
- package/amplify/functions/editorial-checks/schema.js +121 -0
- package/amplify/functions/editorial-checks/schema.ts +123 -0
- package/amplify/functions/editorial-checks/wire.js +53 -0
- package/amplify/functions/editorial-checks/wire.ts +61 -0
- package/amplify/functions/monitoring-view/handler.py +11 -0
- package/amplify/functions/monitoring-view/resource.js +24 -0
- package/amplify/functions/monitoring-view/resource.ts +34 -0
- package/amplify/functions/monitoring-view/schema.js +16 -0
- package/amplify/functions/monitoring-view/schema.ts +18 -0
- package/amplify/functions/monitoring-view/wire.js +37 -0
- package/amplify/functions/monitoring-view/wire.ts +47 -0
- package/amplify/functions/public-forms/core.js +247 -0
- package/amplify/functions/public-forms/core.ts +310 -0
- package/amplify/functions/public-forms/handler.ts +41 -0
- package/amplify/functions/public-forms/origins.js +35 -0
- package/amplify/functions/public-forms/origins.ts +43 -0
- package/amplify/functions/public-forms/resource.js +9 -0
- package/amplify/functions/public-forms/resource.ts +10 -0
- package/amplify/functions/public-forms/wire.js +25 -0
- package/amplify/functions/public-forms/wire.ts +32 -0
- package/amplify/functions/shared/python-bundle.js +20 -14
- package/amplify/functions/shared/python-bundle.ts +25 -18
- package/amplify/publication/redirect-and-index.js +83 -0
- package/amplify/publication/redirect-rules.js +61 -0
- package/amplify/publication/redirect-rules.ts +79 -0
- package/amplify/publication/s3-projection.js +5 -1
- package/amplify/publication/s3-projection.ts +7 -1
- package/amplify/site-backend-config.ts +17 -0
- package/amplify/site-backend.js +36 -1
- package/amplify/site-backend.ts +44 -1
- package/app/api/newsroom/curation/context/route.ts +9 -0
- package/app/api/newsroom/curation/priming/route.ts +9 -0
- package/app/api/newsroom/curation/publication/route.ts +9 -0
- package/bin/papyrus-infra.mjs +20 -1
- package/components/editorial-checks-admin-panel.tsx +362 -0
- package/components/editorial-finding-card.tsx +162 -0
- package/components/editorial-findings-panel.tsx +91 -0
- package/components/editorial-preview-popover.tsx +103 -0
- package/components/markus-body-editor.tsx +213 -0
- package/components/markus-ir-preview.tsx +147 -6
- package/components/monitoring-overview-view.tsx +240 -0
- package/components/monitoring-panel.tsx +40 -0
- package/components/newsroom-article-editor.tsx +136 -11
- package/components/newsroom-board-view.tsx +6 -7
- package/components/newsroom-curation-bootstrap.tsx +45 -0
- package/components/newsroom-ops-shell.tsx +7 -3
- package/components/newsroom-organizer.css +74 -61
- package/components/newsroom-references-view.tsx +14 -14
- package/components/newsroom-theme-fonts/broadsheet-green.css +10 -0
- package/components/newsroom-theme-fonts/broadsheet-green.tsx +6 -0
- package/components/newsroom-theme-fonts/newspaper-gothic.css +9 -0
- package/components/newsroom-theme-fonts/newspaper-gothic.tsx +6 -0
- package/components/newsroom-theme-fonts/night-desk.css +10 -0
- package/components/newsroom-theme-fonts/night-desk.tsx +6 -0
- package/components/newsroom-theme-fonts/refined-newsprint.css +11 -0
- package/components/newsroom-theme-fonts/refined-newsprint.tsx +6 -0
- package/components/newsroom-theme.css +39 -0
- package/components/newsroom-theme.tsx +33 -0
- package/components/publication-cyclotron-panel.tsx +10 -10
- package/components/reference-add-entry.tsx +75 -0
- package/components/reference-curation-controls.tsx +30 -21
- package/components/reference-cyclotron-priming.tsx +4 -4
- package/components/reference-cyclotron-suggestion.tsx +1 -1
- package/components/reference-priming.tsx +14 -10
- package/components/reference-section-suggestion.tsx +1 -1
- package/components/topic-steering-workspace.tsx +27 -2
- package/components/use-editorial-checks.ts +133 -0
- package/lib/category-repository.ts +11 -9
- package/lib/curation-routes.ts +342 -0
- package/lib/editorial-checks-backend.ts +16 -0
- package/lib/editorial-checks-client.ts +212 -0
- package/lib/editorial-checks-demo.ts +147 -0
- package/lib/editorial-checks-settings.ts +27 -0
- package/lib/editorial-checks.ts +171 -0
- package/lib/editorial-preview.ts +228 -0
- package/lib/monitoring-client.ts +29 -0
- package/lib/monitoring-overview-fixture.ts +51 -0
- package/lib/monitoring-overview.ts +144 -0
- package/lib/newsroom-board.ts +27 -2
- package/lib/relevance-status.ts +2 -2
- package/lib/site-brand-tenant.ts +11 -6
- package/lib/site-brand.ts +23 -3
- package/package.json +47 -3
- package/routes.manifest.json +34 -1
- package/components/newsroom-fonts/playfair-display-700.ttf +0 -0
- package/components/newsroom-fonts/playfair-display-900.ttf +0 -0
- package/components/newsroom-fonts/playfair-display-OFL.txt +0 -93
- package/components/newsroom-fonts/plus-jakarta-sans-400.ttf +0 -0
- package/components/newsroom-fonts/plus-jakarta-sans-600.ttf +0 -0
- package/components/newsroom-fonts/plus-jakarta-sans-OFL.txt +0 -93
- package/components/newsroom-newsprint-board.css +0 -7
package/amplify/data/schema.ts
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import { type ClientSchema, a } from "@aws-amplify/backend";
|
|
2
2
|
import { assignmentAction } from "../functions/assignment-action/resource";
|
|
3
3
|
import { categoryAction } from "../functions/category-action/resource";
|
|
4
|
+
import { editorialChecksSchemaDefinitions } from "../functions/editorial-checks/schema";
|
|
5
|
+
import { monitoringViewSchemaDefinitions } from "../functions/monitoring-view/schema";
|
|
4
6
|
import { contentActions } from "../functions/content-actions/resource";
|
|
5
7
|
import { knowledgeQuery } from "../functions/knowledge-query/resource";
|
|
6
8
|
import { manageUserRole } from "../functions/manage-user-role/resource";
|
|
7
9
|
import { modelAttachmentUpload } from "../functions/model-attachment-upload/resource";
|
|
8
10
|
import { newsroomSummary } from "../functions/newsroom-summary/resource";
|
|
11
|
+
import { publicForms } from "../functions/public-forms/resource";
|
|
9
12
|
import { procedureAction } from "../functions/procedure-action/resource";
|
|
10
13
|
import { readerSettings } from "../functions/reader-settings/resource";
|
|
11
14
|
|
|
@@ -23,7 +26,27 @@ const modelWithoutSubscriptions = <Fields extends Parameters<typeof a.model>[0]>
|
|
|
23
26
|
a.model(fields).disableOperations(["subscriptions"]);
|
|
24
27
|
const realtimeModel = <Fields extends Parameters<typeof a.model>[0]>(fields: Fields) => a.model(fields);
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Model groups a site may leave out of its data API. CloudFormation creates at most 2,500 resources in one stack
|
|
31
|
+
* operation, nested stacks included, and the full model set exceeds that on a fresh create. A first deploy that
|
|
32
|
+
* excludes a group fits; a second deploy without the exclusion adds the models as an ordinary update.
|
|
33
|
+
*/
|
|
34
|
+
export const OPTIONAL_MODEL_GROUPS = {
|
|
35
|
+
editions: ["Edition", "EditionItem", "PublishedEdition", "PublishedEditionItem"],
|
|
36
|
+
} as const;
|
|
37
|
+
|
|
38
|
+
export type OptionalModelGroup = keyof typeof OPTIONAL_MODEL_GROUPS;
|
|
39
|
+
|
|
40
|
+
export function assertKnownModelGroups(groups: readonly string[]): asserts groups is readonly OptionalModelGroup[] {
|
|
41
|
+
for (const group of groups) {
|
|
42
|
+
if (!(group in OPTIONAL_MODEL_GROUPS)) {
|
|
43
|
+
throw new Error(`Unknown model group "${group}"; known groups: ${Object.keys(OPTIONAL_MODEL_GROUPS).join(", ")}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function buildBaseDefinitions(includeEditions: boolean) {
|
|
49
|
+
const definitions = {
|
|
27
50
|
UserProfile: modelWithoutSubscriptions({
|
|
28
51
|
id: a.id().required(),
|
|
29
52
|
email: a.email(),
|
|
@@ -542,6 +565,13 @@ const baseDefinitions = {
|
|
|
542
565
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
543
566
|
.handler(a.handler.function(contentActions)),
|
|
544
567
|
|
|
568
|
+
intakePendingReferences: a
|
|
569
|
+
.mutation()
|
|
570
|
+
.arguments({ input: a.json().required() })
|
|
571
|
+
.returns(a.json())
|
|
572
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
573
|
+
.handler(a.handler.function(contentActions)),
|
|
574
|
+
|
|
545
575
|
saveItemDraft: a
|
|
546
576
|
.mutation()
|
|
547
577
|
.arguments({ input: a.json().required() })
|
|
@@ -556,6 +586,13 @@ const baseDefinitions = {
|
|
|
556
586
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
557
587
|
.handler(a.handler.function(contentActions)),
|
|
558
588
|
|
|
589
|
+
moveStoryStage: a
|
|
590
|
+
.mutation()
|
|
591
|
+
.arguments({ input: a.json().required() })
|
|
592
|
+
.returns(a.json())
|
|
593
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
594
|
+
.handler(a.handler.function(contentActions)),
|
|
595
|
+
|
|
559
596
|
unpublishItem: a
|
|
560
597
|
.mutation()
|
|
561
598
|
.arguments({ input: a.json().required() })
|
|
@@ -1782,7 +1819,7 @@ const baseDefinitions = {
|
|
|
1782
1819
|
updatedAt: a.datetime(),
|
|
1783
1820
|
tags: a.hasMany("ItemTag", "itemId"),
|
|
1784
1821
|
mediaAssets: a.hasMany("MediaAsset", "itemId"),
|
|
1785
|
-
editionItems: a.hasMany("EditionItem", "itemId"),
|
|
1822
|
+
...(includeEditions ? { editionItems: a.hasMany("EditionItem", "itemId") } : {}),
|
|
1786
1823
|
})
|
|
1787
1824
|
.secondaryIndexes((index) => [
|
|
1788
1825
|
index("lineageId").sortKeys(["versionNumber"]).queryField("listItemsByLineageAndVersion"),
|
|
@@ -2105,26 +2142,59 @@ const baseDefinitions = {
|
|
|
2105
2142
|
allow.guest().to(["read"]),
|
|
2106
2143
|
allow.groups(contentWriteGroups),
|
|
2107
2144
|
]),
|
|
2145
|
+
...editorialChecksSchemaDefinitions,
|
|
2146
|
+
...monitoringViewSchemaDefinitions,
|
|
2108
2147
|
};
|
|
2148
|
+
if (!includeEditions) {
|
|
2149
|
+
const mutableDefinitions = definitions as Record<string, unknown>;
|
|
2150
|
+
for (const modelName of OPTIONAL_MODEL_GROUPS.editions) delete mutableDefinitions[modelName];
|
|
2151
|
+
}
|
|
2152
|
+
return definitions;
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
export function baseModelNames(excludedGroups: readonly OptionalModelGroup[] = []): string[] {
|
|
2156
|
+
return Object.keys(buildBaseDefinitions(!excludedGroups.includes("editions")));
|
|
2157
|
+
}
|
|
2158
|
+
|
|
2159
|
+
const baseDefinitions = buildBaseDefinitions(true);
|
|
2109
2160
|
|
|
2110
2161
|
export const schema = a.schema(baseDefinitions).authorization((allow) => [
|
|
2162
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2111
2163
|
allow.resource(categoryAction),
|
|
2112
2164
|
allow.resource(contentActions),
|
|
2113
2165
|
allow.resource(manageUserRole),
|
|
2114
2166
|
allow.resource(modelAttachmentUpload),
|
|
2115
2167
|
allow.resource(newsroomSummary),
|
|
2116
2168
|
allow.resource(procedureAction),
|
|
2169
|
+
allow.resource(publicForms).to(["mutate"]),
|
|
2117
2170
|
]);
|
|
2118
2171
|
|
|
2119
2172
|
/** The deployed schema of a site that enables plugins: the base definitions plus what each plugin contributes (docs/plugins.md). */
|
|
2120
2173
|
export function schemaWithPlugins(pluginDefinitions: Record<string, any>) {
|
|
2121
2174
|
return a.schema({ ...baseDefinitions, ...pluginDefinitions } as typeof baseDefinitions).authorization((allow) => [
|
|
2175
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2176
|
+
allow.resource(categoryAction),
|
|
2177
|
+
allow.resource(contentActions),
|
|
2178
|
+
allow.resource(manageUserRole),
|
|
2179
|
+
allow.resource(modelAttachmentUpload),
|
|
2180
|
+
allow.resource(newsroomSummary),
|
|
2181
|
+
allow.resource(procedureAction),
|
|
2182
|
+
]);
|
|
2183
|
+
}
|
|
2184
|
+
|
|
2185
|
+
/** The schema of a site that leaves optional model groups out of its first deploy. */
|
|
2186
|
+
export function schemaExcludingGroups(excludedGroups: readonly OptionalModelGroup[], pluginDefinitions: Record<string, any> = {}) {
|
|
2187
|
+
const definitions = buildBaseDefinitions(!excludedGroups.includes("editions")) as unknown as typeof baseDefinitions;
|
|
2188
|
+
const hasPlugins = Object.keys(pluginDefinitions).length > 0;
|
|
2189
|
+
return a.schema({ ...definitions, ...pluginDefinitions } as typeof baseDefinitions).authorization((allow) => [
|
|
2190
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2122
2191
|
allow.resource(categoryAction),
|
|
2123
2192
|
allow.resource(contentActions),
|
|
2124
2193
|
allow.resource(manageUserRole),
|
|
2125
2194
|
allow.resource(modelAttachmentUpload),
|
|
2126
2195
|
allow.resource(newsroomSummary),
|
|
2127
2196
|
allow.resource(procedureAction),
|
|
2197
|
+
...(hasPlugins ? [] : [allow.resource(publicForms).to(["mutate"])]),
|
|
2128
2198
|
]);
|
|
2129
2199
|
}
|
|
2130
2200
|
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Aspects, Stack } from "aws-cdk-lib";
|
|
2
|
+
import { CfnPolicy } from "aws-cdk-lib/aws-iam";
|
|
3
|
+
/** Lifecycle/context calls, including the fixed reference registration CLI's provenance operations. */
|
|
4
|
+
export const ASSIGNMENT_DATA_FIELDS = [
|
|
5
|
+
"Query/fields/getAssignment",
|
|
6
|
+
"Query/fields/getKnowledgeRawPayload",
|
|
7
|
+
"Query/fields/getProcedureRun",
|
|
8
|
+
"Query/fields/getProcedureVersion",
|
|
9
|
+
"Query/fields/getProcedureDefinition",
|
|
10
|
+
"Query/fields/getModelAttachment",
|
|
11
|
+
"Query/fields/getNewsroomSection",
|
|
12
|
+
"Query/fields/getKnowledgeCorpus",
|
|
13
|
+
"Query/fields/getKnowledgeImportRun",
|
|
14
|
+
"Query/fields/getReference",
|
|
15
|
+
"Query/fields/getReferenceAttachment",
|
|
16
|
+
"Query/fields/getMessage",
|
|
17
|
+
"Query/fields/getSemanticRelation",
|
|
18
|
+
"Query/fields/getSemanticNode",
|
|
19
|
+
"Query/fields/listReferences",
|
|
20
|
+
"Query/fields/listReferenceAttachments",
|
|
21
|
+
"Query/fields/listSemanticRelations",
|
|
22
|
+
"Query/fields/listKnowledgeCorporaByRoleAndName",
|
|
23
|
+
"Query/fields/listItemsByLineageAndVersion",
|
|
24
|
+
"Query/fields/itemBySlug",
|
|
25
|
+
"Query/fields/itemByTenantSlug",
|
|
26
|
+
"Query/fields/listModelAttachmentsByOwnerRoleAndSortKey",
|
|
27
|
+
"Query/fields/listAssignmentsByQueueStatusAndPriority",
|
|
28
|
+
"Query/fields/listSemanticRelationsByObjectState",
|
|
29
|
+
"Query/fields/listSemanticRelationsBySubjectState",
|
|
30
|
+
"Query/fields/listAssignmentEventsByAssignmentAndCreatedAt",
|
|
31
|
+
"Mutation/fields/createAssignment",
|
|
32
|
+
"Mutation/fields/updateAssignment",
|
|
33
|
+
"Mutation/fields/createAssignmentEvent",
|
|
34
|
+
"Mutation/fields/createProcedureRun",
|
|
35
|
+
"Mutation/fields/updateProcedureRun",
|
|
36
|
+
"Mutation/fields/createKnowledgeCorpus",
|
|
37
|
+
"Mutation/fields/updateKnowledgeCorpus",
|
|
38
|
+
"Mutation/fields/createKnowledgeImportRun",
|
|
39
|
+
"Mutation/fields/updateKnowledgeImportRun",
|
|
40
|
+
"Mutation/fields/createKnowledgeRawPayload",
|
|
41
|
+
"Mutation/fields/updateKnowledgeRawPayload",
|
|
42
|
+
"Mutation/fields/createReference",
|
|
43
|
+
"Mutation/fields/updateReference",
|
|
44
|
+
"Mutation/fields/createReferenceAttachment",
|
|
45
|
+
"Mutation/fields/updateReferenceAttachment",
|
|
46
|
+
"Mutation/fields/createMessage",
|
|
47
|
+
"Mutation/fields/updateMessage",
|
|
48
|
+
"Mutation/fields/createSemanticRelation",
|
|
49
|
+
"Mutation/fields/updateSemanticRelation",
|
|
50
|
+
"Mutation/fields/createSemanticNode",
|
|
51
|
+
"Mutation/fields/updateSemanticNode",
|
|
52
|
+
"Mutation/fields/createModelAttachment",
|
|
53
|
+
"Mutation/fields/updateModelAttachment",
|
|
54
|
+
"Mutation/fields/createModelAttachmentUpload",
|
|
55
|
+
"Mutation/fields/completeModelAttachmentUpload",
|
|
56
|
+
"Mutation/fields/createModelAttachmentDownload",
|
|
57
|
+
"Mutation/fields/updateNewsroomSummary",
|
|
58
|
+
];
|
|
59
|
+
/** Keep the SDK's generated configuration, replacing only this role's GraphQL grants at synthesis. */
|
|
60
|
+
export function scopeAssignmentDataAccess(lambda, graphqlApiArn) {
|
|
61
|
+
if (!lambda.role)
|
|
62
|
+
throw new Error("Assignment function needs an execution role.");
|
|
63
|
+
const roleName = lambda.role.roleName;
|
|
64
|
+
const resources = ASSIGNMENT_DATA_FIELDS.map((field) => `${graphqlApiArn}/types/${field}`);
|
|
65
|
+
Aspects.of(Stack.of(lambda)).add({
|
|
66
|
+
visit(node) {
|
|
67
|
+
if (!(node instanceof CfnPolicy))
|
|
68
|
+
return;
|
|
69
|
+
const stack = Stack.of(node);
|
|
70
|
+
const roles = stack.resolve(node.roles);
|
|
71
|
+
if (!roles?.some((role) => JSON.stringify(role) === JSON.stringify(stack.resolve(roleName))))
|
|
72
|
+
return;
|
|
73
|
+
const document = stack.resolve(node.policyDocument);
|
|
74
|
+
document.Statement.forEach((statement, index) => {
|
|
75
|
+
const actions = Array.isArray(statement.Action) ? statement.Action : [statement.Action];
|
|
76
|
+
if (actions.includes("*") || actions.includes("appsync:*")) {
|
|
77
|
+
throw new Error("Unexpected wildcard assignment action; review permissions before synthesis.");
|
|
78
|
+
}
|
|
79
|
+
if (!actions.includes("appsync:GraphQL"))
|
|
80
|
+
return;
|
|
81
|
+
if (statement.Effect !== "Allow" || actions.length !== 1 || statement.NotResource) {
|
|
82
|
+
throw new Error("Unexpected assignment GraphQL policy; review field scoping before synthesis.");
|
|
83
|
+
}
|
|
84
|
+
node.addPropertyOverride(`PolicyDocument.Statement.${index}.Resource`, resources);
|
|
85
|
+
});
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Aspects, Stack } from "aws-cdk-lib";
|
|
2
|
+
import { CfnPolicy } from "aws-cdk-lib/aws-iam";
|
|
3
|
+
import type { IFunction } from "aws-cdk-lib/aws-lambda";
|
|
4
|
+
|
|
5
|
+
/** Lifecycle/context calls, including the fixed reference registration CLI's provenance operations. */
|
|
6
|
+
export const ASSIGNMENT_DATA_FIELDS = [
|
|
7
|
+
"Query/fields/getAssignment",
|
|
8
|
+
"Query/fields/getKnowledgeRawPayload",
|
|
9
|
+
"Query/fields/getProcedureRun",
|
|
10
|
+
"Query/fields/getProcedureVersion",
|
|
11
|
+
"Query/fields/getProcedureDefinition",
|
|
12
|
+
"Query/fields/getModelAttachment",
|
|
13
|
+
"Query/fields/getNewsroomSection",
|
|
14
|
+
"Query/fields/getKnowledgeCorpus",
|
|
15
|
+
"Query/fields/getKnowledgeImportRun",
|
|
16
|
+
"Query/fields/getReference",
|
|
17
|
+
"Query/fields/getReferenceAttachment",
|
|
18
|
+
"Query/fields/getMessage",
|
|
19
|
+
"Query/fields/getSemanticRelation",
|
|
20
|
+
"Query/fields/getSemanticNode",
|
|
21
|
+
"Query/fields/listReferences",
|
|
22
|
+
"Query/fields/listReferenceAttachments",
|
|
23
|
+
"Query/fields/listSemanticRelations",
|
|
24
|
+
"Query/fields/listKnowledgeCorporaByRoleAndName",
|
|
25
|
+
"Query/fields/listItemsByLineageAndVersion",
|
|
26
|
+
"Query/fields/itemBySlug",
|
|
27
|
+
"Query/fields/itemByTenantSlug",
|
|
28
|
+
"Query/fields/listModelAttachmentsByOwnerRoleAndSortKey",
|
|
29
|
+
"Query/fields/listAssignmentsByQueueStatusAndPriority",
|
|
30
|
+
"Query/fields/listSemanticRelationsByObjectState",
|
|
31
|
+
"Query/fields/listSemanticRelationsBySubjectState",
|
|
32
|
+
"Query/fields/listAssignmentEventsByAssignmentAndCreatedAt",
|
|
33
|
+
"Mutation/fields/createAssignment",
|
|
34
|
+
"Mutation/fields/updateAssignment",
|
|
35
|
+
"Mutation/fields/createAssignmentEvent",
|
|
36
|
+
"Mutation/fields/createProcedureRun",
|
|
37
|
+
"Mutation/fields/updateProcedureRun",
|
|
38
|
+
"Mutation/fields/createKnowledgeCorpus",
|
|
39
|
+
"Mutation/fields/updateKnowledgeCorpus",
|
|
40
|
+
"Mutation/fields/createKnowledgeImportRun",
|
|
41
|
+
"Mutation/fields/updateKnowledgeImportRun",
|
|
42
|
+
"Mutation/fields/createKnowledgeRawPayload",
|
|
43
|
+
"Mutation/fields/updateKnowledgeRawPayload",
|
|
44
|
+
"Mutation/fields/createReference",
|
|
45
|
+
"Mutation/fields/updateReference",
|
|
46
|
+
"Mutation/fields/createReferenceAttachment",
|
|
47
|
+
"Mutation/fields/updateReferenceAttachment",
|
|
48
|
+
"Mutation/fields/createMessage",
|
|
49
|
+
"Mutation/fields/updateMessage",
|
|
50
|
+
"Mutation/fields/createSemanticRelation",
|
|
51
|
+
"Mutation/fields/updateSemanticRelation",
|
|
52
|
+
"Mutation/fields/createSemanticNode",
|
|
53
|
+
"Mutation/fields/updateSemanticNode",
|
|
54
|
+
"Mutation/fields/createModelAttachment",
|
|
55
|
+
"Mutation/fields/updateModelAttachment",
|
|
56
|
+
"Mutation/fields/createModelAttachmentUpload",
|
|
57
|
+
"Mutation/fields/completeModelAttachmentUpload",
|
|
58
|
+
"Mutation/fields/createModelAttachmentDownload",
|
|
59
|
+
"Mutation/fields/updateNewsroomSummary",
|
|
60
|
+
] as const;
|
|
61
|
+
|
|
62
|
+
/** Keep the SDK's generated configuration, replacing only this role's GraphQL grants at synthesis. */
|
|
63
|
+
export function scopeAssignmentDataAccess(lambda: IFunction, graphqlApiArn: string): void {
|
|
64
|
+
if (!lambda.role) throw new Error("Assignment function needs an execution role.");
|
|
65
|
+
const roleName = lambda.role.roleName;
|
|
66
|
+
const resources = ASSIGNMENT_DATA_FIELDS.map((field) => `${graphqlApiArn}/types/${field}`);
|
|
67
|
+
Aspects.of(Stack.of(lambda)).add({
|
|
68
|
+
visit(node) {
|
|
69
|
+
if (!(node instanceof CfnPolicy)) return;
|
|
70
|
+
const stack = Stack.of(node);
|
|
71
|
+
const roles = stack.resolve(node.roles) as unknown[] | undefined;
|
|
72
|
+
if (!roles?.some((role) => JSON.stringify(role) === JSON.stringify(stack.resolve(roleName)))) return;
|
|
73
|
+
const document = stack.resolve(node.policyDocument) as { Statement: Array<Record<string, unknown>> };
|
|
74
|
+
document.Statement.forEach((statement, index) => {
|
|
75
|
+
const actions = Array.isArray(statement.Action) ? statement.Action : [statement.Action];
|
|
76
|
+
if (actions.includes("*") || actions.includes("appsync:*")) {
|
|
77
|
+
throw new Error("Unexpected wildcard assignment action; review permissions before synthesis.");
|
|
78
|
+
}
|
|
79
|
+
if (!actions.includes("appsync:GraphQL")) return;
|
|
80
|
+
if (statement.Effect !== "Allow" || actions.length !== 1 || statement.NotResource) {
|
|
81
|
+
throw new Error("Unexpected assignment GraphQL policy; review field scoping before synthesis.");
|
|
82
|
+
}
|
|
83
|
+
node.addPropertyOverride(`PolicyDocument.Statement.${index}.Resource`, resources);
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
@@ -23,6 +23,9 @@ from papyrus_content.rebuild_trigger import (
|
|
|
23
23
|
trigger_staging_build,
|
|
24
24
|
)
|
|
25
25
|
from papyrus_content.story_reporting import save_story_version
|
|
26
|
+
from papyrus_content.publication import ensure_publication_from_environment
|
|
27
|
+
from papyrus_content.editorial_checks import GraphQLEditorialStore, ensure_default_policies
|
|
28
|
+
from papyrus_content.reference_intake import IntakeError, intake_references
|
|
26
29
|
from papyrus_content.publishing import (
|
|
27
30
|
ItemFields,
|
|
28
31
|
PublishError,
|
|
@@ -148,8 +151,19 @@ def schedule_unpurged_invalidation(keys: list[str]) -> None:
|
|
|
148
151
|
schedule_projection_task({"task": "invalidate", "keys": keys}, "edge invalidation failed")
|
|
149
152
|
|
|
150
153
|
|
|
154
|
+
def ensure_default_editorial_policies(client) -> None:
|
|
155
|
+
"""A deployed site starts with working editorial policies. Never fails the deploy: a problem is logged."""
|
|
156
|
+
try:
|
|
157
|
+
result = ensure_default_policies(GraphQLEditorialStore(execute=client.graphql))
|
|
158
|
+
print(json.dumps({"defaultEditorialPolicies": result}))
|
|
159
|
+
except Exception as error:
|
|
160
|
+
print(json.dumps({"defaultEditorialPoliciesError": str(error)[:500]}))
|
|
161
|
+
|
|
162
|
+
|
|
151
163
|
def run_deploy_render_task(client) -> dict:
|
|
152
164
|
"""Render every published page after a backend deploy, then report drift (a dry run: nothing is deleted)."""
|
|
165
|
+
ensure_publication_from_environment(client)
|
|
166
|
+
ensure_default_editorial_policies(client)
|
|
153
167
|
context = projection_context(client)
|
|
154
168
|
report = deploy_render(context)
|
|
155
169
|
print(drift_metric_line(report))
|
|
@@ -283,6 +297,17 @@ def unpublish_item_action(arguments: dict, actor: str) -> dict:
|
|
|
283
297
|
return response
|
|
284
298
|
|
|
285
299
|
|
|
300
|
+
def move_story_stage_action(arguments: dict, actor: str) -> dict:
|
|
301
|
+
from papyrus_content.story_reporting import move_story_stage
|
|
302
|
+
|
|
303
|
+
client, _claims = create_authoring_client()
|
|
304
|
+
try:
|
|
305
|
+
result = move_story_stage(client, arguments["lineageId"], arguments["stage"], actor=actor, reason=arguments.get("reason"))
|
|
306
|
+
except ValueError as error:
|
|
307
|
+
return {"ok": False, "errors": [{"code": "invalid-stage", "message": str(error), "line": None}]}
|
|
308
|
+
return {"ok": True, **result}
|
|
309
|
+
|
|
310
|
+
|
|
286
311
|
def request_staging_build_action() -> dict:
|
|
287
312
|
cms_app_id = (os.environ.get("PAPYRUS_CMS_AMPLIFY_APP_ID") or "").strip()
|
|
288
313
|
if not cms_app_id:
|
|
@@ -299,12 +324,20 @@ def handler(event, context):
|
|
|
299
324
|
arguments = parse_input(event["arguments"]["input"])
|
|
300
325
|
actor = actor_from_event(event)
|
|
301
326
|
try:
|
|
327
|
+
if field == "intakePendingReferences":
|
|
328
|
+
client, _claims = create_authoring_client()
|
|
329
|
+
try:
|
|
330
|
+
return intake_references(client, arguments, event.get("identity") or {})
|
|
331
|
+
except IntakeError as error:
|
|
332
|
+
return {"ok": False, "errors": [{"code": "intake-rejected", "message": str(error)}]}
|
|
302
333
|
if field == "deriveMarkus":
|
|
303
334
|
return derive_markus_action(arguments)
|
|
304
335
|
if field == "saveItemDraft":
|
|
305
336
|
return save_item_draft_action(arguments, actor)
|
|
306
337
|
if field == "publishItem":
|
|
307
338
|
return publish_item_action(arguments, actor)
|
|
339
|
+
if field == "moveStoryStage":
|
|
340
|
+
return move_story_stage_action(arguments, actor)
|
|
308
341
|
if field == "unpublishItem":
|
|
309
342
|
return unpublish_item_action(arguments, actor)
|
|
310
343
|
if field == "requestStagingBuild":
|
|
@@ -7,7 +7,7 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
import { pythonLambdaCode } from "../shared/python-bundle.js";
|
|
8
8
|
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
9
|
const projectRoot = path.resolve(dirname, "../../..");
|
|
10
|
-
export const contentActionsRequirements = ["PyYAML>=6.0.2,<7", "citeproc-py>=0.8.2,<1", "anthus-markus==0.7.0", "Pillow>=11.3,<12", "awscrt>=0.23,<1"];
|
|
10
|
+
export const contentActionsRequirements = ["PyYAML>=6.0.2,<7", "citeproc-py>=0.8.2,<1", "anthus-markus==0.7.0", "Pillow>=11.3,<12", "awscrt>=0.23,<1", "humanus>=0.33,<0.34"];
|
|
11
11
|
function installCheckoutRequirements(outputDir) {
|
|
12
12
|
const requirementsPath = path.join(projectRoot, "amplify/functions/content-actions/requirements.txt");
|
|
13
13
|
execSync(`python3 -m pip install -r "${requirementsPath}" -t "${outputDir}" --no-cache-dir --platform manylinux2014_aarch64 --implementation cp --python-version 3.12 --only-binary=:all: --ignore-requires-python`, { cwd: projectRoot, stdio: "inherit" });
|
|
@@ -10,7 +10,7 @@ import { pythonLambdaCode, type SiteBundleFile } from "../shared/python-bundle";
|
|
|
10
10
|
const dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
11
11
|
const projectRoot = path.resolve(dirname, "../../..");
|
|
12
12
|
|
|
13
|
-
export const contentActionsRequirements = ["PyYAML>=6.0.2,<7", "citeproc-py>=0.8.2,<1", "anthus-markus==0.7.0", "Pillow>=11.3,<12", "awscrt>=0.23,<1"];
|
|
13
|
+
export const contentActionsRequirements = ["PyYAML>=6.0.2,<7", "citeproc-py>=0.8.2,<1", "anthus-markus==0.7.0", "Pillow>=11.3,<12", "awscrt>=0.23,<1", "humanus>=0.33,<0.34"];
|
|
14
14
|
|
|
15
15
|
function installCheckoutRequirements(outputDir: string): void {
|
|
16
16
|
const requirementsPath = path.join(projectRoot, "amplify/functions/content-actions/requirements.txt");
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""The editorial-checks function: routes its GraphQL fields to papyrus_content.editorial_checks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from papyrus_content.editorial_checks import GraphQLEditorialStore, Services, handle, load_provider_keys
|
|
8
|
+
|
|
9
|
+
# Amplify passes secrets to Python functions as placeholders; resolve them once per cold start.
|
|
10
|
+
load_provider_keys()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def handler(event: dict[str, Any], _context: Any = None) -> dict[str, Any]:
|
|
14
|
+
return handle(event["info"]["fieldName"], event.get("arguments") or {}, Services(store=GraphQLEditorialStore()))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
humanus[jev]>=0.33,<0.34
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { defineFunction } from "@aws-amplify/backend";
|
|
2
|
+
import { Duration } from "aws-cdk-lib";
|
|
3
|
+
import { Architecture, Function, Runtime } from "aws-cdk-lib/aws-lambda";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { pythonLambdaCode } from "../shared/python-bundle.js";
|
|
8
|
+
const functionDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
/** AppSync gives a resolver 30 seconds; Humanus's classifier deadline (settings, default 20 s) keeps a check inside this. */
|
|
10
|
+
export const EDITORIAL_CHECKS_TIMEOUT_SECONDS = 28;
|
|
11
|
+
/** Humanus with the Jev classifier lane; papyrus_content.editorial_checks does the work. */
|
|
12
|
+
export const editorialChecksRequirements = ["humanus[jev]>=0.33,<0.34"];
|
|
13
|
+
function installRequirements(outputDir) {
|
|
14
|
+
execFileSync("python3", [
|
|
15
|
+
"-m", "pip", "install", "-r", path.join(functionDirectory, "requirements.txt"), "-t", outputDir, "--no-cache-dir",
|
|
16
|
+
"--platform", "manylinux2014_aarch64", "--implementation", "cp", "--python-version", "3.12",
|
|
17
|
+
"--only-binary=:all:", "--ignore-requires-python",
|
|
18
|
+
], { stdio: "inherit" });
|
|
19
|
+
}
|
|
20
|
+
export const editorialChecks = defineFunction((scope) => new Function(scope, "papyrus-editorial-checks", {
|
|
21
|
+
runtime: Runtime.PYTHON_3_12,
|
|
22
|
+
architecture: Architecture.ARM_64,
|
|
23
|
+
handler: "handler.handler",
|
|
24
|
+
timeout: Duration.seconds(EDITORIAL_CHECKS_TIMEOUT_SECONDS),
|
|
25
|
+
memorySize: 1024,
|
|
26
|
+
code: pythonLambdaCode({
|
|
27
|
+
functionDir: "editorial-checks",
|
|
28
|
+
modules: ["papyrus_content"],
|
|
29
|
+
requirements: editorialChecksRequirements,
|
|
30
|
+
noDeps: true,
|
|
31
|
+
checkoutExtras: installRequirements,
|
|
32
|
+
}, [
|
|
33
|
+
"bash",
|
|
34
|
+
"-c",
|
|
35
|
+
[
|
|
36
|
+
"set -euo pipefail",
|
|
37
|
+
"mkdir -p /asset-output/papyrus_content",
|
|
38
|
+
"cp amplify/functions/editorial-checks/handler.py /asset-output/handler.py",
|
|
39
|
+
"cp -R src/papyrus_content/. /asset-output/papyrus_content/",
|
|
40
|
+
"python3 -m pip install -r amplify/functions/editorial-checks/requirements.txt -t /asset-output --no-cache-dir",
|
|
41
|
+
].join(" && "),
|
|
42
|
+
]),
|
|
43
|
+
}), { resourceGroupName: "data" });
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { defineFunction } from "@aws-amplify/backend";
|
|
2
|
+
import { Duration } from "aws-cdk-lib";
|
|
3
|
+
import { Architecture, Function, Runtime } from "aws-cdk-lib/aws-lambda";
|
|
4
|
+
import type { Construct } from "constructs";
|
|
5
|
+
import { execFileSync } from "node:child_process";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { pythonLambdaCode } from "../shared/python-bundle";
|
|
9
|
+
|
|
10
|
+
const functionDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
|
|
12
|
+
/** AppSync gives a resolver 30 seconds; Humanus's classifier deadline (settings, default 20 s) keeps a check inside this. */
|
|
13
|
+
export const EDITORIAL_CHECKS_TIMEOUT_SECONDS = 28;
|
|
14
|
+
|
|
15
|
+
/** Humanus with the Jev classifier lane; papyrus_content.editorial_checks does the work. */
|
|
16
|
+
export const editorialChecksRequirements = ["humanus[jev]>=0.33,<0.34"];
|
|
17
|
+
|
|
18
|
+
function installRequirements(outputDir: string): void {
|
|
19
|
+
execFileSync(
|
|
20
|
+
"python3",
|
|
21
|
+
[
|
|
22
|
+
"-m", "pip", "install", "-r", path.join(functionDirectory, "requirements.txt"), "-t", outputDir, "--no-cache-dir",
|
|
23
|
+
"--platform", "manylinux2014_aarch64", "--implementation", "cp", "--python-version", "3.12",
|
|
24
|
+
"--only-binary=:all:", "--ignore-requires-python",
|
|
25
|
+
],
|
|
26
|
+
{ stdio: "inherit" },
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const editorialChecks = defineFunction(
|
|
31
|
+
(scope: Construct) =>
|
|
32
|
+
new Function(scope, "papyrus-editorial-checks", {
|
|
33
|
+
runtime: Runtime.PYTHON_3_12,
|
|
34
|
+
architecture: Architecture.ARM_64,
|
|
35
|
+
handler: "handler.handler",
|
|
36
|
+
timeout: Duration.seconds(EDITORIAL_CHECKS_TIMEOUT_SECONDS),
|
|
37
|
+
memorySize: 1024,
|
|
38
|
+
code: pythonLambdaCode(
|
|
39
|
+
{
|
|
40
|
+
functionDir: "editorial-checks",
|
|
41
|
+
modules: ["papyrus_content"],
|
|
42
|
+
requirements: editorialChecksRequirements,
|
|
43
|
+
noDeps: true,
|
|
44
|
+
checkoutExtras: installRequirements,
|
|
45
|
+
},
|
|
46
|
+
[
|
|
47
|
+
"bash",
|
|
48
|
+
"-c",
|
|
49
|
+
[
|
|
50
|
+
"set -euo pipefail",
|
|
51
|
+
"mkdir -p /asset-output/papyrus_content",
|
|
52
|
+
"cp amplify/functions/editorial-checks/handler.py /asset-output/handler.py",
|
|
53
|
+
"cp -R src/papyrus_content/. /asset-output/papyrus_content/",
|
|
54
|
+
"python3 -m pip install -r amplify/functions/editorial-checks/requirements.txt -t /asset-output --no-cache-dir",
|
|
55
|
+
].join(" && "),
|
|
56
|
+
],
|
|
57
|
+
),
|
|
58
|
+
}),
|
|
59
|
+
{ resourceGroupName: "data" },
|
|
60
|
+
);
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { a } from "@aws-amplify/backend";
|
|
2
|
+
import { editorialChecks } from "./resource.js";
|
|
3
|
+
const editors = ["editor", "admin"];
|
|
4
|
+
/**
|
|
5
|
+
* Core to every site's data API. Policies, settings and editors' decisions live in Papyrus; Humanus checks
|
|
6
|
+
* content against them. Editors read policies and record decisions; admins
|
|
7
|
+
* manage policies and settings. Only the function (IAM) calls Humanus.
|
|
8
|
+
*/
|
|
9
|
+
export const editorialChecksSchemaDefinitions = {
|
|
10
|
+
EditorialPolicy: a
|
|
11
|
+
.model({
|
|
12
|
+
id: a.id().required(),
|
|
13
|
+
policyKey: a.string().required(),
|
|
14
|
+
scope: a.string().required(),
|
|
15
|
+
version: a.integer().required(),
|
|
16
|
+
title: a.string().required(),
|
|
17
|
+
guidance: a.string().required(),
|
|
18
|
+
severity: a.string().required(),
|
|
19
|
+
enabled: a.boolean().required(),
|
|
20
|
+
detectors: a.json().required(),
|
|
21
|
+
examples: a.json(),
|
|
22
|
+
rewriteGuidance: a.string(),
|
|
23
|
+
source: a.string().required(),
|
|
24
|
+
})
|
|
25
|
+
.disableOperations(["subscriptions"])
|
|
26
|
+
.authorization((allow) => [allow.group("editor").to(["read"]), allow.group("admin")]),
|
|
27
|
+
EditorialCheckSettings: a
|
|
28
|
+
.model({
|
|
29
|
+
id: a.id().required(),
|
|
30
|
+
settings: a.json().required(),
|
|
31
|
+
})
|
|
32
|
+
.disableOperations(["subscriptions"])
|
|
33
|
+
.authorization((allow) => [allow.group("editor").to(["read"]), allow.group("admin")]),
|
|
34
|
+
EditorialFeedback: a
|
|
35
|
+
.model({
|
|
36
|
+
id: a.id().required(),
|
|
37
|
+
itemId: a.string().required(),
|
|
38
|
+
findingId: a.string().required(),
|
|
39
|
+
policyId: a.string().required(),
|
|
40
|
+
detectorId: a.string().required(),
|
|
41
|
+
lane: a.string().required(),
|
|
42
|
+
probability: a.float(),
|
|
43
|
+
decision: a.string().required(),
|
|
44
|
+
reason: a.string(),
|
|
45
|
+
text: a.string(),
|
|
46
|
+
})
|
|
47
|
+
.disableOperations(["subscriptions"])
|
|
48
|
+
.secondaryIndexes((index) => [index("itemId").queryField("listEditorialFeedbackByItem")])
|
|
49
|
+
.authorization((allow) => [allow.group("editor").to(["create", "read"]), allow.group("admin")]),
|
|
50
|
+
EditorialCheckRun: a
|
|
51
|
+
.model({
|
|
52
|
+
id: a.id().required(),
|
|
53
|
+
itemId: a.string().required(),
|
|
54
|
+
lineageId: a.string().required(),
|
|
55
|
+
versionNumber: a.integer().required(),
|
|
56
|
+
section: a.string(),
|
|
57
|
+
contentHash: a.string(),
|
|
58
|
+
packVersion: a.string(),
|
|
59
|
+
violations: a.integer().required(),
|
|
60
|
+
reviews: a.integer().required(),
|
|
61
|
+
findings: a.json().required(),
|
|
62
|
+
lanes: a.json(),
|
|
63
|
+
checkedAt: a.datetime().required(),
|
|
64
|
+
})
|
|
65
|
+
.disableOperations(["subscriptions"])
|
|
66
|
+
.secondaryIndexes((index) => [index("lineageId").sortKeys(["versionNumber"]).queryField("listEditorialCheckRunsByLineage")])
|
|
67
|
+
.authorization((allow) => [allow.group("editor").to(["read"]), allow.group("admin")]),
|
|
68
|
+
listEditorialStarterPacks: a
|
|
69
|
+
.query()
|
|
70
|
+
.returns(a.json())
|
|
71
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
72
|
+
.handler(a.handler.function(editorialChecks)),
|
|
73
|
+
checkItemVersion: a
|
|
74
|
+
.mutation()
|
|
75
|
+
.arguments({ itemId: a.id().required() })
|
|
76
|
+
.returns(a.json())
|
|
77
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
78
|
+
.handler(a.handler.function(editorialChecks)),
|
|
79
|
+
checkEditorial: a
|
|
80
|
+
.query()
|
|
81
|
+
.arguments({ body: a.string().required(), format: a.string(), section: a.string() })
|
|
82
|
+
.returns(a.json())
|
|
83
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
84
|
+
.handler(a.handler.function(editorialChecks)),
|
|
85
|
+
suggestEditorialRewrite: a
|
|
86
|
+
.query()
|
|
87
|
+
.arguments({
|
|
88
|
+
body: a.string().required(),
|
|
89
|
+
format: a.string(),
|
|
90
|
+
section: a.string(),
|
|
91
|
+
finding: a.json(),
|
|
92
|
+
selection: a.json(),
|
|
93
|
+
note: a.string(),
|
|
94
|
+
})
|
|
95
|
+
.returns(a.json())
|
|
96
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
97
|
+
.handler(a.handler.function(editorialChecks)),
|
|
98
|
+
draftEditorialQuestions: a
|
|
99
|
+
.query()
|
|
100
|
+
.arguments({ title: a.string().required(), guidance: a.string().required(), examples: a.json() })
|
|
101
|
+
.returns(a.json())
|
|
102
|
+
.authorization((allow) => [allow.group("admin")])
|
|
103
|
+
.handler(a.handler.function(editorialChecks)),
|
|
104
|
+
tryEditorialPolicy: a
|
|
105
|
+
.query()
|
|
106
|
+
.arguments({ policy: a.json().required(), text: a.string().required(), format: a.string() })
|
|
107
|
+
.returns(a.json())
|
|
108
|
+
.authorization((allow) => [allow.group("admin")])
|
|
109
|
+
.handler(a.handler.function(editorialChecks)),
|
|
110
|
+
importEditorialStarterPack: a
|
|
111
|
+
.mutation()
|
|
112
|
+
.arguments({ name: a.string().required() })
|
|
113
|
+
.returns(a.json())
|
|
114
|
+
.authorization((allow) => [allow.group("admin")])
|
|
115
|
+
.handler(a.handler.function(editorialChecks)),
|
|
116
|
+
recalibrateEditorialPolicies: a
|
|
117
|
+
.mutation()
|
|
118
|
+
.returns(a.json())
|
|
119
|
+
.authorization((allow) => [allow.group("admin")])
|
|
120
|
+
.handler(a.handler.function(editorialChecks)),
|
|
121
|
+
};
|