@anthusai/papyrus 2.0.0-next.1 → 2.0.0-next.100
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/auth/resource.js +16 -1
- package/amplify/auth/resource.ts +19 -1
- package/amplify/data/function-handler.js +20 -0
- package/amplify/data/function-handler.ts +40 -0
- package/amplify/data/resource.js +158 -61
- package/amplify/data/resource.ts +165 -61
- package/amplify/data/schema.js +2103 -1979
- package/amplify/data/schema.ts +206 -68
- 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/automation-view/handler.py +17 -0
- package/amplify/functions/automation-view/resource.js +24 -0
- package/amplify/functions/automation-view/resource.ts +34 -0
- package/amplify/functions/automation-view/schema.js +26 -0
- package/amplify/functions/automation-view/schema.ts +28 -0
- package/amplify/functions/automation-view/wire.js +56 -0
- package/amplify/functions/automation-view/wire.ts +76 -0
- package/amplify/functions/content-actions/handler.py +70 -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/functions/editorial-checks/resource.js +43 -0
- package/amplify/functions/editorial-checks/resource.ts +60 -0
- package/amplify/functions/editorial-checks/schema.js +122 -0
- package/amplify/functions/editorial-checks/schema.ts +124 -0
- package/amplify/functions/editorial-checks/wire.js +53 -0
- package/amplify/functions/editorial-checks/wire.ts +61 -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 +29 -0
- package/amplify/functions/monitoring-view/schema.ts +31 -0
- package/amplify/functions/monitoring-view/wire.js +64 -0
- package/amplify/functions/monitoring-view/wire.ts +81 -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 +245 -0
- package/amplify/plugins/newsletter/config.ts +305 -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 +263 -0
- package/amplify/plugins/newsletter/papyrus.plugin.ts +311 -0
- package/amplify/plugins/newsletter/schema.js +57 -0
- package/amplify/plugins/newsletter/schema.ts +62 -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 +182 -0
- package/amplify/plugins/reader-steering/config.ts +290 -0
- package/amplify/plugins/reader-steering/function/ai_moderation.py +246 -0
- package/amplify/plugins/reader-steering/function/handler.py +1567 -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/proposals.py +458 -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 +186 -0
- package/amplify/plugins/reader-steering/papyrus.plugin.ts +212 -0
- package/amplify/plugins/reader-steering/schema.js +229 -0
- package/amplify/plugins/reader-steering/schema.ts +231 -0
- package/amplify/plugins/registry.js +4 -0
- package/amplify/plugins/registry.ts +4 -0
- package/amplify/plugins/signup/schema.js +2 -1
- package/amplify/plugins/signup/schema.ts +2 -1
- package/amplify/plugins/types.ts +5 -0
- package/amplify/plugins/wire.js +1 -0
- package/amplify/plugins/wire.ts +1 -0
- package/amplify/publication/projection-lease.js +30 -0
- package/amplify/publication/projection-lease.ts +39 -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.js +19 -0
- package/amplify/site-backend-config.ts +45 -1
- package/amplify/site-backend-names.js +4 -2
- package/amplify/site-backend-names.ts +4 -2
- package/amplify/site-backend.js +51 -4
- package/amplify/site-backend.ts +60 -4
- package/app/api/auth/prospectus/sign-in/route.ts +4 -0
- package/app/api/newsletter/confirm/route.ts +13 -0
- package/app/api/newsletter/preferences/route.ts +13 -0
- package/app/api/newsletter/state/route.ts +9 -0
- package/app/api/newsletter/subscribe/route.ts +9 -0
- package/app/api/newsletter/unsubscribe/route.ts +13 -0
- package/app/api/newsroom/chat/[...path]/route.ts +17 -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/flag/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/app/layout.tsx +5 -1
- package/bin/papyrus-infra.mjs +20 -1
- package/components/automation-panel.tsx +49 -0
- package/components/automation-run-now.tsx +97 -0
- package/components/automation-view.tsx +195 -0
- 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/moderation-panel.tsx +248 -0
- package/components/monitoring-overview-view.tsx +414 -0
- package/components/monitoring-panel.tsx +60 -0
- package/components/news-desk-client-provider.tsx +18 -18
- package/components/news-desk-taxonomy-client.ts +28 -8
- package/components/newsletter-panel.tsx +474 -0
- package/components/newsletter-sections.tsx +163 -0
- package/components/newsroom-access-gate.tsx +16 -2
- package/components/newsroom-article-editor.tsx +145 -18
- package/components/newsroom-articles-view.tsx +6 -6
- package/components/newsroom-assignment-detail.tsx +6 -6
- package/components/newsroom-board-view.tsx +14 -10
- package/components/newsroom-curation-bootstrap.tsx +45 -0
- package/components/newsroom-ops-overview.tsx +26 -96
- package/components/newsroom-ops-shell.tsx +9 -5
- package/components/newsroom-organizer.css +90 -61
- package/components/newsroom-overview-skeleton.tsx +25 -0
- package/components/newsroom-references-view.tsx +192 -49
- 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/notification-center.tsx +281 -0
- package/components/papyrus-console-conversation.css +14 -0
- package/components/papyrus-console-conversation.tsx +55 -0
- package/components/papyrus-console-shell.tsx +62 -420
- package/components/papyrus-private-chat-panel.tsx +134 -0
- package/components/publication-cyclotron-panel.tsx +15 -10
- package/components/reader-auth-state.ts +1 -1
- package/components/reference-add-entry.tsx +75 -0
- package/components/reference-curation-controls.tsx +31 -22
- package/components/reference-cyclotron-priming.tsx +63 -25
- package/components/reference-cyclotron-status.tsx +4 -4
- package/components/reference-cyclotron-suggestion.tsx +15 -11
- package/components/reference-live-agreement.tsx +10 -10
- package/components/reference-priming.tsx +14 -10
- package/components/reference-relevance-review.tsx +4 -4
- package/components/reference-section-suggestion.tsx +2 -2
- package/components/topic-steering-workspace.tsx +241 -144
- package/components/use-editorial-checks.ts +133 -0
- package/infra/amplify-app-shell.js +96 -16
- package/infra/amplify-app-shell.ts +108 -18
- package/infra/cloud-email-config.js +32 -0
- package/infra/cloud-email-config.ts +39 -0
- package/infra/cloud-email.js +57 -0
- package/infra/cloud-email.test.js +69 -0
- package/infra/cloud-email.test.ts +66 -0
- package/infra/cloud-email.ts +60 -0
- 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 +92 -6
- package/infra/site-config.ts +138 -6
- package/lib/automation-client.ts +54 -0
- package/lib/automation-fixture.ts +93 -0
- package/lib/automation-overview.ts +252 -0
- package/lib/category-repository.ts +14 -12
- package/lib/console-web-context.ts +1 -1
- package/lib/curation-routes.ts +345 -0
- package/lib/cyclotron-suggestions.ts +11 -11
- 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 +175 -0
- package/lib/editorial-preview.ts +228 -0
- package/lib/monitoring-client.ts +83 -0
- package/lib/monitoring-overview-fixture.ts +51 -0
- package/lib/monitoring-overview.ts +188 -0
- package/lib/news-desk-session.ts +12 -21
- package/lib/newsletter-api.ts +418 -0
- package/lib/newsletter-client.ts +46 -0
- package/lib/newsletter-fixture.ts +184 -0
- package/lib/newsletter-model.ts +202 -0
- package/lib/newsroom-board.ts +27 -2
- package/lib/newsroom-demo-dashboard.ts +1 -1
- package/lib/newsroom-nav.ts +1 -1
- package/lib/newsroom-organizer-nav.ts +2 -1
- package/lib/papyrus-chat-admission.ts +65 -0
- package/lib/papyrus-hosted-chat.ts +217 -0
- package/lib/papyrus-private-chat-client.ts +81 -0
- package/lib/prospectus-session.ts +30 -14
- package/lib/prospectus-sign-in-routes.ts +106 -31
- package/lib/prospectus-site-identity.ts +46 -6
- 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/reference-review-details.ts +71 -0
- package/lib/reference-review-request.ts +14 -0
- package/lib/relevance-decisions.ts +42 -10
- package/lib/relevance-status.ts +6 -6
- package/lib/review-client.ts +120 -0
- package/lib/review-fixture.ts +45 -0
- package/lib/review-requests.ts +246 -0
- package/lib/semantic-graph.ts +2 -0
- package/lib/shared-chat-admission.ts +182 -0
- package/lib/site-brand-tenant.ts +11 -6
- package/lib/site-brand.ts +23 -3
- package/lib/site-origin-value.ts +50 -0
- package/lib/site-origin.ts +8 -0
- package/lib/use-reference-review-data.ts +44 -0
- package/middleware.ts +14 -3
- package/package.json +98 -4
- package/routes.manifest.json +227 -2
- package/src/with-papyrus.mjs +1 -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
|
@@ -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
|
|
@@ -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,122 @@
|
|
|
1
|
+
import { a } from "@aws-amplify/backend";
|
|
2
|
+
import { editorialChecks } from "./resource.js";
|
|
3
|
+
import { functionHandler } from "../../data/function-handler.js";
|
|
4
|
+
const editors = ["editor", "admin"];
|
|
5
|
+
/**
|
|
6
|
+
* Core to every site's data API. Policies, settings and editors' decisions live in Papyrus; Humanus checks
|
|
7
|
+
* content against them. Editors read policies and record decisions; admins
|
|
8
|
+
* manage policies and settings. Only the function (IAM) calls Humanus.
|
|
9
|
+
*/
|
|
10
|
+
export const editorialChecksSchemaDefinitions = {
|
|
11
|
+
EditorialPolicy: a
|
|
12
|
+
.model({
|
|
13
|
+
id: a.id().required(),
|
|
14
|
+
policyKey: a.string().required(),
|
|
15
|
+
scope: a.string().required(),
|
|
16
|
+
version: a.integer().required(),
|
|
17
|
+
title: a.string().required(),
|
|
18
|
+
guidance: a.string().required(),
|
|
19
|
+
severity: a.string().required(),
|
|
20
|
+
enabled: a.boolean().required(),
|
|
21
|
+
detectors: a.json().required(),
|
|
22
|
+
examples: a.json(),
|
|
23
|
+
rewriteGuidance: a.string(),
|
|
24
|
+
source: a.string().required(),
|
|
25
|
+
})
|
|
26
|
+
.disableOperations(["subscriptions"])
|
|
27
|
+
.authorization((allow) => [allow.group("editor").to(["read"]), allow.group("admin")]),
|
|
28
|
+
EditorialCheckSettings: a
|
|
29
|
+
.model({
|
|
30
|
+
id: a.id().required(),
|
|
31
|
+
settings: a.json().required(),
|
|
32
|
+
})
|
|
33
|
+
.disableOperations(["subscriptions"])
|
|
34
|
+
.authorization((allow) => [allow.group("editor").to(["read"]), allow.group("admin")]),
|
|
35
|
+
EditorialFeedback: a
|
|
36
|
+
.model({
|
|
37
|
+
id: a.id().required(),
|
|
38
|
+
itemId: a.string().required(),
|
|
39
|
+
findingId: a.string().required(),
|
|
40
|
+
policyId: a.string().required(),
|
|
41
|
+
detectorId: a.string().required(),
|
|
42
|
+
lane: a.string().required(),
|
|
43
|
+
probability: a.float(),
|
|
44
|
+
decision: a.string().required(),
|
|
45
|
+
reason: a.string(),
|
|
46
|
+
text: a.string(),
|
|
47
|
+
})
|
|
48
|
+
.disableOperations(["subscriptions"])
|
|
49
|
+
.secondaryIndexes((index) => [index("itemId").queryField("listEditorialFeedbackByItem")])
|
|
50
|
+
.authorization((allow) => [allow.group("editor").to(["create", "read"]), allow.group("admin")]),
|
|
51
|
+
EditorialCheckRun: a
|
|
52
|
+
.model({
|
|
53
|
+
id: a.id().required(),
|
|
54
|
+
itemId: a.string().required(),
|
|
55
|
+
lineageId: a.string().required(),
|
|
56
|
+
versionNumber: a.integer().required(),
|
|
57
|
+
section: a.string(),
|
|
58
|
+
contentHash: a.string(),
|
|
59
|
+
packVersion: a.string(),
|
|
60
|
+
violations: a.integer().required(),
|
|
61
|
+
reviews: a.integer().required(),
|
|
62
|
+
findings: a.json().required(),
|
|
63
|
+
lanes: a.json(),
|
|
64
|
+
checkedAt: a.datetime().required(),
|
|
65
|
+
})
|
|
66
|
+
.disableOperations(["subscriptions"])
|
|
67
|
+
.secondaryIndexes((index) => [index("lineageId").sortKeys(["versionNumber"]).queryField("listEditorialCheckRunsByLineage")])
|
|
68
|
+
.authorization((allow) => [allow.group("editor").to(["read"]), allow.group("admin")]),
|
|
69
|
+
listEditorialStarterPacks: a
|
|
70
|
+
.query()
|
|
71
|
+
.returns(a.json())
|
|
72
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
73
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
74
|
+
checkItemVersion: a
|
|
75
|
+
.mutation()
|
|
76
|
+
.arguments({ itemId: a.id().required() })
|
|
77
|
+
.returns(a.json())
|
|
78
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
79
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
80
|
+
checkEditorial: a
|
|
81
|
+
.query()
|
|
82
|
+
.arguments({ body: a.string().required(), format: a.string(), section: a.string() })
|
|
83
|
+
.returns(a.json())
|
|
84
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
85
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
86
|
+
suggestEditorialRewrite: a
|
|
87
|
+
.query()
|
|
88
|
+
.arguments({
|
|
89
|
+
body: a.string().required(),
|
|
90
|
+
format: a.string(),
|
|
91
|
+
section: a.string(),
|
|
92
|
+
finding: a.json(),
|
|
93
|
+
selection: a.json(),
|
|
94
|
+
note: a.string(),
|
|
95
|
+
})
|
|
96
|
+
.returns(a.json())
|
|
97
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
98
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
99
|
+
draftEditorialQuestions: a
|
|
100
|
+
.query()
|
|
101
|
+
.arguments({ title: a.string().required(), guidance: a.string().required(), examples: a.json() })
|
|
102
|
+
.returns(a.json())
|
|
103
|
+
.authorization((allow) => [allow.group("admin")])
|
|
104
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
105
|
+
tryEditorialPolicy: a
|
|
106
|
+
.query()
|
|
107
|
+
.arguments({ policy: a.json().required(), text: a.string().required(), format: a.string() })
|
|
108
|
+
.returns(a.json())
|
|
109
|
+
.authorization((allow) => [allow.group("admin")])
|
|
110
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
111
|
+
importEditorialStarterPack: a
|
|
112
|
+
.mutation()
|
|
113
|
+
.arguments({ name: a.string().required() })
|
|
114
|
+
.returns(a.json())
|
|
115
|
+
.authorization((allow) => [allow.group("admin")])
|
|
116
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
117
|
+
recalibrateEditorialPolicies: a
|
|
118
|
+
.mutation()
|
|
119
|
+
.returns(a.json())
|
|
120
|
+
.authorization((allow) => [allow.group("admin")])
|
|
121
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
122
|
+
};
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { a } from "@aws-amplify/backend";
|
|
2
|
+
import { editorialChecks } from "./resource";
|
|
3
|
+
import { functionHandler } from "../../data/function-handler";
|
|
4
|
+
|
|
5
|
+
const editors = ["editor", "admin"];
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Core to every site's data API. Policies, settings and editors' decisions live in Papyrus; Humanus checks
|
|
9
|
+
* content against them. Editors read policies and record decisions; admins
|
|
10
|
+
* manage policies and settings. Only the function (IAM) calls Humanus.
|
|
11
|
+
*/
|
|
12
|
+
export const editorialChecksSchemaDefinitions = {
|
|
13
|
+
EditorialPolicy: a
|
|
14
|
+
.model({
|
|
15
|
+
id: a.id().required(),
|
|
16
|
+
policyKey: a.string().required(),
|
|
17
|
+
scope: a.string().required(),
|
|
18
|
+
version: a.integer().required(),
|
|
19
|
+
title: a.string().required(),
|
|
20
|
+
guidance: a.string().required(),
|
|
21
|
+
severity: a.string().required(),
|
|
22
|
+
enabled: a.boolean().required(),
|
|
23
|
+
detectors: a.json().required(),
|
|
24
|
+
examples: a.json(),
|
|
25
|
+
rewriteGuidance: a.string(),
|
|
26
|
+
source: a.string().required(),
|
|
27
|
+
})
|
|
28
|
+
.disableOperations(["subscriptions"])
|
|
29
|
+
.authorization((allow) => [allow.group("editor").to(["read"]), allow.group("admin")]),
|
|
30
|
+
EditorialCheckSettings: a
|
|
31
|
+
.model({
|
|
32
|
+
id: a.id().required(),
|
|
33
|
+
settings: a.json().required(),
|
|
34
|
+
})
|
|
35
|
+
.disableOperations(["subscriptions"])
|
|
36
|
+
.authorization((allow) => [allow.group("editor").to(["read"]), allow.group("admin")]),
|
|
37
|
+
EditorialFeedback: a
|
|
38
|
+
.model({
|
|
39
|
+
id: a.id().required(),
|
|
40
|
+
itemId: a.string().required(),
|
|
41
|
+
findingId: a.string().required(),
|
|
42
|
+
policyId: a.string().required(),
|
|
43
|
+
detectorId: a.string().required(),
|
|
44
|
+
lane: a.string().required(),
|
|
45
|
+
probability: a.float(),
|
|
46
|
+
decision: a.string().required(),
|
|
47
|
+
reason: a.string(),
|
|
48
|
+
text: a.string(),
|
|
49
|
+
})
|
|
50
|
+
.disableOperations(["subscriptions"])
|
|
51
|
+
.secondaryIndexes((index) => [index("itemId").queryField("listEditorialFeedbackByItem")])
|
|
52
|
+
.authorization((allow) => [allow.group("editor").to(["create", "read"]), allow.group("admin")]),
|
|
53
|
+
EditorialCheckRun: a
|
|
54
|
+
.model({
|
|
55
|
+
id: a.id().required(),
|
|
56
|
+
itemId: a.string().required(),
|
|
57
|
+
lineageId: a.string().required(),
|
|
58
|
+
versionNumber: a.integer().required(),
|
|
59
|
+
section: a.string(),
|
|
60
|
+
contentHash: a.string(),
|
|
61
|
+
packVersion: a.string(),
|
|
62
|
+
violations: a.integer().required(),
|
|
63
|
+
reviews: a.integer().required(),
|
|
64
|
+
findings: a.json().required(),
|
|
65
|
+
lanes: a.json(),
|
|
66
|
+
checkedAt: a.datetime().required(),
|
|
67
|
+
})
|
|
68
|
+
.disableOperations(["subscriptions"])
|
|
69
|
+
.secondaryIndexes((index) => [index("lineageId").sortKeys(["versionNumber"]).queryField("listEditorialCheckRunsByLineage")])
|
|
70
|
+
.authorization((allow) => [allow.group("editor").to(["read"]), allow.group("admin")]),
|
|
71
|
+
listEditorialStarterPacks: a
|
|
72
|
+
.query()
|
|
73
|
+
.returns(a.json())
|
|
74
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
75
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
76
|
+
checkItemVersion: a
|
|
77
|
+
.mutation()
|
|
78
|
+
.arguments({ itemId: a.id().required() })
|
|
79
|
+
.returns(a.json())
|
|
80
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
81
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
82
|
+
checkEditorial: a
|
|
83
|
+
.query()
|
|
84
|
+
.arguments({ body: a.string().required(), format: a.string(), section: a.string() })
|
|
85
|
+
.returns(a.json())
|
|
86
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
87
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
88
|
+
suggestEditorialRewrite: a
|
|
89
|
+
.query()
|
|
90
|
+
.arguments({
|
|
91
|
+
body: a.string().required(),
|
|
92
|
+
format: a.string(),
|
|
93
|
+
section: a.string(),
|
|
94
|
+
finding: a.json(),
|
|
95
|
+
selection: a.json(),
|
|
96
|
+
note: a.string(),
|
|
97
|
+
})
|
|
98
|
+
.returns(a.json())
|
|
99
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
100
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
101
|
+
draftEditorialQuestions: a
|
|
102
|
+
.query()
|
|
103
|
+
.arguments({ title: a.string().required(), guidance: a.string().required(), examples: a.json() })
|
|
104
|
+
.returns(a.json())
|
|
105
|
+
.authorization((allow) => [allow.group("admin")])
|
|
106
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
107
|
+
tryEditorialPolicy: a
|
|
108
|
+
.query()
|
|
109
|
+
.arguments({ policy: a.json().required(), text: a.string().required(), format: a.string() })
|
|
110
|
+
.returns(a.json())
|
|
111
|
+
.authorization((allow) => [allow.group("admin")])
|
|
112
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
113
|
+
importEditorialStarterPack: a
|
|
114
|
+
.mutation()
|
|
115
|
+
.arguments({ name: a.string().required() })
|
|
116
|
+
.returns(a.json())
|
|
117
|
+
.authorization((allow) => [allow.group("admin")])
|
|
118
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
119
|
+
recalibrateEditorialPolicies: a
|
|
120
|
+
.mutation()
|
|
121
|
+
.returns(a.json())
|
|
122
|
+
.authorization((allow) => [allow.group("admin")])
|
|
123
|
+
.handler(functionHandler("editorialChecks", editorialChecks)),
|
|
124
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { secret } from "@aws-amplify/backend";
|
|
2
|
+
import { Stack } from "aws-cdk-lib";
|
|
3
|
+
import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
4
|
+
/** The model operations the function performs with its own role. The two context reads are its least-privilege access to private policy. */
|
|
5
|
+
export const EDITORIAL_CHECKS_FUNCTION_FIELDS = [
|
|
6
|
+
"Query/fields/listKnowledgeCorporaByRoleAndName",
|
|
7
|
+
"Query/fields/getEditorialContext",
|
|
8
|
+
"Query/fields/listEditorialPolicies",
|
|
9
|
+
"Query/fields/getEditorialCheckSettings",
|
|
10
|
+
"Query/fields/listEditorialFeedbacks",
|
|
11
|
+
"Mutation/fields/createEditorialPolicy",
|
|
12
|
+
"Mutation/fields/updateEditorialPolicy",
|
|
13
|
+
"Query/fields/getItem",
|
|
14
|
+
"Query/fields/getEditorialCheckRun",
|
|
15
|
+
"Mutation/fields/createEditorialCheckRun",
|
|
16
|
+
"Mutation/fields/updateEditorialCheckRun",
|
|
17
|
+
];
|
|
18
|
+
export const EDITORIAL_CHECKS_SECRETS = ["TYPESAFE_API_KEY", "OPENAI_API_KEY"];
|
|
19
|
+
/**
|
|
20
|
+
* SSM parameters holding the provider keys. A Python function reads its secret()
|
|
21
|
+
* values itself (Amplify only resolves them for Node functions), so it needs read
|
|
22
|
+
* access to the app's branch and shared secret paths, and to the sandbox paths.
|
|
23
|
+
*/
|
|
24
|
+
export function editorialChecksSecretParameters(region, account, appId) {
|
|
25
|
+
const arn = (path) => `arn:aws:ssm:${region}:${account}:parameter/amplify/${path}`;
|
|
26
|
+
return EDITORIAL_CHECKS_SECRETS.flatMap((name) => [
|
|
27
|
+
arn(`papyrus/*/${name}`),
|
|
28
|
+
arn(`shared/papyrus/${name}`),
|
|
29
|
+
...(appId ? [arn(`${appId}/*/${name}`), arn(`shared/${appId}/${name}`)] : []),
|
|
30
|
+
]);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Environment, secrets and least-privilege IAM for the editorial-checks function. Every site gets
|
|
34
|
+
* these; whether checks run is a stored setting (EditorialCheckSettings.enabled), not a deploy flag.
|
|
35
|
+
*/
|
|
36
|
+
export function wireEditorialChecks(backend) {
|
|
37
|
+
const lambda = backend.editorialChecks.resources.lambda;
|
|
38
|
+
const stack = Stack.of(lambda);
|
|
39
|
+
lambda.addEnvironment("PAPYRUS_GRAPHQL_ENDPOINT", backend.data.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl);
|
|
40
|
+
for (const name of EDITORIAL_CHECKS_SECRETS)
|
|
41
|
+
backend.editorialChecks.addEnvironment(name, secret(name));
|
|
42
|
+
lambda.addToRolePolicy(new PolicyStatement({
|
|
43
|
+
effect: Effect.ALLOW,
|
|
44
|
+
actions: ["ssm:GetParameter", "ssm:GetParameters"],
|
|
45
|
+
resources: editorialChecksSecretParameters(stack.region, stack.account, (process.env.AWS_APP_ID ?? "").trim()),
|
|
46
|
+
}));
|
|
47
|
+
const apiId = backend.data.resources.graphqlApi.apiId;
|
|
48
|
+
lambda.addToRolePolicy(new PolicyStatement({
|
|
49
|
+
effect: Effect.ALLOW,
|
|
50
|
+
actions: ["appsync:GraphQL"],
|
|
51
|
+
resources: EDITORIAL_CHECKS_FUNCTION_FIELDS.map((field) => `arn:aws:appsync:${stack.region}:${stack.account}:apis/${apiId}/types/${field}`),
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { secret } from "@aws-amplify/backend";
|
|
2
|
+
import { Stack } from "aws-cdk-lib";
|
|
3
|
+
import { Effect, PolicyStatement } from "aws-cdk-lib/aws-iam";
|
|
4
|
+
import type { Function as LambdaFunction } from "aws-cdk-lib/aws-lambda";
|
|
5
|
+
|
|
6
|
+
/** The model operations the function performs with its own role. The two context reads are its least-privilege access to private policy. */
|
|
7
|
+
export const EDITORIAL_CHECKS_FUNCTION_FIELDS = [
|
|
8
|
+
"Query/fields/listKnowledgeCorporaByRoleAndName",
|
|
9
|
+
"Query/fields/getEditorialContext",
|
|
10
|
+
"Query/fields/listEditorialPolicies",
|
|
11
|
+
"Query/fields/getEditorialCheckSettings",
|
|
12
|
+
"Query/fields/listEditorialFeedbacks",
|
|
13
|
+
"Mutation/fields/createEditorialPolicy",
|
|
14
|
+
"Mutation/fields/updateEditorialPolicy",
|
|
15
|
+
"Query/fields/getItem",
|
|
16
|
+
"Query/fields/getEditorialCheckRun",
|
|
17
|
+
"Mutation/fields/createEditorialCheckRun",
|
|
18
|
+
"Mutation/fields/updateEditorialCheckRun",
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export const EDITORIAL_CHECKS_SECRETS = ["TYPESAFE_API_KEY", "OPENAI_API_KEY"];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* SSM parameters holding the provider keys. A Python function reads its secret()
|
|
25
|
+
* values itself (Amplify only resolves them for Node functions), so it needs read
|
|
26
|
+
* access to the app's branch and shared secret paths, and to the sandbox paths.
|
|
27
|
+
*/
|
|
28
|
+
export function editorialChecksSecretParameters(region: string, account: string, appId: string): string[] {
|
|
29
|
+
const arn = (path: string) => `arn:aws:ssm:${region}:${account}:parameter/amplify/${path}`;
|
|
30
|
+
return EDITORIAL_CHECKS_SECRETS.flatMap((name) => [
|
|
31
|
+
arn(`papyrus/*/${name}`),
|
|
32
|
+
arn(`shared/papyrus/${name}`),
|
|
33
|
+
...(appId ? [arn(`${appId}/*/${name}`), arn(`shared/${appId}/${name}`)] : []),
|
|
34
|
+
]);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Environment, secrets and least-privilege IAM for the editorial-checks function. Every site gets
|
|
39
|
+
* these; whether checks run is a stored setting (EditorialCheckSettings.enabled), not a deploy flag.
|
|
40
|
+
*/
|
|
41
|
+
export function wireEditorialChecks(backend: any): void {
|
|
42
|
+
const lambda = backend.editorialChecks.resources.lambda as LambdaFunction;
|
|
43
|
+
const stack = Stack.of(lambda);
|
|
44
|
+
lambda.addEnvironment("PAPYRUS_GRAPHQL_ENDPOINT", backend.data.resources.cfnResources.cfnGraphqlApi.attrGraphQlUrl as string);
|
|
45
|
+
for (const name of EDITORIAL_CHECKS_SECRETS) backend.editorialChecks.addEnvironment(name, secret(name));
|
|
46
|
+
lambda.addToRolePolicy(
|
|
47
|
+
new PolicyStatement({
|
|
48
|
+
effect: Effect.ALLOW,
|
|
49
|
+
actions: ["ssm:GetParameter", "ssm:GetParameters"],
|
|
50
|
+
resources: editorialChecksSecretParameters(stack.region, stack.account, (process.env.AWS_APP_ID ?? "").trim()),
|
|
51
|
+
}),
|
|
52
|
+
);
|
|
53
|
+
const apiId = backend.data.resources.graphqlApi.apiId as string;
|
|
54
|
+
lambda.addToRolePolicy(
|
|
55
|
+
new PolicyStatement({
|
|
56
|
+
effect: Effect.ALLOW,
|
|
57
|
+
actions: ["appsync:GraphQL"],
|
|
58
|
+
resources: EDITORIAL_CHECKS_FUNCTION_FIELDS.map((field) => `arn:aws:appsync:${stack.region}:${stack.account}:apis/${apiId}/types/${field}`),
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""The monitoring-view function: `monitoringOverview` for editors and admins, read-only."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from papyrus_content.monitoring_view import handle
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def handler(event: dict[str, Any], _context: Any = None) -> dict[str, Any]:
|
|
11
|
+
return handle(event)
|
|
@@ -0,0 +1,24 @@
|
|
|
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 { pythonLambdaCode } from "../shared/python-bundle.js";
|
|
5
|
+
/** AppSync gives a resolver 30 seconds; a read of one small table needs a few. */
|
|
6
|
+
export const MONITORING_VIEW_TIMEOUT_SECONDS = 15;
|
|
7
|
+
/** stdlib plus the runtime's boto3: the bundle is `papyrus_content` and nothing else. */
|
|
8
|
+
export const monitoringView = defineFunction((scope) => new Function(scope, "papyrus-monitoring-view", {
|
|
9
|
+
runtime: Runtime.PYTHON_3_12,
|
|
10
|
+
architecture: Architecture.ARM_64,
|
|
11
|
+
handler: "handler.handler",
|
|
12
|
+
timeout: Duration.seconds(MONITORING_VIEW_TIMEOUT_SECONDS),
|
|
13
|
+
memorySize: 256,
|
|
14
|
+
code: pythonLambdaCode({ functionDir: "monitoring-view", modules: ["papyrus_content"], noDeps: true }, [
|
|
15
|
+
"bash",
|
|
16
|
+
"-c",
|
|
17
|
+
[
|
|
18
|
+
"set -euo pipefail",
|
|
19
|
+
"mkdir -p /asset-output/papyrus_content",
|
|
20
|
+
"cp amplify/functions/monitoring-view/handler.py /asset-output/handler.py",
|
|
21
|
+
"cp -R src/papyrus_content/. /asset-output/papyrus_content/",
|
|
22
|
+
].join(" && "),
|
|
23
|
+
]),
|
|
24
|
+
}), { resourceGroupName: "data" });
|
|
@@ -0,0 +1,34 @@
|
|
|
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 { pythonLambdaCode } from "../shared/python-bundle";
|
|
6
|
+
|
|
7
|
+
/** AppSync gives a resolver 30 seconds; a read of one small table needs a few. */
|
|
8
|
+
export const MONITORING_VIEW_TIMEOUT_SECONDS = 15;
|
|
9
|
+
|
|
10
|
+
/** stdlib plus the runtime's boto3: the bundle is `papyrus_content` and nothing else. */
|
|
11
|
+
export const monitoringView = defineFunction(
|
|
12
|
+
(scope: Construct) =>
|
|
13
|
+
new Function(scope, "papyrus-monitoring-view", {
|
|
14
|
+
runtime: Runtime.PYTHON_3_12,
|
|
15
|
+
architecture: Architecture.ARM_64,
|
|
16
|
+
handler: "handler.handler",
|
|
17
|
+
timeout: Duration.seconds(MONITORING_VIEW_TIMEOUT_SECONDS),
|
|
18
|
+
memorySize: 256,
|
|
19
|
+
code: pythonLambdaCode(
|
|
20
|
+
{ functionDir: "monitoring-view", modules: ["papyrus_content"], noDeps: true },
|
|
21
|
+
[
|
|
22
|
+
"bash",
|
|
23
|
+
"-c",
|
|
24
|
+
[
|
|
25
|
+
"set -euo pipefail",
|
|
26
|
+
"mkdir -p /asset-output/papyrus_content",
|
|
27
|
+
"cp amplify/functions/monitoring-view/handler.py /asset-output/handler.py",
|
|
28
|
+
"cp -R src/papyrus_content/. /asset-output/papyrus_content/",
|
|
29
|
+
].join(" && "),
|
|
30
|
+
],
|
|
31
|
+
),
|
|
32
|
+
}),
|
|
33
|
+
{ resourceGroupName: "data" },
|
|
34
|
+
);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { a } from "@aws-amplify/backend";
|
|
2
|
+
import { monitoringView } from "./resource.js";
|
|
3
|
+
import { functionHandler } from "../../data/function-handler.js";
|
|
4
|
+
const editors = ["editor", "admin"];
|
|
5
|
+
/**
|
|
6
|
+
* Core to every site's data API. The Monitoring page reads the shared monitoring pipeline through
|
|
7
|
+
* this one query. AppSync admits editors and admins only; the function checks the groups again,
|
|
8
|
+
* and returns the whole shared pipeline to admins alone.
|
|
9
|
+
*/
|
|
10
|
+
export const monitoringViewSchemaDefinitions = {
|
|
11
|
+
monitoringOverview: a
|
|
12
|
+
.query()
|
|
13
|
+
.arguments({ scope: a.string() })
|
|
14
|
+
.returns(a.json())
|
|
15
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
16
|
+
.handler(functionHandler("monitoringView", monitoringView)),
|
|
17
|
+
monitoringEmailSubmission: a
|
|
18
|
+
.query()
|
|
19
|
+
.arguments({ receiptId: a.string().required() })
|
|
20
|
+
.returns(a.json())
|
|
21
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
22
|
+
.handler(functionHandler("monitoringView", monitoringView)),
|
|
23
|
+
monitoringResolveEmailArticleLink: a
|
|
24
|
+
.mutation()
|
|
25
|
+
.arguments({ receiptId: a.string().required(), candidateIndex: a.integer().required() })
|
|
26
|
+
.returns(a.json())
|
|
27
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
28
|
+
.handler(functionHandler("monitoringView", monitoringView)),
|
|
29
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { a } from "@aws-amplify/backend";
|
|
2
|
+
import { monitoringView } from "./resource";
|
|
3
|
+
import { functionHandler } from "../../data/function-handler";
|
|
4
|
+
|
|
5
|
+
const editors = ["editor", "admin"];
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Core to every site's data API. The Monitoring page reads the shared monitoring pipeline through
|
|
9
|
+
* this one query. AppSync admits editors and admins only; the function checks the groups again,
|
|
10
|
+
* and returns the whole shared pipeline to admins alone.
|
|
11
|
+
*/
|
|
12
|
+
export const monitoringViewSchemaDefinitions = {
|
|
13
|
+
monitoringOverview: a
|
|
14
|
+
.query()
|
|
15
|
+
.arguments({ scope: a.string() })
|
|
16
|
+
.returns(a.json())
|
|
17
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
18
|
+
.handler(functionHandler("monitoringView", monitoringView)),
|
|
19
|
+
monitoringEmailSubmission: a
|
|
20
|
+
.query()
|
|
21
|
+
.arguments({ receiptId: a.string().required() })
|
|
22
|
+
.returns(a.json())
|
|
23
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
24
|
+
.handler(functionHandler("monitoringView", monitoringView)),
|
|
25
|
+
monitoringResolveEmailArticleLink: a
|
|
26
|
+
.mutation()
|
|
27
|
+
.arguments({ receiptId: a.string().required(), candidateIndex: a.integer().required() })
|
|
28
|
+
.returns(a.json())
|
|
29
|
+
.authorization((allow) => [allow.groups(editors)])
|
|
30
|
+
.handler(functionHandler("monitoringView", monitoringView)),
|
|
31
|
+
};
|