@anthusai/papyrus 5.57.0 → 5.59.0
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/message-actions.js +68 -0
- package/amplify/data/message-actions.ts +70 -0
- package/amplify/data/resolvers/delete-own-message.js +30 -0
- package/amplify/data/resolvers/edit-message.js +33 -0
- package/amplify/data/resolvers/moderate-message.js +32 -0
- package/amplify/data/resolvers/post-message.js +68 -0
- package/amplify/data/resolvers/rename-thread.js +28 -0
- package/amplify/data/resource.js +28 -3
- package/amplify/data/resource.ts +28 -3
- package/amplify/data/schema.js +34 -5
- package/amplify/data/schema.ts +34 -5
- package/amplify/functions/category-action/handler.ts +51 -125
- package/amplify/functions/shared/reference-curation-metadata.ts +21 -29
- package/components/conversation-context.tsx +29 -2
- package/components/conversation-provider.tsx +5 -1
- package/components/conversation-thread.tsx +154 -25
- package/components/conversations-memory-source.ts +83 -0
- package/components/conversations-network-source.ts +52 -0
- package/components/conversations-page.tsx +14 -0
- package/components/conversations-source.ts +36 -0
- package/components/conversations-view.tsx +680 -0
- package/components/decision-control.tsx +307 -0
- package/components/list-detail-layout.tsx +44 -0
- package/components/needs-me-bodies.tsx +18 -17
- package/components/needs-me-feed.tsx +16 -32
- package/components/news-desk-taxonomy-client.ts +216 -62
- package/components/newsroom-article-editor.tsx +10 -4
- package/components/newsroom-organizer.css +34 -1
- package/components/newsroom-references-view.tsx +41 -100
- package/components/notification-cards.tsx +9 -8
- package/components/notification-center.tsx +5 -7
- package/components/proposals-view.tsx +4 -4
- package/components/reference-conversation.tsx +5 -0
- package/components/reference-feed.tsx +129 -127
- package/components/reference-review-card.tsx +9 -135
- package/components/references-page-loading.tsx +12 -6
- package/components/story-reporting-trail-client.ts +4 -3
- package/components/story-reporting-trail.tsx +8 -0
- package/components/topic-steering-workspace.tsx +44 -786
- package/components/use-conversation.ts +36 -2
- package/components/use-feed-preferences.ts +33 -0
- package/components/use-reference-ratings.ts +11 -7
- package/lib/category-repository.ts +14 -38
- package/lib/console-chat-client.ts +8 -178
- package/lib/conversation-feed.ts +96 -0
- package/lib/conversation.ts +73 -2
- package/lib/cyclotron-suggestions.ts +18 -36
- package/lib/local-preview-seed.ts +16 -27
- package/lib/needs-me-items.ts +4 -4
- package/lib/needs-me-ranking.ts +9 -22
- package/lib/newsroom-board.ts +10 -1
- package/lib/newsroom-demo-dashboard.ts +0 -7
- package/lib/newsroom-organizer-nav.ts +2 -2
- package/lib/post-message.ts +54 -0
- package/lib/proposals-admin.ts +2 -2
- package/lib/reference-feed-keys.ts +69 -0
- package/lib/reference-feed-ranking.ts +77 -160
- package/lib/reference-policy.ts +83 -11
- package/lib/reference-review-details.ts +21 -57
- package/lib/relevance-decisions.ts +6 -2
- package/lib/review-drafts.ts +2 -2
- package/lib/review-fixture.ts +1 -1
- package/lib/review-requests.ts +4 -4
- package/lib/semantic-graph.ts +1 -1
- package/lib/story-reporting-trail.ts +28 -2
- package/package.json +12 -2
- package/routes.manifest.json +1 -1
- package/components/review-rating-grid.tsx +0 -190
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { a } from "@aws-amplify/backend";
|
|
2
|
+
/**
|
|
3
|
+
* Messages belong to their author, and the backend says so. The browser cannot create, edit or delete a Message through the
|
|
4
|
+
* model (editors and admins only read it); it calls these four mutations, whose resolvers stamp the author from the signed-in
|
|
5
|
+
* identity and check ownership against the stored `authorSub` in the same DynamoDB write. `renameThread` does the same for a
|
|
6
|
+
* conversation's title. Functions and IAM callers (the router, procedures, the console responder, email, Slack) write the
|
|
7
|
+
* model directly with their own grants, as before.
|
|
8
|
+
*
|
|
9
|
+
* Each resolver is one self-contained file (AppSync takes a single file and offers `@aws-appsync/utils` only).
|
|
10
|
+
*/
|
|
11
|
+
const people = (allow) => [allow.group("editor"), allow.group("admin")];
|
|
12
|
+
const adminsOnly = (allow) => [allow.group("admin")];
|
|
13
|
+
const onMessages = (entry) => a.handler.custom({ dataSource: a.ref("Message"), entry });
|
|
14
|
+
const onThreads = (entry) => a.handler.custom({ dataSource: a.ref("MessageThread"), entry });
|
|
15
|
+
export const messageActionsSchemaDefinitions = {
|
|
16
|
+
postMessage: a
|
|
17
|
+
.mutation()
|
|
18
|
+
.arguments({
|
|
19
|
+
id: a.id().required(),
|
|
20
|
+
threadId: a.id().required(),
|
|
21
|
+
content: a.string(),
|
|
22
|
+
summary: a.string(),
|
|
23
|
+
parentMessageId: a.id(),
|
|
24
|
+
sequenceNumber: a.integer(),
|
|
25
|
+
messageKind: a.string().required(),
|
|
26
|
+
messageDomain: a.string().required(),
|
|
27
|
+
messageType: a.string(),
|
|
28
|
+
role: a.string(),
|
|
29
|
+
source: a.string(),
|
|
30
|
+
authorLabel: a.string(),
|
|
31
|
+
semanticLayer: a.string(),
|
|
32
|
+
searchVisibility: a.string(),
|
|
33
|
+
responseTarget: a.string(),
|
|
34
|
+
responseStatus: a.string(),
|
|
35
|
+
responseOwner: a.string(),
|
|
36
|
+
responseStartedAt: a.datetime(),
|
|
37
|
+
responseCompletedAt: a.datetime(),
|
|
38
|
+
metadata: a.json(),
|
|
39
|
+
newsroomFeedKey: a.string(),
|
|
40
|
+
})
|
|
41
|
+
.returns(a.ref("Message"))
|
|
42
|
+
.authorization(people)
|
|
43
|
+
.handler(onMessages("./resolvers/post-message.js")),
|
|
44
|
+
editMessage: a
|
|
45
|
+
.mutation()
|
|
46
|
+
.arguments({ id: a.id().required(), content: a.string().required(), summary: a.string() })
|
|
47
|
+
.returns(a.ref("Message"))
|
|
48
|
+
.authorization(people)
|
|
49
|
+
.handler(onMessages("./resolvers/edit-message.js")),
|
|
50
|
+
deleteOwnMessage: a
|
|
51
|
+
.mutation()
|
|
52
|
+
.arguments({ id: a.id().required() })
|
|
53
|
+
.returns(a.ref("Message"))
|
|
54
|
+
.authorization(people)
|
|
55
|
+
.handler(onMessages("./resolvers/delete-own-message.js")),
|
|
56
|
+
moderateMessage: a
|
|
57
|
+
.mutation()
|
|
58
|
+
.arguments({ id: a.id().required() })
|
|
59
|
+
.returns(a.ref("Message"))
|
|
60
|
+
.authorization(adminsOnly)
|
|
61
|
+
.handler(onMessages("./resolvers/moderate-message.js")),
|
|
62
|
+
renameThread: a
|
|
63
|
+
.mutation()
|
|
64
|
+
.arguments({ id: a.id().required(), title: a.string().required() })
|
|
65
|
+
.returns(a.ref("MessageThread"))
|
|
66
|
+
.authorization(people)
|
|
67
|
+
.handler(onThreads("./resolvers/rename-thread.js")),
|
|
68
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { a } from "@aws-amplify/backend";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Messages belong to their author, and the backend says so. The browser cannot create, edit or delete a Message through the
|
|
5
|
+
* model (editors and admins only read it); it calls these four mutations, whose resolvers stamp the author from the signed-in
|
|
6
|
+
* identity and check ownership against the stored `authorSub` in the same DynamoDB write. `renameThread` does the same for a
|
|
7
|
+
* conversation's title. Functions and IAM callers (the router, procedures, the console responder, email, Slack) write the
|
|
8
|
+
* model directly with their own grants, as before.
|
|
9
|
+
*
|
|
10
|
+
* Each resolver is one self-contained file (AppSync takes a single file and offers `@aws-appsync/utils` only).
|
|
11
|
+
*/
|
|
12
|
+
const people = (allow: any) => [allow.group("editor"), allow.group("admin")];
|
|
13
|
+
const adminsOnly = (allow: any) => [allow.group("admin")];
|
|
14
|
+
const onMessages = (entry: string) => a.handler.custom({ dataSource: a.ref("Message"), entry });
|
|
15
|
+
const onThreads = (entry: string) => a.handler.custom({ dataSource: a.ref("MessageThread"), entry });
|
|
16
|
+
|
|
17
|
+
export const messageActionsSchemaDefinitions = {
|
|
18
|
+
postMessage: a
|
|
19
|
+
.mutation()
|
|
20
|
+
.arguments({
|
|
21
|
+
id: a.id().required(),
|
|
22
|
+
threadId: a.id().required(),
|
|
23
|
+
content: a.string(),
|
|
24
|
+
summary: a.string(),
|
|
25
|
+
parentMessageId: a.id(),
|
|
26
|
+
sequenceNumber: a.integer(),
|
|
27
|
+
messageKind: a.string().required(),
|
|
28
|
+
messageDomain: a.string().required(),
|
|
29
|
+
messageType: a.string(),
|
|
30
|
+
role: a.string(),
|
|
31
|
+
source: a.string(),
|
|
32
|
+
authorLabel: a.string(),
|
|
33
|
+
semanticLayer: a.string(),
|
|
34
|
+
searchVisibility: a.string(),
|
|
35
|
+
responseTarget: a.string(),
|
|
36
|
+
responseStatus: a.string(),
|
|
37
|
+
responseOwner: a.string(),
|
|
38
|
+
responseStartedAt: a.datetime(),
|
|
39
|
+
responseCompletedAt: a.datetime(),
|
|
40
|
+
metadata: a.json(),
|
|
41
|
+
newsroomFeedKey: a.string(),
|
|
42
|
+
})
|
|
43
|
+
.returns(a.ref("Message"))
|
|
44
|
+
.authorization(people)
|
|
45
|
+
.handler(onMessages("./resolvers/post-message.js")),
|
|
46
|
+
editMessage: a
|
|
47
|
+
.mutation()
|
|
48
|
+
.arguments({ id: a.id().required(), content: a.string().required(), summary: a.string() })
|
|
49
|
+
.returns(a.ref("Message"))
|
|
50
|
+
.authorization(people)
|
|
51
|
+
.handler(onMessages("./resolvers/edit-message.js")),
|
|
52
|
+
deleteOwnMessage: a
|
|
53
|
+
.mutation()
|
|
54
|
+
.arguments({ id: a.id().required() })
|
|
55
|
+
.returns(a.ref("Message"))
|
|
56
|
+
.authorization(people)
|
|
57
|
+
.handler(onMessages("./resolvers/delete-own-message.js")),
|
|
58
|
+
moderateMessage: a
|
|
59
|
+
.mutation()
|
|
60
|
+
.arguments({ id: a.id().required() })
|
|
61
|
+
.returns(a.ref("Message"))
|
|
62
|
+
.authorization(adminsOnly)
|
|
63
|
+
.handler(onMessages("./resolvers/moderate-message.js")),
|
|
64
|
+
renameThread: a
|
|
65
|
+
.mutation()
|
|
66
|
+
.arguments({ id: a.id().required(), title: a.string().required() })
|
|
67
|
+
.returns(a.ref("MessageThread"))
|
|
68
|
+
.authorization(people)
|
|
69
|
+
.handler(onThreads("./resolvers/rename-thread.js")),
|
|
70
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// deleteOwnMessage: the author deletes their own Message. A soft delete: the Message stays (replies keep their place) marked
|
|
2
|
+
// deleted, with when and by whom. Nothing is removed.
|
|
3
|
+
import { util } from "@aws-appsync/utils";
|
|
4
|
+
import * as ddb from "@aws-appsync/utils/dynamodb";
|
|
5
|
+
|
|
6
|
+
export function request(ctx) {
|
|
7
|
+
const identity = ctx.identity;
|
|
8
|
+
if (!identity || !identity.sub) util.error("Sign in to delete a message.", "Unauthorized");
|
|
9
|
+
const now = util.time.nowISO8601();
|
|
10
|
+
return ddb.update({
|
|
11
|
+
key: { id: ctx.args.id },
|
|
12
|
+
update: {
|
|
13
|
+
status: ddb.operations.replace("deleted"),
|
|
14
|
+
deletedAt: ddb.operations.replace(now),
|
|
15
|
+
deletedBySub: ddb.operations.replace(identity.sub),
|
|
16
|
+
updatedAt: ddb.operations.replace(now),
|
|
17
|
+
},
|
|
18
|
+
condition: { authorSub: { eq: identity.sub }, status: { ne: "deleted" } },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function response(ctx) {
|
|
23
|
+
if (ctx.error) {
|
|
24
|
+
if (ctx.error.type === "DynamoDB:ConditionalCheckFailedException") {
|
|
25
|
+
util.error("Only the author can delete their own message, and it may already be deleted.", "Unauthorized");
|
|
26
|
+
}
|
|
27
|
+
util.error(ctx.error.message, ctx.error.type);
|
|
28
|
+
}
|
|
29
|
+
return ctx.result;
|
|
30
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// editMessage: a person changes the text of their own Message, and no one else's. The condition is checked by DynamoDB against the
|
|
2
|
+
// stored authorSub in the same write, so there is no window between the check and the change. A deleted Message cannot be edited.
|
|
3
|
+
// Authorship cannot be reassigned: the update never touches authorSub.
|
|
4
|
+
import { util } from "@aws-appsync/utils";
|
|
5
|
+
import * as ddb from "@aws-appsync/utils/dynamodb";
|
|
6
|
+
|
|
7
|
+
export function request(ctx) {
|
|
8
|
+
const identity = ctx.identity;
|
|
9
|
+
if (!identity || !identity.sub) util.error("Sign in to edit a message.", "Unauthorized");
|
|
10
|
+
const content = typeof ctx.args.content === "string" ? ctx.args.content.trim() : "";
|
|
11
|
+
if (!content) util.error("A message cannot be empty.", "BadRequest");
|
|
12
|
+
const now = util.time.nowISO8601();
|
|
13
|
+
return ddb.update({
|
|
14
|
+
key: { id: ctx.args.id },
|
|
15
|
+
update: {
|
|
16
|
+
content: ddb.operations.replace(content),
|
|
17
|
+
summary: ddb.operations.replace(ctx.args.summary ? ctx.args.summary : content.slice(0, 120)),
|
|
18
|
+
editedAt: ddb.operations.replace(now),
|
|
19
|
+
updatedAt: ddb.operations.replace(now),
|
|
20
|
+
},
|
|
21
|
+
condition: { authorSub: { eq: identity.sub }, status: { ne: "deleted" } },
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function response(ctx) {
|
|
26
|
+
if (ctx.error) {
|
|
27
|
+
if (ctx.error.type === "DynamoDB:ConditionalCheckFailedException") {
|
|
28
|
+
util.error("Only the author can edit a message, and a deleted message cannot be edited.", "Unauthorized");
|
|
29
|
+
}
|
|
30
|
+
util.error(ctx.error.message, ctx.error.type);
|
|
31
|
+
}
|
|
32
|
+
return ctx.result;
|
|
33
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// moderateMessage: an admin removes anyone's Message. The same soft delete as the author's, with no change to the text, and only
|
|
2
|
+
// for the admin group (checked here as well as by the mutation's authorization).
|
|
3
|
+
import { util } from "@aws-appsync/utils";
|
|
4
|
+
import * as ddb from "@aws-appsync/utils/dynamodb";
|
|
5
|
+
|
|
6
|
+
export function request(ctx) {
|
|
7
|
+
const identity = ctx.identity;
|
|
8
|
+
if (!identity || !identity.sub) util.error("Sign in to moderate a message.", "Unauthorized");
|
|
9
|
+
const groups = identity.groups || [];
|
|
10
|
+
if (groups.indexOf("admin") < 0) util.error("Only an admin can delete someone else's message.", "Unauthorized");
|
|
11
|
+
const now = util.time.nowISO8601();
|
|
12
|
+
return ddb.update({
|
|
13
|
+
key: { id: ctx.args.id },
|
|
14
|
+
update: {
|
|
15
|
+
status: ddb.operations.replace("deleted"),
|
|
16
|
+
deletedAt: ddb.operations.replace(now),
|
|
17
|
+
deletedBySub: ddb.operations.replace(identity.sub),
|
|
18
|
+
updatedAt: ddb.operations.replace(now),
|
|
19
|
+
},
|
|
20
|
+
condition: { id: { attributeExists: true }, status: { ne: "deleted" } },
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function response(ctx) {
|
|
25
|
+
if (ctx.error) {
|
|
26
|
+
if (ctx.error.type === "DynamoDB:ConditionalCheckFailedException") {
|
|
27
|
+
util.error("That message does not exist or is already deleted.", "NotFound");
|
|
28
|
+
}
|
|
29
|
+
util.error(ctx.error.message, ctx.error.type);
|
|
30
|
+
}
|
|
31
|
+
return ctx.result;
|
|
32
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// postMessage: the only way a person's Message is created. The author is the signed-in user (the identity's sub), whatever the
|
|
2
|
+
// client sends: the mutation has no author argument, and a label is only a label. One DynamoDB PutItem that refuses to replace an
|
|
3
|
+
// existing Message (ids are derived from the sequence number, so a lost race comes back as a conflict the caller retries).
|
|
4
|
+
import { util } from "@aws-appsync/utils";
|
|
5
|
+
import * as ddb from "@aws-appsync/utils/dynamodb";
|
|
6
|
+
|
|
7
|
+
function clean(value) {
|
|
8
|
+
const out = {};
|
|
9
|
+
const keys = Object.keys(value);
|
|
10
|
+
for (let index = 0; index < keys.length; index++) {
|
|
11
|
+
const key = keys[index];
|
|
12
|
+
if (value[key] !== null && value[key] !== undefined) out[key] = value[key];
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function request(ctx) {
|
|
18
|
+
const identity = ctx.identity;
|
|
19
|
+
if (!identity || !identity.sub) util.error("Sign in to post a message.", "Unauthorized");
|
|
20
|
+
const args = ctx.args;
|
|
21
|
+
const content = typeof args.content === "string" ? args.content.trim() : "";
|
|
22
|
+
// A placeholder waiting for a responder (PENDING) may start empty; any other message needs text.
|
|
23
|
+
if (!content && args.responseStatus !== "PENDING") util.error("A message cannot be empty.", "BadRequest");
|
|
24
|
+
if (!args.id || !args.threadId) util.error("A message needs an id and a conversation.", "BadRequest");
|
|
25
|
+
const now = util.time.nowISO8601();
|
|
26
|
+
const claims = identity.claims || {};
|
|
27
|
+
const email = typeof claims.email === "string" ? claims.email : "";
|
|
28
|
+
const label = typeof args.authorLabel === "string" && args.authorLabel.trim() ? args.authorLabel.trim() : email ? email : "editor";
|
|
29
|
+
const item = clean({
|
|
30
|
+
__typename: "Message",
|
|
31
|
+
id: args.id,
|
|
32
|
+
messageKind: args.messageKind,
|
|
33
|
+
messageDomain: args.messageDomain,
|
|
34
|
+
status: "active",
|
|
35
|
+
summary: args.summary ? args.summary : content.slice(0, 120),
|
|
36
|
+
source: args.source,
|
|
37
|
+
authorSub: identity.sub,
|
|
38
|
+
authorLabel: label,
|
|
39
|
+
threadId: args.threadId,
|
|
40
|
+
parentMessageId: args.parentMessageId,
|
|
41
|
+
sequenceNumber: args.sequenceNumber,
|
|
42
|
+
role: args.role ? args.role : "human",
|
|
43
|
+
messageType: args.messageType,
|
|
44
|
+
content: content,
|
|
45
|
+
semanticLayer: args.semanticLayer,
|
|
46
|
+
searchVisibility: args.searchVisibility,
|
|
47
|
+
responseTarget: args.responseTarget,
|
|
48
|
+
responseStatus: args.responseStatus ? args.responseStatus : "COMPLETED",
|
|
49
|
+
responseOwner: args.responseOwner,
|
|
50
|
+
responseStartedAt: args.responseStartedAt,
|
|
51
|
+
responseCompletedAt: args.responseCompletedAt,
|
|
52
|
+
metadata: args.metadata,
|
|
53
|
+
createdAt: now,
|
|
54
|
+
updatedAt: now,
|
|
55
|
+
newsroomFeedKey: args.newsroomFeedKey,
|
|
56
|
+
});
|
|
57
|
+
return ddb.put({ key: { id: args.id }, item: item, condition: { id: { attributeExists: false } } });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function response(ctx) {
|
|
61
|
+
if (ctx.error) {
|
|
62
|
+
if (ctx.error.type === "DynamoDB:ConditionalCheckFailedException") {
|
|
63
|
+
util.error("A message with this id already exists (conditional check failed); try again.", "Conflict");
|
|
64
|
+
}
|
|
65
|
+
util.error(ctx.error.message, ctx.error.type);
|
|
66
|
+
}
|
|
67
|
+
return ctx.result;
|
|
68
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// renameThread: the person who started a conversation, or an admin, gives it a new title. The browser's own update of a thread
|
|
2
|
+
// cannot change the title (the field is read and create only), so this is the one way. Nothing else about the thread changes.
|
|
3
|
+
import { util } from "@aws-appsync/utils";
|
|
4
|
+
import * as ddb from "@aws-appsync/utils/dynamodb";
|
|
5
|
+
|
|
6
|
+
export function request(ctx) {
|
|
7
|
+
const identity = ctx.identity;
|
|
8
|
+
if (!identity || !identity.sub) util.error("Sign in to rename a conversation.", "Unauthorized");
|
|
9
|
+
const title = typeof ctx.args.title === "string" ? ctx.args.title.trim() : "";
|
|
10
|
+
if (!title) util.error("A title cannot be empty.", "BadRequest");
|
|
11
|
+
const groups = identity.groups || [];
|
|
12
|
+
const isAdmin = groups.indexOf("admin") >= 0;
|
|
13
|
+
return ddb.update({
|
|
14
|
+
key: { id: ctx.args.id },
|
|
15
|
+
update: { title: ddb.operations.replace(title.slice(0, 120)) },
|
|
16
|
+
condition: isAdmin ? { id: { attributeExists: true } } : { createdBySub: { eq: identity.sub } },
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function response(ctx) {
|
|
21
|
+
if (ctx.error) {
|
|
22
|
+
if (ctx.error.type === "DynamoDB:ConditionalCheckFailedException") {
|
|
23
|
+
util.error("Only the person who started this conversation, or an admin, can rename it.", "Unauthorized");
|
|
24
|
+
}
|
|
25
|
+
util.error(ctx.error.message, ctx.error.type);
|
|
26
|
+
}
|
|
27
|
+
return ctx.result;
|
|
28
|
+
}
|
package/amplify/data/resource.js
CHANGED
|
@@ -247,8 +247,6 @@ const schema = a.schema({
|
|
|
247
247
|
referenceId: a.id().required(),
|
|
248
248
|
status: a.string().required(),
|
|
249
249
|
reasonCode: a.string(),
|
|
250
|
-
messageId: a.id(),
|
|
251
|
-
relationId: a.id(),
|
|
252
250
|
}),
|
|
253
251
|
ReferenceQualityActionResult: a.customType({
|
|
254
252
|
ok: a.boolean().required(),
|
|
@@ -1569,8 +1567,26 @@ const schema = a.schema({
|
|
|
1569
1567
|
curationStatusUpdatedAt: a.datetime(),
|
|
1570
1568
|
curationStatusUpdatedBy: a.string(),
|
|
1571
1569
|
curationStatusReason: a.string(),
|
|
1570
|
+
curationReasonCode: a.string(),
|
|
1571
|
+
curationActorKind: a.string(),
|
|
1572
|
+
curationSectionId: a.string(),
|
|
1573
|
+
curationShareable: a.boolean(),
|
|
1574
|
+
curationDelegation: a.json(),
|
|
1575
|
+
curationDecisionRelationId: a.string(),
|
|
1572
1576
|
newsroomFeedKey: a.string(),
|
|
1573
1577
|
reviewedFeedKey: a.string(),
|
|
1578
|
+
discoveredByMonitorId: a.string(),
|
|
1579
|
+
discoveryDetails: a.json(),
|
|
1580
|
+
discoveryRule: a.json(),
|
|
1581
|
+
// The References page reads the feed through two indexes (docs/reference-feed-indexes.md). `feedStateKey` is
|
|
1582
|
+
// "<corpusId>#unrated" or "<corpusId>#rated" for a current, not archived Reference (null otherwise: it is in no feed).
|
|
1583
|
+
// `predictionSortKey` orders Best bets, `feedArrivalAt` orders Newest. The prediction is the Cyclotron's ORIGINAL
|
|
1584
|
+
// P(accept), written once, and never rewritten by a vote.
|
|
1585
|
+
feedStateKey: a.string(),
|
|
1586
|
+
feedArrivalAt: a.datetime(),
|
|
1587
|
+
predictedAcceptProbability: a.float(),
|
|
1588
|
+
predictionDecidedAt: a.datetime(),
|
|
1589
|
+
predictionSortKey: a.string(),
|
|
1574
1590
|
updatedAt: a.datetime(),
|
|
1575
1591
|
})
|
|
1576
1592
|
.secondaryIndexes((index) => [
|
|
@@ -1583,6 +1599,9 @@ const schema = a.schema({
|
|
|
1583
1599
|
index("curationStatus").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusAndUpdatedAt"),
|
|
1584
1600
|
index("curationStatusKey").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusKeyAndUpdatedAt"),
|
|
1585
1601
|
index("reviewedFeedKey").sortKeys(["updatedAt"]).queryField("listReferencesByReviewedFeedAndUpdatedAt"),
|
|
1602
|
+
index("discoveredByMonitorId").sortKeys(["createdAt"]).queryField("listReferencesByMonitorAndCreatedAt"),
|
|
1603
|
+
index("feedStateKey").sortKeys(["predictionSortKey"]).queryField("listReferencesByFeedStateAndPrediction"),
|
|
1604
|
+
index("feedStateKey").sortKeys(["feedArrivalAt"]).queryField("listReferencesByFeedStateAndArrival"),
|
|
1586
1605
|
])
|
|
1587
1606
|
.authorization((allow) => [
|
|
1588
1607
|
allow.groups(categoryWriteGroups).to(["read"]),
|
|
@@ -1670,6 +1689,9 @@ const schema = a.schema({
|
|
|
1670
1689
|
authorSub: a.string(),
|
|
1671
1690
|
authorUserProfileId: a.id(),
|
|
1672
1691
|
authorLabel: a.string(),
|
|
1692
|
+
editedAt: a.datetime(),
|
|
1693
|
+
deletedAt: a.datetime(),
|
|
1694
|
+
deletedBySub: a.string(),
|
|
1673
1695
|
threadId: a.id(),
|
|
1674
1696
|
parentMessageId: a.id(),
|
|
1675
1697
|
sequenceNumber: a.integer(),
|
|
@@ -1704,7 +1726,10 @@ const schema = a.schema({
|
|
|
1704
1726
|
index("searchVisibility").sortKeys(["createdAt"]).name("messagesBySearchVisibilityCreatedAt").queryField("listMessagesBySearchVisibilityAndCreatedAt"),
|
|
1705
1727
|
])
|
|
1706
1728
|
.authorization((allow) => [
|
|
1707
|
-
|
|
1729
|
+
// Messages belong to their author, and the backend stamps the author: people read Messages here and write them only through
|
|
1730
|
+
// postMessage, editMessage, deleteOwnMessage and moderateMessage (message-actions.ts). Functions and IAM callers write directly.
|
|
1731
|
+
allow.group("editor").to(["read"]),
|
|
1732
|
+
allow.group(adminGroup).to(["read"]),
|
|
1708
1733
|
allow.authenticated("identityPool").to(["read", "create", "update"]),
|
|
1709
1734
|
]),
|
|
1710
1735
|
MessageThread: a
|
package/amplify/data/resource.ts
CHANGED
|
@@ -267,8 +267,6 @@ const schema = a.schema({
|
|
|
267
267
|
referenceId: a.id().required(),
|
|
268
268
|
status: a.string().required(),
|
|
269
269
|
reasonCode: a.string(),
|
|
270
|
-
messageId: a.id(),
|
|
271
|
-
relationId: a.id(),
|
|
272
270
|
}),
|
|
273
271
|
|
|
274
272
|
ReferenceQualityActionResult: a.customType({
|
|
@@ -1682,8 +1680,26 @@ const schema = a.schema({
|
|
|
1682
1680
|
curationStatusUpdatedAt: a.datetime(),
|
|
1683
1681
|
curationStatusUpdatedBy: a.string(),
|
|
1684
1682
|
curationStatusReason: a.string(),
|
|
1683
|
+
curationReasonCode: a.string(),
|
|
1684
|
+
curationActorKind: a.string(),
|
|
1685
|
+
curationSectionId: a.string(),
|
|
1686
|
+
curationShareable: a.boolean(),
|
|
1687
|
+
curationDelegation: a.json(),
|
|
1688
|
+
curationDecisionRelationId: a.string(),
|
|
1685
1689
|
newsroomFeedKey: a.string(),
|
|
1686
1690
|
reviewedFeedKey: a.string(),
|
|
1691
|
+
discoveredByMonitorId: a.string(),
|
|
1692
|
+
discoveryDetails: a.json(),
|
|
1693
|
+
discoveryRule: a.json(),
|
|
1694
|
+
// The References page reads the feed through two indexes (docs/reference-feed-indexes.md). `feedStateKey` is
|
|
1695
|
+
// "<corpusId>#unrated" or "<corpusId>#rated" for a current, not archived Reference (null otherwise: it is in no feed).
|
|
1696
|
+
// `predictionSortKey` orders Best bets, `feedArrivalAt` orders Newest. The prediction is the Cyclotron's ORIGINAL
|
|
1697
|
+
// P(accept), written once, and never rewritten by a vote.
|
|
1698
|
+
feedStateKey: a.string(),
|
|
1699
|
+
feedArrivalAt: a.datetime(),
|
|
1700
|
+
predictedAcceptProbability: a.float(),
|
|
1701
|
+
predictionDecidedAt: a.datetime(),
|
|
1702
|
+
predictionSortKey: a.string(),
|
|
1687
1703
|
updatedAt: a.datetime(),
|
|
1688
1704
|
})
|
|
1689
1705
|
.secondaryIndexes((index) => [
|
|
@@ -1696,6 +1712,9 @@ const schema = a.schema({
|
|
|
1696
1712
|
index("curationStatus").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusAndUpdatedAt"),
|
|
1697
1713
|
index("curationStatusKey").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusKeyAndUpdatedAt"),
|
|
1698
1714
|
index("reviewedFeedKey").sortKeys(["updatedAt"]).queryField("listReferencesByReviewedFeedAndUpdatedAt"),
|
|
1715
|
+
index("discoveredByMonitorId").sortKeys(["createdAt"]).queryField("listReferencesByMonitorAndCreatedAt"),
|
|
1716
|
+
index("feedStateKey").sortKeys(["predictionSortKey"]).queryField("listReferencesByFeedStateAndPrediction"),
|
|
1717
|
+
index("feedStateKey").sortKeys(["feedArrivalAt"]).queryField("listReferencesByFeedStateAndArrival"),
|
|
1699
1718
|
])
|
|
1700
1719
|
.authorization((allow) => [
|
|
1701
1720
|
allow.groups(categoryWriteGroups).to(["read"]),
|
|
@@ -1786,6 +1805,9 @@ const schema = a.schema({
|
|
|
1786
1805
|
authorSub: a.string(),
|
|
1787
1806
|
authorUserProfileId: a.id(),
|
|
1788
1807
|
authorLabel: a.string(),
|
|
1808
|
+
editedAt: a.datetime(),
|
|
1809
|
+
deletedAt: a.datetime(),
|
|
1810
|
+
deletedBySub: a.string(),
|
|
1789
1811
|
threadId: a.id(),
|
|
1790
1812
|
parentMessageId: a.id(),
|
|
1791
1813
|
sequenceNumber: a.integer(),
|
|
@@ -1820,7 +1842,10 @@ const schema = a.schema({
|
|
|
1820
1842
|
index("searchVisibility").sortKeys(["createdAt"]).name("messagesBySearchVisibilityCreatedAt").queryField("listMessagesBySearchVisibilityAndCreatedAt"),
|
|
1821
1843
|
])
|
|
1822
1844
|
.authorization((allow) => [
|
|
1823
|
-
|
|
1845
|
+
// Messages belong to their author, and the backend stamps the author: people read Messages here and write them only through
|
|
1846
|
+
// postMessage, editMessage, deleteOwnMessage and moderateMessage (message-actions.ts). Functions and IAM callers write directly.
|
|
1847
|
+
allow.group("editor").to(["read"]),
|
|
1848
|
+
allow.group(adminGroup).to(["read"]),
|
|
1824
1849
|
allow.authenticated("identityPool").to(["read", "create", "update"]),
|
|
1825
1850
|
]),
|
|
1826
1851
|
|
package/amplify/data/schema.js
CHANGED
|
@@ -4,6 +4,7 @@ import { categoryAction } from "../functions/category-action/resource.js";
|
|
|
4
4
|
import { editorialChecksSchemaDefinitions } from "../functions/editorial-checks/schema.js";
|
|
5
5
|
import { automationViewSchemaDefinitions } from "../functions/automation-view/schema.js";
|
|
6
6
|
import { proposalActionsSchemaDefinitions } from "../functions/proposal-actions/schema.js";
|
|
7
|
+
import { messageActionsSchemaDefinitions } from "./message-actions.js";
|
|
7
8
|
import { monitoringViewSchemaDefinitions } from "../functions/monitoring-view/schema.js";
|
|
8
9
|
import { contentActions } from "../functions/content-actions/resource.js";
|
|
9
10
|
import { knowledgeQuery } from "../functions/knowledge-query/resource.js";
|
|
@@ -259,8 +260,6 @@ function buildBaseDefinitions(includeEditions) {
|
|
|
259
260
|
referenceId: a.id().required(),
|
|
260
261
|
status: a.string().required(),
|
|
261
262
|
reasonCode: a.string(),
|
|
262
|
-
messageId: a.id(),
|
|
263
|
-
relationId: a.id(),
|
|
264
263
|
}),
|
|
265
264
|
ReferenceQualityActionResult: a.customType({
|
|
266
265
|
ok: a.boolean().required(),
|
|
@@ -1475,8 +1474,26 @@ function buildBaseDefinitions(includeEditions) {
|
|
|
1475
1474
|
curationStatusUpdatedAt: a.datetime(),
|
|
1476
1475
|
curationStatusUpdatedBy: a.string(),
|
|
1477
1476
|
curationStatusReason: a.string(),
|
|
1477
|
+
curationReasonCode: a.string(),
|
|
1478
|
+
curationActorKind: a.string(),
|
|
1479
|
+
curationSectionId: a.string(),
|
|
1480
|
+
curationShareable: a.boolean(),
|
|
1481
|
+
curationDelegation: a.json(),
|
|
1482
|
+
curationDecisionRelationId: a.string(),
|
|
1478
1483
|
newsroomFeedKey: a.string(),
|
|
1479
1484
|
reviewedFeedKey: a.string(),
|
|
1485
|
+
discoveredByMonitorId: a.string(),
|
|
1486
|
+
discoveryDetails: a.json(),
|
|
1487
|
+
discoveryRule: a.json(),
|
|
1488
|
+
// The References page reads the feed through two indexes (docs/reference-feed-indexes.md). `feedStateKey` is
|
|
1489
|
+
// "<corpusId>#unrated" or "<corpusId>#rated" for a current, not archived Reference (null otherwise: it is in no feed).
|
|
1490
|
+
// `predictionSortKey` orders Best bets, `feedArrivalAt` orders Newest. The prediction is the Cyclotron's ORIGINAL
|
|
1491
|
+
// P(accept), written once, and never rewritten by a vote.
|
|
1492
|
+
feedStateKey: a.string(),
|
|
1493
|
+
feedArrivalAt: a.datetime(),
|
|
1494
|
+
predictedAcceptProbability: a.float(),
|
|
1495
|
+
predictionDecidedAt: a.datetime(),
|
|
1496
|
+
predictionSortKey: a.string(),
|
|
1480
1497
|
updatedAt: a.datetime(),
|
|
1481
1498
|
})
|
|
1482
1499
|
.secondaryIndexes((index) => [
|
|
@@ -1489,6 +1506,9 @@ function buildBaseDefinitions(includeEditions) {
|
|
|
1489
1506
|
index("curationStatus").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusAndUpdatedAt"),
|
|
1490
1507
|
index("curationStatusKey").sortKeys(["curationStatusUpdatedAt"]).queryField("listReferencesByCurationStatusKeyAndUpdatedAt"),
|
|
1491
1508
|
index("reviewedFeedKey").sortKeys(["updatedAt"]).queryField("listReferencesByReviewedFeedAndUpdatedAt"),
|
|
1509
|
+
index("discoveredByMonitorId").sortKeys(["createdAt"]).queryField("listReferencesByMonitorAndCreatedAt"),
|
|
1510
|
+
index("feedStateKey").sortKeys(["predictionSortKey"]).queryField("listReferencesByFeedStateAndPrediction"),
|
|
1511
|
+
index("feedStateKey").sortKeys(["feedArrivalAt"]).queryField("listReferencesByFeedStateAndArrival"),
|
|
1492
1512
|
])
|
|
1493
1513
|
.authorization((allow) => [
|
|
1494
1514
|
allow.groups(categoryWriteGroups).to(["read"]),
|
|
@@ -1573,6 +1593,9 @@ function buildBaseDefinitions(includeEditions) {
|
|
|
1573
1593
|
authorSub: a.string(),
|
|
1574
1594
|
authorUserProfileId: a.id(),
|
|
1575
1595
|
authorLabel: a.string(),
|
|
1596
|
+
editedAt: a.datetime(),
|
|
1597
|
+
deletedAt: a.datetime(),
|
|
1598
|
+
deletedBySub: a.string(),
|
|
1576
1599
|
threadId: a.id(),
|
|
1577
1600
|
parentMessageId: a.id(),
|
|
1578
1601
|
sequenceNumber: a.integer(),
|
|
@@ -1607,19 +1630,24 @@ function buildBaseDefinitions(includeEditions) {
|
|
|
1607
1630
|
index("searchVisibility").sortKeys(["createdAt"]).name("messagesBySearchVisibilityCreatedAt").queryField("listMessagesBySearchVisibilityAndCreatedAt"),
|
|
1608
1631
|
])
|
|
1609
1632
|
.authorization((allow) => [
|
|
1610
|
-
|
|
1633
|
+
// Messages belong to their author, and the backend stamps the author: people read Messages here and write them only through
|
|
1634
|
+
// postMessage, editMessage, deleteOwnMessage and moderateMessage (message-actions.ts). Functions and IAM callers write directly.
|
|
1635
|
+
allow.group("editor").to(["read"]),
|
|
1636
|
+
allow.group(adminGroup).to(["read"]),
|
|
1611
1637
|
]),
|
|
1612
1638
|
MessageThread: realtimeModel({
|
|
1613
1639
|
id: a.id().required(),
|
|
1614
1640
|
threadKind: a.string().required(),
|
|
1615
1641
|
status: a.string().required(),
|
|
1616
|
-
title
|
|
1642
|
+
// The title and the creator are set when a conversation is created and read afterwards; a title changes only through
|
|
1643
|
+
// renameThread (its creator or an admin), and the creator never changes.
|
|
1644
|
+
title: a.string().required().authorization((allow) => [allow.group("editor").to(["read", "create"]), allow.group(adminGroup).to(["read", "create"])]),
|
|
1617
1645
|
summary: a.string(),
|
|
1618
1646
|
primaryAnchorKind: a.string(),
|
|
1619
1647
|
primaryAnchorId: a.id(),
|
|
1620
1648
|
primaryAnchorLineageId: a.id(),
|
|
1621
1649
|
primaryAnchorKey: a.string(),
|
|
1622
|
-
createdBySub: a.string(),
|
|
1650
|
+
createdBySub: a.string().authorization((allow) => [allow.group("editor").to(["read", "create"]), allow.group(adminGroup).to(["read", "create"])]),
|
|
1623
1651
|
createdByUserProfileId: a.id(),
|
|
1624
1652
|
createdByLabel: a.string(),
|
|
1625
1653
|
messageCount: a.integer(),
|
|
@@ -2104,6 +2132,7 @@ function buildBaseDefinitions(includeEditions) {
|
|
|
2104
2132
|
...monitoringViewSchemaDefinitions,
|
|
2105
2133
|
...automationViewSchemaDefinitions,
|
|
2106
2134
|
...proposalActionsSchemaDefinitions,
|
|
2135
|
+
...messageActionsSchemaDefinitions,
|
|
2107
2136
|
};
|
|
2108
2137
|
if (!includeEditions) {
|
|
2109
2138
|
const mutableDefinitions = definitions;
|