@anthusai/papyrus 2.0.0-next.2 → 2.0.0-next.20
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 +13 -0
- package/amplify/data/resource.ts +15 -0
- package/amplify/data/schema.js +18 -0
- package/amplify/data/schema.ts +20 -0
- 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 +22 -0
- 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 +92 -0
- package/amplify/functions/editorial-checks/schema.ts +94 -0
- package/amplify/functions/editorial-checks/wire.js +49 -0
- package/amplify/functions/editorial-checks/wire.ts +57 -0
- package/amplify/functions/model-attachment-upload/handler.ts +1 -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/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.js +18 -0
- package/amplify/site-backend.ts +24 -0
- 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 +347 -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 +194 -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 +46 -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/resource.js
CHANGED
|
@@ -589,6 +589,12 @@ const schema = a.schema({
|
|
|
589
589
|
.returns(a.json())
|
|
590
590
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
591
591
|
.handler(a.handler.function(contentActions)),
|
|
592
|
+
intakePendingReferences: a
|
|
593
|
+
.mutation()
|
|
594
|
+
.arguments({ input: a.json().required() })
|
|
595
|
+
.returns(a.json())
|
|
596
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
597
|
+
.handler(a.handler.function(contentActions)),
|
|
592
598
|
saveItemDraft: a
|
|
593
599
|
.mutation()
|
|
594
600
|
.arguments({ input: a.json().required() })
|
|
@@ -601,6 +607,12 @@ const schema = a.schema({
|
|
|
601
607
|
.returns(a.json())
|
|
602
608
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
603
609
|
.handler(a.handler.function(contentActions)),
|
|
610
|
+
moveStoryStage: a
|
|
611
|
+
.mutation()
|
|
612
|
+
.arguments({ input: a.json().required() })
|
|
613
|
+
.returns(a.json())
|
|
614
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
615
|
+
.handler(a.handler.function(contentActions)),
|
|
604
616
|
unpublishItem: a
|
|
605
617
|
.mutation()
|
|
606
618
|
.arguments({ input: a.json().required() })
|
|
@@ -2146,6 +2158,7 @@ const schema = a.schema({
|
|
|
2146
2158
|
allow.groups(contentWriteGroups),
|
|
2147
2159
|
]),
|
|
2148
2160
|
}).authorization((allow) => [
|
|
2161
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2149
2162
|
allow.resource(categoryAction),
|
|
2150
2163
|
allow.resource(contentActions),
|
|
2151
2164
|
allow.resource(manageUserRole),
|
package/amplify/data/resource.ts
CHANGED
|
@@ -641,6 +641,13 @@ const schema = a.schema({
|
|
|
641
641
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
642
642
|
.handler(a.handler.function(contentActions)),
|
|
643
643
|
|
|
644
|
+
intakePendingReferences: a
|
|
645
|
+
.mutation()
|
|
646
|
+
.arguments({ input: a.json().required() })
|
|
647
|
+
.returns(a.json())
|
|
648
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
649
|
+
.handler(a.handler.function(contentActions)),
|
|
650
|
+
|
|
644
651
|
saveItemDraft: a
|
|
645
652
|
.mutation()
|
|
646
653
|
.arguments({ input: a.json().required() })
|
|
@@ -655,6 +662,13 @@ const schema = a.schema({
|
|
|
655
662
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
656
663
|
.handler(a.handler.function(contentActions)),
|
|
657
664
|
|
|
665
|
+
moveStoryStage: a
|
|
666
|
+
.mutation()
|
|
667
|
+
.arguments({ input: a.json().required() })
|
|
668
|
+
.returns(a.json())
|
|
669
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
670
|
+
.handler(a.handler.function(contentActions)),
|
|
671
|
+
|
|
658
672
|
unpublishItem: a
|
|
659
673
|
.mutation()
|
|
660
674
|
.arguments({ input: a.json().required() })
|
|
@@ -2271,6 +2285,7 @@ const schema = a.schema({
|
|
|
2271
2285
|
allow.groups(contentWriteGroups),
|
|
2272
2286
|
]),
|
|
2273
2287
|
}).authorization((allow) => [
|
|
2288
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2274
2289
|
allow.resource(categoryAction),
|
|
2275
2290
|
allow.resource(contentActions),
|
|
2276
2291
|
allow.resource(manageUserRole),
|
package/amplify/data/schema.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { a } from "@aws-amplify/backend";
|
|
2
2
|
import { assignmentAction } from "../functions/assignment-action/resource.js";
|
|
3
3
|
import { categoryAction } from "../functions/category-action/resource.js";
|
|
4
|
+
import { editorialChecksSchemaDefinitions } from "../functions/editorial-checks/schema.js";
|
|
5
|
+
import { monitoringViewSchemaDefinitions } from "../functions/monitoring-view/schema.js";
|
|
4
6
|
import { contentActions } from "../functions/content-actions/resource.js";
|
|
5
7
|
import { knowledgeQuery } from "../functions/knowledge-query/resource.js";
|
|
6
8
|
import { manageUserRole } from "../functions/manage-user-role/resource.js";
|
|
@@ -496,6 +498,12 @@ const baseDefinitions = {
|
|
|
496
498
|
.returns(a.json())
|
|
497
499
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
498
500
|
.handler(a.handler.function(contentActions)),
|
|
501
|
+
intakePendingReferences: a
|
|
502
|
+
.mutation()
|
|
503
|
+
.arguments({ input: a.json().required() })
|
|
504
|
+
.returns(a.json())
|
|
505
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
506
|
+
.handler(a.handler.function(contentActions)),
|
|
499
507
|
saveItemDraft: a
|
|
500
508
|
.mutation()
|
|
501
509
|
.arguments({ input: a.json().required() })
|
|
@@ -508,6 +516,12 @@ const baseDefinitions = {
|
|
|
508
516
|
.returns(a.json())
|
|
509
517
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
510
518
|
.handler(a.handler.function(contentActions)),
|
|
519
|
+
moveStoryStage: a
|
|
520
|
+
.mutation()
|
|
521
|
+
.arguments({ input: a.json().required() })
|
|
522
|
+
.returns(a.json())
|
|
523
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
524
|
+
.handler(a.handler.function(contentActions)),
|
|
511
525
|
unpublishItem: a
|
|
512
526
|
.mutation()
|
|
513
527
|
.arguments({ input: a.json().required() })
|
|
@@ -1987,8 +2001,11 @@ const baseDefinitions = {
|
|
|
1987
2001
|
allow.guest().to(["read"]),
|
|
1988
2002
|
allow.groups(contentWriteGroups),
|
|
1989
2003
|
]),
|
|
2004
|
+
...editorialChecksSchemaDefinitions,
|
|
2005
|
+
...monitoringViewSchemaDefinitions,
|
|
1990
2006
|
};
|
|
1991
2007
|
export const schema = a.schema(baseDefinitions).authorization((allow) => [
|
|
2008
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
1992
2009
|
allow.resource(categoryAction),
|
|
1993
2010
|
allow.resource(contentActions),
|
|
1994
2011
|
allow.resource(manageUserRole),
|
|
@@ -1999,6 +2016,7 @@ export const schema = a.schema(baseDefinitions).authorization((allow) => [
|
|
|
1999
2016
|
/** The deployed schema of a site that enables plugins: the base definitions plus what each plugin contributes (docs/plugins.md). */
|
|
2000
2017
|
export function schemaWithPlugins(pluginDefinitions) {
|
|
2001
2018
|
return a.schema({ ...baseDefinitions, ...pluginDefinitions }).authorization((allow) => [
|
|
2019
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2002
2020
|
allow.resource(categoryAction),
|
|
2003
2021
|
allow.resource(contentActions),
|
|
2004
2022
|
allow.resource(manageUserRole),
|
package/amplify/data/schema.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
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";
|
|
@@ -542,6 +544,13 @@ const baseDefinitions = {
|
|
|
542
544
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
543
545
|
.handler(a.handler.function(contentActions)),
|
|
544
546
|
|
|
547
|
+
intakePendingReferences: a
|
|
548
|
+
.mutation()
|
|
549
|
+
.arguments({ input: a.json().required() })
|
|
550
|
+
.returns(a.json())
|
|
551
|
+
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
552
|
+
.handler(a.handler.function(contentActions)),
|
|
553
|
+
|
|
545
554
|
saveItemDraft: a
|
|
546
555
|
.mutation()
|
|
547
556
|
.arguments({ input: a.json().required() })
|
|
@@ -556,6 +565,13 @@ const baseDefinitions = {
|
|
|
556
565
|
.authorization((allow) => [allow.groups(contentWriteGroups)])
|
|
557
566
|
.handler(a.handler.function(contentActions)),
|
|
558
567
|
|
|
568
|
+
moveStoryStage: 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
|
+
|
|
559
575
|
unpublishItem: a
|
|
560
576
|
.mutation()
|
|
561
577
|
.arguments({ input: a.json().required() })
|
|
@@ -2105,9 +2121,12 @@ const baseDefinitions = {
|
|
|
2105
2121
|
allow.guest().to(["read"]),
|
|
2106
2122
|
allow.groups(contentWriteGroups),
|
|
2107
2123
|
]),
|
|
2124
|
+
...editorialChecksSchemaDefinitions,
|
|
2125
|
+
...monitoringViewSchemaDefinitions,
|
|
2108
2126
|
};
|
|
2109
2127
|
|
|
2110
2128
|
export const schema = a.schema(baseDefinitions).authorization((allow) => [
|
|
2129
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2111
2130
|
allow.resource(categoryAction),
|
|
2112
2131
|
allow.resource(contentActions),
|
|
2113
2132
|
allow.resource(manageUserRole),
|
|
@@ -2119,6 +2138,7 @@ export const schema = a.schema(baseDefinitions).authorization((allow) => [
|
|
|
2119
2138
|
/** The deployed schema of a site that enables plugins: the base definitions plus what each plugin contributes (docs/plugins.md). */
|
|
2120
2139
|
export function schemaWithPlugins(pluginDefinitions: Record<string, any>) {
|
|
2121
2140
|
return a.schema({ ...baseDefinitions, ...pluginDefinitions } as typeof baseDefinitions).authorization((allow) => [
|
|
2141
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2122
2142
|
allow.resource(categoryAction),
|
|
2123
2143
|
allow.resource(contentActions),
|
|
2124
2144
|
allow.resource(manageUserRole),
|
|
@@ -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,8 @@ 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.reference_intake import IntakeError, intake_references
|
|
26
28
|
from papyrus_content.publishing import (
|
|
27
29
|
ItemFields,
|
|
28
30
|
PublishError,
|
|
@@ -150,6 +152,7 @@ def schedule_unpurged_invalidation(keys: list[str]) -> None:
|
|
|
150
152
|
|
|
151
153
|
def run_deploy_render_task(client) -> dict:
|
|
152
154
|
"""Render every published page after a backend deploy, then report drift (a dry run: nothing is deleted)."""
|
|
155
|
+
ensure_publication_from_environment(client)
|
|
153
156
|
context = projection_context(client)
|
|
154
157
|
report = deploy_render(context)
|
|
155
158
|
print(drift_metric_line(report))
|
|
@@ -283,6 +286,17 @@ def unpublish_item_action(arguments: dict, actor: str) -> dict:
|
|
|
283
286
|
return response
|
|
284
287
|
|
|
285
288
|
|
|
289
|
+
def move_story_stage_action(arguments: dict, actor: str) -> dict:
|
|
290
|
+
from papyrus_content.story_reporting import move_story_stage
|
|
291
|
+
|
|
292
|
+
client, _claims = create_authoring_client()
|
|
293
|
+
try:
|
|
294
|
+
result = move_story_stage(client, arguments["lineageId"], arguments["stage"], actor=actor, reason=arguments.get("reason"))
|
|
295
|
+
except ValueError as error:
|
|
296
|
+
return {"ok": False, "errors": [{"code": "invalid-stage", "message": str(error), "line": None}]}
|
|
297
|
+
return {"ok": True, **result}
|
|
298
|
+
|
|
299
|
+
|
|
286
300
|
def request_staging_build_action() -> dict:
|
|
287
301
|
cms_app_id = (os.environ.get("PAPYRUS_CMS_AMPLIFY_APP_ID") or "").strip()
|
|
288
302
|
if not cms_app_id:
|
|
@@ -299,12 +313,20 @@ def handler(event, context):
|
|
|
299
313
|
arguments = parse_input(event["arguments"]["input"])
|
|
300
314
|
actor = actor_from_event(event)
|
|
301
315
|
try:
|
|
316
|
+
if field == "intakePendingReferences":
|
|
317
|
+
client, _claims = create_authoring_client()
|
|
318
|
+
try:
|
|
319
|
+
return intake_references(client, arguments, event.get("identity") or {})
|
|
320
|
+
except IntakeError as error:
|
|
321
|
+
return {"ok": False, "errors": [{"code": "intake-rejected", "message": str(error)}]}
|
|
302
322
|
if field == "deriveMarkus":
|
|
303
323
|
return derive_markus_action(arguments)
|
|
304
324
|
if field == "saveItemDraft":
|
|
305
325
|
return save_item_draft_action(arguments, actor)
|
|
306
326
|
if field == "publishItem":
|
|
307
327
|
return publish_item_action(arguments, actor)
|
|
328
|
+
if field == "moveStoryStage":
|
|
329
|
+
return move_story_stage_action(arguments, actor)
|
|
308
330
|
if field == "unpublishItem":
|
|
309
331
|
return unpublish_item_action(arguments, actor)
|
|
310
332
|
if field == "requestStagingBuild":
|
|
@@ -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,92 @@
|
|
|
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
|
+
checkEditorial: a
|
|
51
|
+
.query()
|
|
52
|
+
.arguments({ body: a.string().required(), format: a.string(), section: a.string() })
|
|
53
|
+
.returns(a.json())
|
|
54
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
55
|
+
.handler(a.handler.function(editorialChecks)),
|
|
56
|
+
suggestEditorialRewrite: a
|
|
57
|
+
.query()
|
|
58
|
+
.arguments({
|
|
59
|
+
body: a.string().required(),
|
|
60
|
+
format: a.string(),
|
|
61
|
+
section: a.string(),
|
|
62
|
+
finding: a.json(),
|
|
63
|
+
selection: a.json(),
|
|
64
|
+
note: a.string(),
|
|
65
|
+
})
|
|
66
|
+
.returns(a.json())
|
|
67
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
68
|
+
.handler(a.handler.function(editorialChecks)),
|
|
69
|
+
draftEditorialQuestions: a
|
|
70
|
+
.query()
|
|
71
|
+
.arguments({ title: a.string().required(), guidance: a.string().required(), examples: a.json() })
|
|
72
|
+
.returns(a.json())
|
|
73
|
+
.authorization((allow) => [allow.group("admin")])
|
|
74
|
+
.handler(a.handler.function(editorialChecks)),
|
|
75
|
+
tryEditorialPolicy: a
|
|
76
|
+
.query()
|
|
77
|
+
.arguments({ policy: a.json().required(), text: a.string().required(), format: a.string() })
|
|
78
|
+
.returns(a.json())
|
|
79
|
+
.authorization((allow) => [allow.group("admin")])
|
|
80
|
+
.handler(a.handler.function(editorialChecks)),
|
|
81
|
+
importEditorialStarterPack: a
|
|
82
|
+
.mutation()
|
|
83
|
+
.arguments({ name: a.string().required() })
|
|
84
|
+
.returns(a.json())
|
|
85
|
+
.authorization((allow) => [allow.group("admin")])
|
|
86
|
+
.handler(a.handler.function(editorialChecks)),
|
|
87
|
+
recalibrateEditorialPolicies: a
|
|
88
|
+
.mutation()
|
|
89
|
+
.returns(a.json())
|
|
90
|
+
.authorization((allow) => [allow.group("admin")])
|
|
91
|
+
.handler(a.handler.function(editorialChecks)),
|
|
92
|
+
};
|