@firedrill-tools/linkedin 0.1.1
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/LICENSE +201 -0
- package/README.md +189 -0
- package/firedrill/agent.target.json +16 -0
- package/firedrill/baseline.scenario.json +1834 -0
- package/firedrill/comment-throttled.scenario.json +11 -0
- package/firedrill/conformance.suite.json +27 -0
- package/firedrill/linkedin-comment-throttled.drill.json +51 -0
- package/firedrill/linkedin-comments-connection.drill.json +43 -0
- package/firedrill/linkedin-comments.drill.json +196 -0
- package/firedrill/linkedin-denied.drill.json +383 -0
- package/firedrill/linkedin-fresh-install.drill.json +43 -0
- package/firedrill/linkedin-identity.drill.json +73 -0
- package/firedrill/linkedin-invalid-token.drill.json +393 -0
- package/firedrill/linkedin-limited-scopes.drill.json +88 -0
- package/firedrill/linkedin-openid-only.drill.json +383 -0
- package/firedrill/linkedin-orgs-connection.drill.json +28 -0
- package/firedrill/linkedin-orgs-network.drill.json +193 -0
- package/firedrill/linkedin-post-lost-response.drill.json +61 -0
- package/firedrill/linkedin-posts-lifecycle.drill.json +228 -0
- package/firedrill/linkedin-reactions-metadata.drill.json +196 -0
- package/firedrill/linkedin-reactions-outage.drill.json +66 -0
- package/firedrill/linkedin-tight-limits.drill.json +228 -0
- package/firedrill/linkedin-ugc-legacy.drill.json +43 -0
- package/firedrill/linkedin-visibility-connection.drill.json +73 -0
- package/firedrill/linkedin-visibility-stranger.drill.json +88 -0
- package/firedrill/linkedin-wire-errors.drill.json +353 -0
- package/firedrill/post-lost-response.scenario.json +11 -0
- package/firedrill/reactions-outage.scenario.json +11 -0
- package/firedrill/tight-limits.scenario.json +16 -0
- package/firedrill/tools/linkedin/app/assets/ATTRIBUTION.md +28 -0
- package/firedrill/tools/linkedin/app/assets/fonts/OFL.txt +93 -0
- package/firedrill/tools/linkedin/app/assets/linkedin-in.svg +1 -0
- package/firedrill/tools/linkedin/app/assets/linkedin-wordmark.svg +1 -0
- package/firedrill/tools/linkedin/app/assets/linkedin.svg +1 -0
- package/firedrill/tools/linkedin/app/site/app.js +80 -0
- package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-400-normal.woff2 +0 -0
- package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-600-normal.woff2 +0 -0
- package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-700-normal.woff2 +0 -0
- package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-ext-400-normal.woff2 +0 -0
- package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-ext-600-normal.woff2 +0 -0
- package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-ext-700-normal.woff2 +0 -0
- package/firedrill/tools/linkedin/app/site/assets/linkedin-wordmark.svg +1 -0
- package/firedrill/tools/linkedin/app/site/assets/linkedin.svg +1 -0
- package/firedrill/tools/linkedin/app/site/base.css +87 -0
- package/firedrill/tools/linkedin/app/site/comment-box.js +119 -0
- package/firedrill/tools/linkedin/app/site/comments.css +59 -0
- package/firedrill/tools/linkedin/app/site/comments.js +188 -0
- package/firedrill/tools/linkedin/app/site/company.js +115 -0
- package/firedrill/tools/linkedin/app/site/composer.js +112 -0
- package/firedrill/tools/linkedin/app/site/feed.css +105 -0
- package/firedrill/tools/linkedin/app/site/feed.js +134 -0
- package/firedrill/tools/linkedin/app/site/icons.js +70 -0
- package/firedrill/tools/linkedin/app/site/index.html +58 -0
- package/firedrill/tools/linkedin/app/site/nav.css +51 -0
- package/firedrill/tools/linkedin/app/site/network.js +77 -0
- package/firedrill/tools/linkedin/app/site/overlay.css +62 -0
- package/firedrill/tools/linkedin/app/site/overlay.js +108 -0
- package/firedrill/tools/linkedin/app/site/pages.css +78 -0
- package/firedrill/tools/linkedin/app/site/post-actions.js +83 -0
- package/firedrill/tools/linkedin/app/site/post-menu.js +72 -0
- package/firedrill/tools/linkedin/app/site/post.css +82 -0
- package/firedrill/tools/linkedin/app/site/post.js +131 -0
- package/firedrill/tools/linkedin/app/site/profile.js +139 -0
- package/firedrill/tools/linkedin/app/site/reactions.js +46 -0
- package/firedrill/tools/linkedin/app/site/reactors.js +71 -0
- package/firedrill/tools/linkedin/app/site/shell.js +107 -0
- package/firedrill/tools/linkedin/app/site/store.js +62 -0
- package/firedrill/tools/linkedin/app/site/text.js +80 -0
- package/firedrill/tools/linkedin/app/site/ui.js +142 -0
- package/firedrill/tools/linkedin/behavior.mjs +66 -0
- package/firedrill/tools/linkedin/lib/access.mjs +69 -0
- package/firedrill/tools/linkedin/lib/auth.mjs +115 -0
- package/firedrill/tools/linkedin/lib/cascade.mjs +28 -0
- package/firedrill/tools/linkedin/lib/errors.mjs +50 -0
- package/firedrill/tools/linkedin/lib/message.mjs +43 -0
- package/firedrill/tools/linkedin/lib/paging.mjs +54 -0
- package/firedrill/tools/linkedin/lib/postinput.mjs +47 -0
- package/firedrill/tools/linkedin/lib/render.mjs +95 -0
- package/firedrill/tools/linkedin/lib/routes.mjs +219 -0
- package/firedrill/tools/linkedin/lib/social.mjs +25 -0
- package/firedrill/tools/linkedin/lib/store.mjs +54 -0
- package/firedrill/tools/linkedin/lib/text.mjs +55 -0
- package/firedrill/tools/linkedin/lib/urn.mjs +93 -0
- package/firedrill/tools/linkedin/lib/wire.mjs +125 -0
- package/firedrill/tools/linkedin/linkedin.tool.json +8935 -0
- package/firedrill/tools/linkedin/ops/comments-read.mjs +57 -0
- package/firedrill/tools/linkedin/ops/comments-write.mjs +104 -0
- package/firedrill/tools/linkedin/ops/feed.mjs +74 -0
- package/firedrill/tools/linkedin/ops/network.mjs +46 -0
- package/firedrill/tools/linkedin/ops/orgs.mjs +93 -0
- package/firedrill/tools/linkedin/ops/posts-edit.mjs +71 -0
- package/firedrill/tools/linkedin/ops/posts-read.mjs +54 -0
- package/firedrill/tools/linkedin/ops/posts-write.mjs +74 -0
- package/firedrill/tools/linkedin/ops/profile.mjs +38 -0
- package/firedrill/tools/linkedin/ops/reactions.mjs +78 -0
- package/firedrill/tools/linkedin/ops/social.mjs +34 -0
- package/firedrill/world.json +2585 -0
- package/firedrill.json +5 -0
- package/package.json +52 -0
- package/starter.json +1833 -0
- package/test/client.mjs +71 -0
- package/test/conformance.mjs +20 -0
- package/test/fixtures.mjs +182 -0
- package/test/flows/comments.mjs +116 -0
- package/test/flows/every.mjs +38 -0
- package/test/flows/faults.mjs +56 -0
- package/test/flows/identity.mjs +70 -0
- package/test/flows/orgs.mjs +107 -0
- package/test/flows/posts.mjs +196 -0
- package/test/flows/reactions.mjs +101 -0
- package/test/flows/wire.mjs +62 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// reactions.create (create or replace), reactions.list (compound-key get and entity finder) and reactions.delete.
|
|
2
|
+
import { loadThread } from "../lib/access.mjs";
|
|
3
|
+
import { actingAs, caller, checkWire, urnField } from "../lib/auth.mjs";
|
|
4
|
+
import { reactionsOn } from "../lib/cascade.mjs";
|
|
5
|
+
import { invalidValue, missing, notFound } from "../lib/errors.mjs";
|
|
6
|
+
import { buildPage, pageArgs } from "../lib/paging.mjs";
|
|
7
|
+
import { renderReaction } from "../lib/render.mjs";
|
|
8
|
+
import { nowMs } from "../lib/store.mjs";
|
|
9
|
+
import { activityUrn, commentUrn, reactionId } from "../lib/urn.mjs";
|
|
10
|
+
|
|
11
|
+
export const REACTION_TYPES = new Set(["LIKE", "PRAISE", "APPRECIATION", "EMPATHY", "INTEREST", "ENTERTAINMENT"]);
|
|
12
|
+
const SORTS = new Set(["CHRONOLOGICAL", "REVERSE_CHRONOLOGICAL", "RELEVANCE"]);
|
|
13
|
+
|
|
14
|
+
/** Resolves a reaction entity (post, activity or comment URN) to its stored entity URN; drafts are not reactable. */
|
|
15
|
+
function entityOf(context, who, field, value, resource, write) {
|
|
16
|
+
const { post, comment } = loadThread(context, who, field, value, resource, { write });
|
|
17
|
+
if (post.lifecycleState === "DRAFT") notFound(context, "Not Found");
|
|
18
|
+
return comment === null ? activityUrn(post.activityId) : commentUrn(comment.activityId, comment.id);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createReaction(input, context) {
|
|
22
|
+
const who = caller(context);
|
|
23
|
+
checkWire(context, input, "CREATE");
|
|
24
|
+
if (input.actor === undefined || input.actor === null) missing(context, "actor");
|
|
25
|
+
const actor = actingAs(context, who, "actor", input.actor, "POST /reactions");
|
|
26
|
+
if (input.root === undefined || input.root === null) missing(context, "root");
|
|
27
|
+
const entity = entityOf(context, who, "root", input.root, "POST /reactions", true);
|
|
28
|
+
const type = input.reactionType;
|
|
29
|
+
if (type === undefined || type === null) missing(context, "reactionType");
|
|
30
|
+
if (type === "MAYBE") invalidValue(context, "reactionType", "MAYBE (deprecated)");
|
|
31
|
+
if (typeof type !== "string" || !REACTION_TYPES.has(type)) invalidValue(context, "reactionType", type);
|
|
32
|
+
const rowId = `${entity}|${actor.urn}`;
|
|
33
|
+
const existing = context.state.get("reactions", rowId);
|
|
34
|
+
if (existing !== null && existing.reactionType === type) return renderReaction(existing);
|
|
35
|
+
const now = nowMs(context);
|
|
36
|
+
const row = existing === null
|
|
37
|
+
? { entity, actor: actor.urn, reactionType: type, impersonator: actor.agent, createdAtMs: now, lastModifiedAtMs: now }
|
|
38
|
+
: { ...existing, reactionType: type, impersonator: actor.agent, lastModifiedAtMs: now };
|
|
39
|
+
context.state.put("reactions", rowId, row);
|
|
40
|
+
context.events.emit("reaction.created", {
|
|
41
|
+
reactionId: reactionId(actor.urn, entity), root: entity, actor: actor.urn, reactionType: type,
|
|
42
|
+
previousReactionType: existing === null ? null : existing.reactionType, created: now,
|
|
43
|
+
});
|
|
44
|
+
return renderReaction(row);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function listReactions(input, context) {
|
|
48
|
+
const who = caller(context);
|
|
49
|
+
const single = input.single === true;
|
|
50
|
+
checkWire(context, input, single ? "GET" : "FINDER");
|
|
51
|
+
const entity = entityOf(context, who, "entity", input.entity, "GET /reactions", false);
|
|
52
|
+
if (single) {
|
|
53
|
+
const actor = urnField(context, "actor", input.actor, ["person", "organization"]);
|
|
54
|
+
const row = context.state.get("reactions", `${entity}|${actor.urn}`);
|
|
55
|
+
if (row === null) notFound(context, "Not Found");
|
|
56
|
+
return { paging: { start: 0, count: 1, links: [], total: 1 }, elements: [renderReaction(row)] };
|
|
57
|
+
}
|
|
58
|
+
const sort = input.sort ?? "REVERSE_CHRONOLOGICAL";
|
|
59
|
+
if (typeof sort !== "string" || !SORTS.has(sort)) invalidValue(context, "sort", sort);
|
|
60
|
+
const { start, count } = pageArgs(context, input);
|
|
61
|
+
const direction = sort === "CHRONOLOGICAL" ? 1 : -1;
|
|
62
|
+
const rows = reactionsOn(context, entity)
|
|
63
|
+
.sort((a, b) => direction * (a.createdAtMs - b.createdAtMs) || (a.actor < b.actor ? -1 : a.actor > b.actor ? 1 : 0));
|
|
64
|
+
return buildPage(context, rows, renderReaction, {
|
|
65
|
+
start, count, path: `/rest/reactions/(entity:${entity})`, query: [["q", "entity"], ["sort", `(value:${sort})`]],
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function deleteReaction(input, context) {
|
|
70
|
+
const who = caller(context);
|
|
71
|
+
checkWire(context, input, "DELETE");
|
|
72
|
+
const actor = actingAs(context, who, "actor", input.actor, "DELETE /reactions");
|
|
73
|
+
const entity = entityOf(context, who, "entity", input.entity, "DELETE /reactions", true);
|
|
74
|
+
const rowId = `${entity}|${actor.urn}`;
|
|
75
|
+
if (context.state.get("reactions", rowId) === null) notFound(context, "Not Found");
|
|
76
|
+
context.state.delete("reactions", rowId);
|
|
77
|
+
return { id: reactionId(actor.urn, entity), deleted: true };
|
|
78
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// social_metadata.get and social_metadata.set_comments_state.
|
|
2
|
+
import { loadThread } from "../lib/access.mjs";
|
|
3
|
+
import { actingAs, caller, checkWire } from "../lib/auth.mjs";
|
|
4
|
+
import { deleteComments, threadComments } from "../lib/cascade.mjs";
|
|
5
|
+
import { fail, invalidValue, missing, notFound } from "../lib/errors.mjs";
|
|
6
|
+
import { reactionSummaries, summarize } from "../lib/social.mjs";
|
|
7
|
+
|
|
8
|
+
export function getSocialMetadata(input, context) {
|
|
9
|
+
const who = caller(context);
|
|
10
|
+
checkWire(context, input, "GET");
|
|
11
|
+
const { post, comment, parsed } = loadThread(context, who, "entity", input.entity, "GET /socialMetadata", { inPath: input.wire !== undefined });
|
|
12
|
+
if (post.lifecycleState === "DRAFT") notFound(context, "Not Found");
|
|
13
|
+
const { commentSummary, reactions } = summarize(context, post, comment);
|
|
14
|
+
return { entity: parsed.urn, commentsState: post.commentsState, commentSummary, reactionSummaries: reactionSummaries(reactions) };
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function setCommentsState(input, context) {
|
|
18
|
+
const who = caller(context);
|
|
19
|
+
checkWire(context, input, "PARTIAL_UPDATE");
|
|
20
|
+
const { post, parsed } = loadThread(context, who, "entity", input.entity, "POST /socialMetadata",
|
|
21
|
+
{ inPath: input.wire !== undefined, types: ["share", "ugcPost", "activity"], write: true });
|
|
22
|
+
if (typeof input.wire?.fieldError === "string") invalidValue(context, input.wire.fieldError, "an unsupported patch");
|
|
23
|
+
if (input.actor === undefined || input.actor === null) missing(context, "actor");
|
|
24
|
+
const actor = actingAs(context, who, "actor", input.actor, "POST /socialMetadata");
|
|
25
|
+
const state = input.commentsState;
|
|
26
|
+
if (state === undefined || state === null) missing(context, "commentsState");
|
|
27
|
+
if (state !== "OPEN" && state !== "CLOSED") invalidValue(context, "commentsState", state);
|
|
28
|
+
if (post.lifecycleState === "DRAFT") notFound(context, "Not Found");
|
|
29
|
+
if (actor.urn !== post.author) fail(context, "ACCESS_DENIED", "Only the author of the post can change its comments state");
|
|
30
|
+
if (post.commentsState === state) return { entity: parsed.urn, commentsState: state, deletedComments: 0 };
|
|
31
|
+
const deletedComments = state === "CLOSED" ? deleteComments(context, threadComments(context, post.activityId)) : 0;
|
|
32
|
+
context.state.put("posts", post.id, { ...post, commentsState: state });
|
|
33
|
+
return { entity: parsed.urn, commentsState: state, deletedComments };
|
|
34
|
+
}
|