@anthusai/papyrus 2.0.0-next.5 → 2.0.0-next.51
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 +23 -3
- package/amplify/data/resource.ts +25 -3
- package/amplify/data/schema.js +2028 -1979
- package/amplify/data/schema.ts +72 -14
- package/amplify/functions/assignment-action/data-access.js +90 -0
- package/amplify/functions/assignment-action/data-access.ts +89 -0
- package/amplify/functions/assignment-action/handler.ts +1 -1
- package/amplify/functions/content-actions/handler.py +53 -2
- 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/{plugins/editorial-checks/function → functions/editorial-checks}/resource.js +7 -7
- package/amplify/{plugins/editorial-checks/function → functions/editorial-checks}/resource.ts +7 -7
- package/amplify/functions/editorial-checks/schema.js +121 -0
- package/amplify/{plugins → functions}/editorial-checks/schema.ts +33 -6
- 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/editorial-context.ts +63 -4
- package/amplify/functions/shared/python-bundle.js +20 -14
- package/amplify/functions/shared/python-bundle.ts +25 -18
- package/amplify/plugins/newsletter/config.js +195 -0
- package/amplify/plugins/newsletter/config.ts +240 -0
- package/amplify/plugins/newsletter/function/handler.py +6 -0
- package/amplify/plugins/newsletter/function/requirements.txt +5 -0
- package/amplify/plugins/newsletter/ops/handler.py +12 -0
- package/amplify/plugins/newsletter/ops/resource.js +27 -0
- package/amplify/plugins/newsletter/ops/resource.ts +37 -0
- package/amplify/plugins/newsletter/papyrus.plugin.js +260 -0
- package/amplify/plugins/newsletter/papyrus.plugin.ts +308 -0
- package/amplify/plugins/newsletter/schema.js +56 -0
- package/amplify/plugins/newsletter/schema.ts +61 -0
- package/amplify/plugins/newsletter/secure-string-parameter.js +107 -0
- package/amplify/plugins/newsletter/secure-string-parameter.ts +120 -0
- package/amplify/plugins/newsletter/sending.js +140 -0
- package/amplify/plugins/newsletter/sending.ts +186 -0
- package/amplify/plugins/reader-steering/config.js +162 -0
- package/amplify/plugins/reader-steering/config.ts +248 -0
- package/amplify/plugins/reader-steering/function/ai_moderation.py +246 -0
- package/amplify/plugins/reader-steering/function/handler.py +1363 -0
- package/amplify/plugins/reader-steering/function/moderation-prompt.md +27 -0
- package/amplify/plugins/reader-steering/function/moderation.py +316 -0
- package/amplify/plugins/reader-steering/function/resource.js +27 -0
- package/amplify/plugins/reader-steering/function/resource.ts +35 -0
- package/amplify/plugins/reader-steering/papyrus.plugin.js +175 -0
- package/amplify/plugins/reader-steering/papyrus.plugin.ts +201 -0
- package/amplify/plugins/reader-steering/schema.js +140 -0
- package/amplify/plugins/reader-steering/schema.ts +142 -0
- package/amplify/plugins/registry.js +4 -2
- package/amplify/plugins/registry.ts +4 -2
- package/amplify/plugins/types.ts +5 -0
- package/amplify/plugins/wire.js +1 -0
- package/amplify/plugins/wire.ts +1 -0
- package/amplify/publication/redirect-and-index.js +105 -0
- package/amplify/publication/redirect-rules.js +73 -0
- package/amplify/publication/redirect-rules.ts +93 -0
- package/amplify/publication/s3-projection.js +7 -1
- package/amplify/publication/s3-projection.ts +9 -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/newsletter/confirm/route.ts +13 -0
- package/app/api/newsletter/preferences/route.ts +13 -0
- package/app/api/newsletter/subscribe/route.ts +9 -0
- package/app/api/newsletter/unsubscribe/route.ts +13 -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/app/api/reader/comment/delete/route.ts +9 -0
- package/app/api/reader/comment/edit/route.ts +9 -0
- package/app/api/reader/comment/route.ts +9 -0
- package/app/api/reader/handle/route.ts +9 -0
- package/app/api/reader/me/route.ts +9 -0
- package/app/api/reader/rating/route.ts +9 -0
- package/app/api/reader/report/route.ts +9 -0
- package/app/api/reader/retired/route.ts +9 -0
- package/app/api/reader/submit/route.ts +9 -0
- package/app/api/reader/vote/route.ts +9 -0
- package/bin/papyrus-infra.mjs +20 -1
- package/components/editorial-checks-admin-panel.tsx +36 -11
- package/components/moderation-panel.tsx +248 -0
- package/components/monitoring-overview-view.tsx +241 -0
- package/components/monitoring-panel.tsx +40 -0
- package/components/newsletter-panel.tsx +474 -0
- package/components/newsletter-sections.tsx +163 -0
- package/components/newsroom-article-editor.tsx +2 -4
- package/components/newsroom-board-view.tsx +4 -1
- package/components/newsroom-curation-bootstrap.tsx +45 -0
- package/components/newsroom-organizer.css +31 -0
- package/components/newsroom-references-view.tsx +14 -14
- package/components/publication-cyclotron-panel.tsx +15 -10
- package/components/reference-add-entry.tsx +75 -0
- package/components/reference-curation-controls.tsx +31 -22
- package/components/reference-cyclotron-priming.tsx +58 -19
- 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 +64 -20
- package/components/use-editorial-checks.ts +12 -10
- package/infra/amplify-app-shell.js +90 -16
- package/infra/amplify-app-shell.ts +102 -18
- package/infra/index.js +1 -0
- package/infra/secure-string-parameter-handler.js +41 -0
- package/infra/secure-string-parameter-handler.ts +41 -0
- package/infra/secure-string-parameter.js +79 -0
- package/infra/secure-string-parameter.ts +91 -0
- package/infra/site-config.js +85 -6
- package/infra/site-config.ts +123 -6
- package/lib/category-repository.ts +11 -9
- package/lib/curation-routes.ts +345 -0
- package/lib/editorial-checks-backend.ts +1 -1
- package/lib/editorial-checks-client.ts +19 -1
- package/lib/editorial-checks-demo.ts +2 -2
- package/lib/editorial-checks-settings.ts +3 -0
- package/lib/editorial-checks.ts +8 -11
- package/lib/editorial-preview.ts +5 -5
- package/lib/monitoring-client.ts +29 -0
- package/lib/monitoring-overview-fixture.ts +51 -0
- package/lib/monitoring-overview.ts +145 -0
- package/lib/newsletter-api.ts +435 -0
- package/lib/newsletter-client.ts +46 -0
- package/lib/newsletter-fixture.ts +184 -0
- package/lib/newsletter-model.ts +202 -0
- package/lib/newsletter-pages.ts +364 -0
- package/lib/newsroom-board.ts +27 -2
- package/lib/prospectus-session.ts +15 -14
- package/lib/prospectus-sign-in-routes.ts +54 -13
- package/lib/prospectus-site-identity.ts +24 -1
- package/lib/reader-api.ts +145 -0
- package/lib/reader-moderation.ts +220 -0
- package/lib/reader-retirement-route.ts +89 -0
- package/lib/reader-subject.md +29 -0
- package/lib/reader-subject.ts +53 -0
- package/lib/relevance-status.ts +2 -2
- package/lib/site-brand-tenant.ts +11 -6
- package/lib/site-brand.ts +6 -3
- package/lib/site-origin.ts +67 -0
- package/middleware.ts +14 -3
- package/package.json +42 -4
- package/routes.manifest.json +191 -1
- package/amplify/plugins/editorial-checks/function/handler.py +0 -11
- package/amplify/plugins/editorial-checks/function/requirements.txt +0 -1
- package/amplify/plugins/editorial-checks/papyrus.plugin.js +0 -50
- package/amplify/plugins/editorial-checks/papyrus.plugin.ts +0 -63
- package/amplify/plugins/editorial-checks/schema.js +0 -94
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() })
|
|
@@ -817,6 +854,7 @@ const baseDefinitions = {
|
|
|
817
854
|
storyLineageId: a.id(),
|
|
818
855
|
assignmentId: a.id(),
|
|
819
856
|
maxCharacters: a.integer(),
|
|
857
|
+
optionalPublicationDoctrine: a.boolean(),
|
|
820
858
|
})
|
|
821
859
|
.returns(a.json().required())
|
|
822
860
|
.authorization((allow) => [allow.groups(categoryWriteGroups)])
|
|
@@ -1782,7 +1820,7 @@ const baseDefinitions = {
|
|
|
1782
1820
|
updatedAt: a.datetime(),
|
|
1783
1821
|
tags: a.hasMany("ItemTag", "itemId"),
|
|
1784
1822
|
mediaAssets: a.hasMany("MediaAsset", "itemId"),
|
|
1785
|
-
editionItems: a.hasMany("EditionItem", "itemId"),
|
|
1823
|
+
...(includeEditions ? { editionItems: a.hasMany("EditionItem", "itemId") } : {}),
|
|
1786
1824
|
})
|
|
1787
1825
|
.secondaryIndexes((index) => [
|
|
1788
1826
|
index("lineageId").sortKeys(["versionNumber"]).queryField("listItemsByLineageAndVersion"),
|
|
@@ -2105,27 +2143,47 @@ const baseDefinitions = {
|
|
|
2105
2143
|
allow.guest().to(["read"]),
|
|
2106
2144
|
allow.groups(contentWriteGroups),
|
|
2107
2145
|
]),
|
|
2146
|
+
...editorialChecksSchemaDefinitions,
|
|
2147
|
+
...monitoringViewSchemaDefinitions,
|
|
2108
2148
|
};
|
|
2149
|
+
if (!includeEditions) {
|
|
2150
|
+
const mutableDefinitions = definitions as Record<string, unknown>;
|
|
2151
|
+
for (const modelName of OPTIONAL_MODEL_GROUPS.editions) delete mutableDefinitions[modelName];
|
|
2152
|
+
}
|
|
2153
|
+
return definitions;
|
|
2154
|
+
}
|
|
2109
2155
|
|
|
2110
|
-
export
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
allow.resource(manageUserRole),
|
|
2114
|
-
allow.resource(modelAttachmentUpload),
|
|
2115
|
-
allow.resource(newsroomSummary),
|
|
2116
|
-
allow.resource(procedureAction),
|
|
2117
|
-
]);
|
|
2156
|
+
export function baseModelNames(excludedGroups: readonly OptionalModelGroup[] = []): string[] {
|
|
2157
|
+
return Object.keys(buildBaseDefinitions(!excludedGroups.includes("editions")));
|
|
2158
|
+
}
|
|
2118
2159
|
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2160
|
+
const baseDefinitions = buildBaseDefinitions(true);
|
|
2161
|
+
|
|
2162
|
+
/** Schema-level function grants shared by every deployed schema, with or without plugins, so none can drop one. */
|
|
2163
|
+
function functionGrants(allow: any) {
|
|
2164
|
+
return [
|
|
2165
|
+
allow.resource(assignmentAction).to(["query", "mutate"]),
|
|
2122
2166
|
allow.resource(categoryAction),
|
|
2123
2167
|
allow.resource(contentActions),
|
|
2124
2168
|
allow.resource(manageUserRole),
|
|
2125
2169
|
allow.resource(modelAttachmentUpload),
|
|
2126
2170
|
allow.resource(newsroomSummary),
|
|
2127
2171
|
allow.resource(procedureAction),
|
|
2128
|
-
|
|
2172
|
+
allow.resource(publicForms).to(["mutate"]),
|
|
2173
|
+
];
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
export const schema = a.schema(baseDefinitions).authorization(functionGrants);
|
|
2177
|
+
|
|
2178
|
+
/** The deployed schema of a site that enables plugins: the base definitions plus what each plugin contributes (docs/plugins.md). */
|
|
2179
|
+
export function schemaWithPlugins(pluginDefinitions: Record<string, any>) {
|
|
2180
|
+
return a.schema({ ...baseDefinitions, ...pluginDefinitions } as typeof baseDefinitions).authorization(functionGrants);
|
|
2181
|
+
}
|
|
2182
|
+
|
|
2183
|
+
/** The schema of a site that leaves optional model groups out of its first deploy. */
|
|
2184
|
+
export function schemaExcludingGroups(excludedGroups: readonly OptionalModelGroup[], pluginDefinitions: Record<string, any> = {}) {
|
|
2185
|
+
const definitions = buildBaseDefinitions(!excludedGroups.includes("editions")) as unknown as typeof baseDefinitions;
|
|
2186
|
+
return a.schema({ ...definitions, ...pluginDefinitions } as typeof baseDefinitions).authorization(functionGrants);
|
|
2129
2187
|
}
|
|
2130
2188
|
|
|
2131
2189
|
export type Schema = ClientSchema<typeof schema>;
|
|
@@ -0,0 +1,90 @@
|
|
|
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/listNewsroomSections",
|
|
13
|
+
"Query/fields/listNewsroomSectionsByTenantAndSortOrder",
|
|
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
|
+
];
|
|
61
|
+
/** Keep the SDK's generated configuration, replacing only this role's GraphQL grants at synthesis. */
|
|
62
|
+
export function scopeAssignmentDataAccess(lambda, graphqlApiArn) {
|
|
63
|
+
if (!lambda.role)
|
|
64
|
+
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))
|
|
70
|
+
return;
|
|
71
|
+
const stack = Stack.of(node);
|
|
72
|
+
const roles = stack.resolve(node.roles);
|
|
73
|
+
if (!roles?.some((role) => JSON.stringify(role) === JSON.stringify(stack.resolve(roleName))))
|
|
74
|
+
return;
|
|
75
|
+
const document = stack.resolve(node.policyDocument);
|
|
76
|
+
document.Statement.forEach((statement, index) => {
|
|
77
|
+
const actions = Array.isArray(statement.Action) ? statement.Action : [statement.Action];
|
|
78
|
+
if (actions.includes("*") || actions.includes("appsync:*")) {
|
|
79
|
+
throw new Error("Unexpected wildcard assignment action; review permissions before synthesis.");
|
|
80
|
+
}
|
|
81
|
+
if (!actions.includes("appsync:GraphQL"))
|
|
82
|
+
return;
|
|
83
|
+
if (statement.Effect !== "Allow" || actions.length !== 1 || statement.NotResource) {
|
|
84
|
+
throw new Error("Unexpected assignment GraphQL policy; review field scoping before synthesis.");
|
|
85
|
+
}
|
|
86
|
+
node.addPropertyOverride(`PolicyDocument.Statement.${index}.Resource`, resources);
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
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/listNewsroomSections",
|
|
15
|
+
"Query/fields/listNewsroomSectionsByTenantAndSortOrder",
|
|
16
|
+
"Query/fields/getKnowledgeCorpus",
|
|
17
|
+
"Query/fields/getKnowledgeImportRun",
|
|
18
|
+
"Query/fields/getReference",
|
|
19
|
+
"Query/fields/getReferenceAttachment",
|
|
20
|
+
"Query/fields/getMessage",
|
|
21
|
+
"Query/fields/getSemanticRelation",
|
|
22
|
+
"Query/fields/getSemanticNode",
|
|
23
|
+
"Query/fields/listReferences",
|
|
24
|
+
"Query/fields/listReferenceAttachments",
|
|
25
|
+
"Query/fields/listSemanticRelations",
|
|
26
|
+
"Query/fields/listKnowledgeCorporaByRoleAndName",
|
|
27
|
+
"Query/fields/listItemsByLineageAndVersion",
|
|
28
|
+
"Query/fields/itemBySlug",
|
|
29
|
+
"Query/fields/itemByTenantSlug",
|
|
30
|
+
"Query/fields/listModelAttachmentsByOwnerRoleAndSortKey",
|
|
31
|
+
"Query/fields/listAssignmentsByQueueStatusAndPriority",
|
|
32
|
+
"Query/fields/listSemanticRelationsByObjectState",
|
|
33
|
+
"Query/fields/listSemanticRelationsBySubjectState",
|
|
34
|
+
"Query/fields/listAssignmentEventsByAssignmentAndCreatedAt",
|
|
35
|
+
"Mutation/fields/createAssignment",
|
|
36
|
+
"Mutation/fields/updateAssignment",
|
|
37
|
+
"Mutation/fields/createAssignmentEvent",
|
|
38
|
+
"Mutation/fields/createProcedureRun",
|
|
39
|
+
"Mutation/fields/updateProcedureRun",
|
|
40
|
+
"Mutation/fields/createKnowledgeCorpus",
|
|
41
|
+
"Mutation/fields/updateKnowledgeCorpus",
|
|
42
|
+
"Mutation/fields/createKnowledgeImportRun",
|
|
43
|
+
"Mutation/fields/updateKnowledgeImportRun",
|
|
44
|
+
"Mutation/fields/createKnowledgeRawPayload",
|
|
45
|
+
"Mutation/fields/updateKnowledgeRawPayload",
|
|
46
|
+
"Mutation/fields/createReference",
|
|
47
|
+
"Mutation/fields/updateReference",
|
|
48
|
+
"Mutation/fields/createReferenceAttachment",
|
|
49
|
+
"Mutation/fields/updateReferenceAttachment",
|
|
50
|
+
"Mutation/fields/createMessage",
|
|
51
|
+
"Mutation/fields/updateMessage",
|
|
52
|
+
"Mutation/fields/createSemanticRelation",
|
|
53
|
+
"Mutation/fields/updateSemanticRelation",
|
|
54
|
+
"Mutation/fields/createSemanticNode",
|
|
55
|
+
"Mutation/fields/updateSemanticNode",
|
|
56
|
+
"Mutation/fields/createModelAttachment",
|
|
57
|
+
"Mutation/fields/updateModelAttachment",
|
|
58
|
+
"Mutation/fields/createModelAttachmentUpload",
|
|
59
|
+
"Mutation/fields/completeModelAttachmentUpload",
|
|
60
|
+
"Mutation/fields/createModelAttachmentDownload",
|
|
61
|
+
"Mutation/fields/updateNewsroomSummary",
|
|
62
|
+
] as const;
|
|
63
|
+
|
|
64
|
+
/** Keep the SDK's generated configuration, replacing only this role's GraphQL grants at synthesis. */
|
|
65
|
+
export function scopeAssignmentDataAccess(lambda: IFunction, graphqlApiArn: string): void {
|
|
66
|
+
if (!lambda.role) throw new Error("Assignment function needs an execution role.");
|
|
67
|
+
const roleName = lambda.role.roleName;
|
|
68
|
+
const resources = ASSIGNMENT_DATA_FIELDS.map((field) => `${graphqlApiArn}/types/${field}`);
|
|
69
|
+
Aspects.of(Stack.of(lambda)).add({
|
|
70
|
+
visit(node) {
|
|
71
|
+
if (!(node instanceof CfnPolicy)) return;
|
|
72
|
+
const stack = Stack.of(node);
|
|
73
|
+
const roles = stack.resolve(node.roles) as unknown[] | undefined;
|
|
74
|
+
if (!roles?.some((role) => JSON.stringify(role) === JSON.stringify(stack.resolve(roleName)))) return;
|
|
75
|
+
const document = stack.resolve(node.policyDocument) as { Statement: Array<Record<string, unknown>> };
|
|
76
|
+
document.Statement.forEach((statement, index) => {
|
|
77
|
+
const actions = Array.isArray(statement.Action) ? statement.Action : [statement.Action];
|
|
78
|
+
if (actions.includes("*") || actions.includes("appsync:*")) {
|
|
79
|
+
throw new Error("Unexpected wildcard assignment action; review permissions before synthesis.");
|
|
80
|
+
}
|
|
81
|
+
if (!actions.includes("appsync:GraphQL")) return;
|
|
82
|
+
if (statement.Effect !== "Allow" || actions.length !== 1 || statement.NotResource) {
|
|
83
|
+
throw new Error("Unexpected assignment GraphQL policy; review field scoping before synthesis.");
|
|
84
|
+
}
|
|
85
|
+
node.addPropertyOverride(`PolicyDocument.Statement.${index}.Resource`, resources);
|
|
86
|
+
});
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
}
|
|
@@ -316,7 +316,7 @@ async function listAssignmentsForObject(event: Parameters<Schema["listAssignment
|
|
|
316
316
|
return contexts;
|
|
317
317
|
}
|
|
318
318
|
|
|
319
|
-
async function editorialContext(client: DataClient, scope: { publicationKey?: string; sectionId?: string | null; storyLineageId?: string | null; assignmentId?: string | null; maxCharacters?: number | null }) {
|
|
319
|
+
async function editorialContext(client: DataClient, scope: { publicationKey?: string; sectionId?: string | null; storyLineageId?: string | null; assignmentId?: string | null; maxCharacters?: number | null; optionalPublicationDoctrine?: boolean | null }) {
|
|
320
320
|
if (!editorialEndpoint) throw new Error("Editorial context requires the configured backend endpoint.");
|
|
321
321
|
return resolveEditorialContext(client as unknown as EditorialClient, {
|
|
322
322
|
...scope, endpoint: editorialEndpoint, tenantId: tenantIdFromEnvironment(), readAttachment: readEditorialAttachment,
|
|
@@ -9,6 +9,7 @@ from papyrus_content.graphql_authoring import create_authoring_client
|
|
|
9
9
|
from papyrus_content.markus_renderer.derive import derive_body
|
|
10
10
|
from papyrus_content.projection.deploy_render import deploy_render, drift_metric_line
|
|
11
11
|
from papyrus_content.projection.engine import project, project_all, project_item_pages
|
|
12
|
+
from papyrus_content.reader_steering.task import TASK_NAME as READER_SNAPSHOT_TASK, run_reader_snapshot
|
|
12
13
|
from papyrus_content.projection.runtime import (
|
|
13
14
|
DEAD_LETTER_QUEUE_ENV,
|
|
14
15
|
context_from_environment,
|
|
@@ -23,6 +24,10 @@ from papyrus_content.rebuild_trigger import (
|
|
|
23
24
|
trigger_staging_build,
|
|
24
25
|
)
|
|
25
26
|
from papyrus_content.story_reporting import save_story_version
|
|
27
|
+
from papyrus_content.publication import ensure_publication_from_environment
|
|
28
|
+
from papyrus_content.editorial_checks import GraphQLEditorialStore, ensure_default_policies
|
|
29
|
+
from papyrus_content.reference_intake import IntakeError, intake_references
|
|
30
|
+
from papyrus_content.publish_alerts import PUBLISHED_TASK_KEY, announce_published
|
|
26
31
|
from papyrus_content.publishing import (
|
|
27
32
|
ItemFields,
|
|
28
33
|
PublishError,
|
|
@@ -115,11 +120,17 @@ def projection_context(client):
|
|
|
115
120
|
return context
|
|
116
121
|
|
|
117
122
|
|
|
118
|
-
def project_item_action(client, item_id: str, *, unpublished: bool) -> dict:
|
|
123
|
+
def project_item_action(client, item_id: str, *, unpublished: bool, published_item_id: str | None = None) -> dict:
|
|
124
|
+
"""Project one item. ``published_item_id`` is passed only for a real publish: the alert is announced once the page
|
|
125
|
+
is live, here when the projection succeeds, or by the scheduled retry (which carries the same marker) when it did not."""
|
|
119
126
|
if not projection_configured():
|
|
127
|
+
if published_item_id:
|
|
128
|
+
announce_published(item_id, published_item_id)
|
|
120
129
|
return {}
|
|
121
130
|
liveness = {} if unpublished else {"live": False}
|
|
122
131
|
task = {"task": "item", "itemId": item_id, "unpublished": unpublished}
|
|
132
|
+
if published_item_id:
|
|
133
|
+
task[PUBLISHED_TASK_KEY] = {"publishedItemId": published_item_id}
|
|
123
134
|
try:
|
|
124
135
|
report = project(projection_context(client), item_id, unpublished=unpublished)
|
|
125
136
|
except Exception as error: # noqa: BLE001 - a projection failure never fails the save
|
|
@@ -141,6 +152,8 @@ def project_item_action(client, item_id: str, *, unpublished: bool) -> dict:
|
|
|
141
152
|
schedule_unpurged_invalidation(report.unpurged)
|
|
142
153
|
if report.follow_up_item_pages:
|
|
143
154
|
schedule_projection_task({"task": "item-pages", "exceptItemId": item_id}, "follow-up")
|
|
155
|
+
if report.ok and published_item_id:
|
|
156
|
+
announce_published(item_id, published_item_id)
|
|
144
157
|
return response
|
|
145
158
|
|
|
146
159
|
|
|
@@ -148,8 +161,19 @@ def schedule_unpurged_invalidation(keys: list[str]) -> None:
|
|
|
148
161
|
schedule_projection_task({"task": "invalidate", "keys": keys}, "edge invalidation failed")
|
|
149
162
|
|
|
150
163
|
|
|
164
|
+
def ensure_default_editorial_policies(client) -> None:
|
|
165
|
+
"""A deployed site starts with working editorial policies. Never fails the deploy: a problem is logged."""
|
|
166
|
+
try:
|
|
167
|
+
result = ensure_default_policies(GraphQLEditorialStore(execute=client.graphql))
|
|
168
|
+
print(json.dumps({"defaultEditorialPolicies": result}))
|
|
169
|
+
except Exception as error:
|
|
170
|
+
print(json.dumps({"defaultEditorialPoliciesError": str(error)[:500]}))
|
|
171
|
+
|
|
172
|
+
|
|
151
173
|
def run_deploy_render_task(client) -> dict:
|
|
152
174
|
"""Render every published page after a backend deploy, then report drift (a dry run: nothing is deleted)."""
|
|
175
|
+
ensure_publication_from_environment(client)
|
|
176
|
+
ensure_default_editorial_policies(client)
|
|
153
177
|
context = projection_context(client)
|
|
154
178
|
report = deploy_render(context)
|
|
155
179
|
print(drift_metric_line(report))
|
|
@@ -173,6 +197,8 @@ def run_projection_task(task: dict) -> dict:
|
|
|
173
197
|
report = project_all(projection_context(client))
|
|
174
198
|
elif task.get("task") == "deploy-render":
|
|
175
199
|
return run_deploy_render_task(client)
|
|
200
|
+
elif task.get("task") == READER_SNAPSHOT_TASK:
|
|
201
|
+
return run_reader_snapshot(projection_context(client), client, poll_retirements=task.get("retirementPoll") is True).to_dict()
|
|
176
202
|
elif task.get("task") == "invalidate":
|
|
177
203
|
invalidate = projection_context(client).invalidate
|
|
178
204
|
if invalidate is None:
|
|
@@ -184,6 +210,10 @@ def run_projection_task(task: dict) -> dict:
|
|
|
184
210
|
schedule_unpurged_invalidation(report.unpurged)
|
|
185
211
|
if not report.ok:
|
|
186
212
|
raise ProjectionTaskFailed("; ".join(report.errors))
|
|
213
|
+
published = task.get(PUBLISHED_TASK_KEY)
|
|
214
|
+
if task.get("task") == "item" and not task.get("unpublished") and isinstance(published, dict):
|
|
215
|
+
# A real publish (the CLI marks it, as does the newsroom's retry): the page is live, so tell the newsletter.
|
|
216
|
+
announce_published(task["itemId"], published.get("publishedItemId"))
|
|
187
217
|
return {"ok": True, "paths": [*report.written, *report.deleted]}
|
|
188
218
|
|
|
189
219
|
|
|
@@ -269,7 +299,9 @@ def publish_item_action(arguments: dict, actor: str) -> dict:
|
|
|
269
299
|
}
|
|
270
300
|
if result.changed:
|
|
271
301
|
response.update(site_triggers_for(client, arguments["id"]))
|
|
272
|
-
response.update(
|
|
302
|
+
response.update(
|
|
303
|
+
project_item_action(client, arguments["id"], unpublished=False, published_item_id=result.published_item_id if result.changed else None)
|
|
304
|
+
)
|
|
273
305
|
return response
|
|
274
306
|
|
|
275
307
|
|
|
@@ -283,6 +315,17 @@ def unpublish_item_action(arguments: dict, actor: str) -> dict:
|
|
|
283
315
|
return response
|
|
284
316
|
|
|
285
317
|
|
|
318
|
+
def move_story_stage_action(arguments: dict, actor: str) -> dict:
|
|
319
|
+
from papyrus_content.story_reporting import move_story_stage
|
|
320
|
+
|
|
321
|
+
client, _claims = create_authoring_client()
|
|
322
|
+
try:
|
|
323
|
+
result = move_story_stage(client, arguments["lineageId"], arguments["stage"], actor=actor, reason=arguments.get("reason"))
|
|
324
|
+
except ValueError as error:
|
|
325
|
+
return {"ok": False, "errors": [{"code": "invalid-stage", "message": str(error), "line": None}]}
|
|
326
|
+
return {"ok": True, **result}
|
|
327
|
+
|
|
328
|
+
|
|
286
329
|
def request_staging_build_action() -> dict:
|
|
287
330
|
cms_app_id = (os.environ.get("PAPYRUS_CMS_AMPLIFY_APP_ID") or "").strip()
|
|
288
331
|
if not cms_app_id:
|
|
@@ -299,12 +342,20 @@ def handler(event, context):
|
|
|
299
342
|
arguments = parse_input(event["arguments"]["input"])
|
|
300
343
|
actor = actor_from_event(event)
|
|
301
344
|
try:
|
|
345
|
+
if field == "intakePendingReferences":
|
|
346
|
+
client, _claims = create_authoring_client()
|
|
347
|
+
try:
|
|
348
|
+
return intake_references(client, arguments, event.get("identity") or {})
|
|
349
|
+
except IntakeError as error:
|
|
350
|
+
return {"ok": False, "errors": [{"code": "intake-rejected", "message": str(error)}]}
|
|
302
351
|
if field == "deriveMarkus":
|
|
303
352
|
return derive_markus_action(arguments)
|
|
304
353
|
if field == "saveItemDraft":
|
|
305
354
|
return save_item_draft_action(arguments, actor)
|
|
306
355
|
if field == "publishItem":
|
|
307
356
|
return publish_item_action(arguments, actor)
|
|
357
|
+
if field == "moveStoryStage":
|
|
358
|
+
return move_story_stage_action(arguments, actor)
|
|
308
359
|
if field == "unpublishItem":
|
|
309
360
|
return unpublish_item_action(arguments, actor)
|
|
310
361
|
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, event_field_name, 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_field_name(event), event.get("arguments") or {}, Services(store=GraphQLEditorialStore()))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
humanus[jev]>=0.33,<0.34
|
package/amplify/{plugins/editorial-checks/function → functions/editorial-checks}/resource.js
RENAMED
|
@@ -4,12 +4,12 @@ import { Architecture, Function, Runtime } from "aws-cdk-lib/aws-lambda";
|
|
|
4
4
|
import { execFileSync } from "node:child_process";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
import { pythonLambdaCode } from "
|
|
7
|
+
import { pythonLambdaCode } from "../shared/python-bundle.js";
|
|
8
8
|
const functionDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
-
/** AppSync gives a resolver 30 seconds;
|
|
9
|
+
/** AppSync gives a resolver 30 seconds; Humanus's classifier deadline (settings, default 20 s) keeps a check inside this. */
|
|
10
10
|
export const EDITORIAL_CHECKS_TIMEOUT_SECONDS = 28;
|
|
11
|
-
/**
|
|
12
|
-
export const editorialChecksRequirements = ["
|
|
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
13
|
function installRequirements(outputDir) {
|
|
14
14
|
execFileSync("python3", [
|
|
15
15
|
"-m", "pip", "install", "-r", path.join(functionDirectory, "requirements.txt"), "-t", outputDir, "--no-cache-dir",
|
|
@@ -24,7 +24,7 @@ export const editorialChecks = defineFunction((scope) => new Function(scope, "pa
|
|
|
24
24
|
timeout: Duration.seconds(EDITORIAL_CHECKS_TIMEOUT_SECONDS),
|
|
25
25
|
memorySize: 1024,
|
|
26
26
|
code: pythonLambdaCode({
|
|
27
|
-
functionDir: "
|
|
27
|
+
functionDir: "editorial-checks",
|
|
28
28
|
modules: ["papyrus_content"],
|
|
29
29
|
requirements: editorialChecksRequirements,
|
|
30
30
|
noDeps: true,
|
|
@@ -35,9 +35,9 @@ export const editorialChecks = defineFunction((scope) => new Function(scope, "pa
|
|
|
35
35
|
[
|
|
36
36
|
"set -euo pipefail",
|
|
37
37
|
"mkdir -p /asset-output/papyrus_content",
|
|
38
|
-
"cp amplify/
|
|
38
|
+
"cp amplify/functions/editorial-checks/handler.py /asset-output/handler.py",
|
|
39
39
|
"cp -R src/papyrus_content/. /asset-output/papyrus_content/",
|
|
40
|
-
"python3 -m pip install -r amplify/
|
|
40
|
+
"python3 -m pip install -r amplify/functions/editorial-checks/requirements.txt -t /asset-output --no-cache-dir",
|
|
41
41
|
].join(" && "),
|
|
42
42
|
]),
|
|
43
43
|
}), { resourceGroupName: "data" });
|
package/amplify/{plugins/editorial-checks/function → functions/editorial-checks}/resource.ts
RENAMED
|
@@ -5,15 +5,15 @@ import type { Construct } from "constructs";
|
|
|
5
5
|
import { execFileSync } from "node:child_process";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
-
import { pythonLambdaCode } from "
|
|
8
|
+
import { pythonLambdaCode } from "../shared/python-bundle";
|
|
9
9
|
|
|
10
10
|
const functionDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
11
11
|
|
|
12
|
-
/** AppSync gives a resolver 30 seconds;
|
|
12
|
+
/** AppSync gives a resolver 30 seconds; Humanus's classifier deadline (settings, default 20 s) keeps a check inside this. */
|
|
13
13
|
export const EDITORIAL_CHECKS_TIMEOUT_SECONDS = 28;
|
|
14
14
|
|
|
15
|
-
/**
|
|
16
|
-
export const editorialChecksRequirements = ["
|
|
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
17
|
|
|
18
18
|
function installRequirements(outputDir: string): void {
|
|
19
19
|
execFileSync(
|
|
@@ -37,7 +37,7 @@ export const editorialChecks = defineFunction(
|
|
|
37
37
|
memorySize: 1024,
|
|
38
38
|
code: pythonLambdaCode(
|
|
39
39
|
{
|
|
40
|
-
functionDir: "
|
|
40
|
+
functionDir: "editorial-checks",
|
|
41
41
|
modules: ["papyrus_content"],
|
|
42
42
|
requirements: editorialChecksRequirements,
|
|
43
43
|
noDeps: true,
|
|
@@ -49,9 +49,9 @@ export const editorialChecks = defineFunction(
|
|
|
49
49
|
[
|
|
50
50
|
"set -euo pipefail",
|
|
51
51
|
"mkdir -p /asset-output/papyrus_content",
|
|
52
|
-
"cp amplify/
|
|
52
|
+
"cp amplify/functions/editorial-checks/handler.py /asset-output/handler.py",
|
|
53
53
|
"cp -R src/papyrus_content/. /asset-output/papyrus_content/",
|
|
54
|
-
"python3 -m pip install -r amplify/
|
|
54
|
+
"python3 -m pip install -r amplify/functions/editorial-checks/requirements.txt -t /asset-output --no-cache-dir",
|
|
55
55
|
].join(" && "),
|
|
56
56
|
],
|
|
57
57
|
),
|