@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.
Files changed (111) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +189 -0
  3. package/firedrill/agent.target.json +16 -0
  4. package/firedrill/baseline.scenario.json +1834 -0
  5. package/firedrill/comment-throttled.scenario.json +11 -0
  6. package/firedrill/conformance.suite.json +27 -0
  7. package/firedrill/linkedin-comment-throttled.drill.json +51 -0
  8. package/firedrill/linkedin-comments-connection.drill.json +43 -0
  9. package/firedrill/linkedin-comments.drill.json +196 -0
  10. package/firedrill/linkedin-denied.drill.json +383 -0
  11. package/firedrill/linkedin-fresh-install.drill.json +43 -0
  12. package/firedrill/linkedin-identity.drill.json +73 -0
  13. package/firedrill/linkedin-invalid-token.drill.json +393 -0
  14. package/firedrill/linkedin-limited-scopes.drill.json +88 -0
  15. package/firedrill/linkedin-openid-only.drill.json +383 -0
  16. package/firedrill/linkedin-orgs-connection.drill.json +28 -0
  17. package/firedrill/linkedin-orgs-network.drill.json +193 -0
  18. package/firedrill/linkedin-post-lost-response.drill.json +61 -0
  19. package/firedrill/linkedin-posts-lifecycle.drill.json +228 -0
  20. package/firedrill/linkedin-reactions-metadata.drill.json +196 -0
  21. package/firedrill/linkedin-reactions-outage.drill.json +66 -0
  22. package/firedrill/linkedin-tight-limits.drill.json +228 -0
  23. package/firedrill/linkedin-ugc-legacy.drill.json +43 -0
  24. package/firedrill/linkedin-visibility-connection.drill.json +73 -0
  25. package/firedrill/linkedin-visibility-stranger.drill.json +88 -0
  26. package/firedrill/linkedin-wire-errors.drill.json +353 -0
  27. package/firedrill/post-lost-response.scenario.json +11 -0
  28. package/firedrill/reactions-outage.scenario.json +11 -0
  29. package/firedrill/tight-limits.scenario.json +16 -0
  30. package/firedrill/tools/linkedin/app/assets/ATTRIBUTION.md +28 -0
  31. package/firedrill/tools/linkedin/app/assets/fonts/OFL.txt +93 -0
  32. package/firedrill/tools/linkedin/app/assets/linkedin-in.svg +1 -0
  33. package/firedrill/tools/linkedin/app/assets/linkedin-wordmark.svg +1 -0
  34. package/firedrill/tools/linkedin/app/assets/linkedin.svg +1 -0
  35. package/firedrill/tools/linkedin/app/site/app.js +80 -0
  36. package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-400-normal.woff2 +0 -0
  37. package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-600-normal.woff2 +0 -0
  38. package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-700-normal.woff2 +0 -0
  39. package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-ext-400-normal.woff2 +0 -0
  40. package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-ext-600-normal.woff2 +0 -0
  41. package/firedrill/tools/linkedin/app/site/assets/fonts/fira-sans-latin-ext-700-normal.woff2 +0 -0
  42. package/firedrill/tools/linkedin/app/site/assets/linkedin-wordmark.svg +1 -0
  43. package/firedrill/tools/linkedin/app/site/assets/linkedin.svg +1 -0
  44. package/firedrill/tools/linkedin/app/site/base.css +87 -0
  45. package/firedrill/tools/linkedin/app/site/comment-box.js +119 -0
  46. package/firedrill/tools/linkedin/app/site/comments.css +59 -0
  47. package/firedrill/tools/linkedin/app/site/comments.js +188 -0
  48. package/firedrill/tools/linkedin/app/site/company.js +115 -0
  49. package/firedrill/tools/linkedin/app/site/composer.js +112 -0
  50. package/firedrill/tools/linkedin/app/site/feed.css +105 -0
  51. package/firedrill/tools/linkedin/app/site/feed.js +134 -0
  52. package/firedrill/tools/linkedin/app/site/icons.js +70 -0
  53. package/firedrill/tools/linkedin/app/site/index.html +58 -0
  54. package/firedrill/tools/linkedin/app/site/nav.css +51 -0
  55. package/firedrill/tools/linkedin/app/site/network.js +77 -0
  56. package/firedrill/tools/linkedin/app/site/overlay.css +62 -0
  57. package/firedrill/tools/linkedin/app/site/overlay.js +108 -0
  58. package/firedrill/tools/linkedin/app/site/pages.css +78 -0
  59. package/firedrill/tools/linkedin/app/site/post-actions.js +83 -0
  60. package/firedrill/tools/linkedin/app/site/post-menu.js +72 -0
  61. package/firedrill/tools/linkedin/app/site/post.css +82 -0
  62. package/firedrill/tools/linkedin/app/site/post.js +131 -0
  63. package/firedrill/tools/linkedin/app/site/profile.js +139 -0
  64. package/firedrill/tools/linkedin/app/site/reactions.js +46 -0
  65. package/firedrill/tools/linkedin/app/site/reactors.js +71 -0
  66. package/firedrill/tools/linkedin/app/site/shell.js +107 -0
  67. package/firedrill/tools/linkedin/app/site/store.js +62 -0
  68. package/firedrill/tools/linkedin/app/site/text.js +80 -0
  69. package/firedrill/tools/linkedin/app/site/ui.js +142 -0
  70. package/firedrill/tools/linkedin/behavior.mjs +66 -0
  71. package/firedrill/tools/linkedin/lib/access.mjs +69 -0
  72. package/firedrill/tools/linkedin/lib/auth.mjs +115 -0
  73. package/firedrill/tools/linkedin/lib/cascade.mjs +28 -0
  74. package/firedrill/tools/linkedin/lib/errors.mjs +50 -0
  75. package/firedrill/tools/linkedin/lib/message.mjs +43 -0
  76. package/firedrill/tools/linkedin/lib/paging.mjs +54 -0
  77. package/firedrill/tools/linkedin/lib/postinput.mjs +47 -0
  78. package/firedrill/tools/linkedin/lib/render.mjs +95 -0
  79. package/firedrill/tools/linkedin/lib/routes.mjs +219 -0
  80. package/firedrill/tools/linkedin/lib/social.mjs +25 -0
  81. package/firedrill/tools/linkedin/lib/store.mjs +54 -0
  82. package/firedrill/tools/linkedin/lib/text.mjs +55 -0
  83. package/firedrill/tools/linkedin/lib/urn.mjs +93 -0
  84. package/firedrill/tools/linkedin/lib/wire.mjs +125 -0
  85. package/firedrill/tools/linkedin/linkedin.tool.json +8935 -0
  86. package/firedrill/tools/linkedin/ops/comments-read.mjs +57 -0
  87. package/firedrill/tools/linkedin/ops/comments-write.mjs +104 -0
  88. package/firedrill/tools/linkedin/ops/feed.mjs +74 -0
  89. package/firedrill/tools/linkedin/ops/network.mjs +46 -0
  90. package/firedrill/tools/linkedin/ops/orgs.mjs +93 -0
  91. package/firedrill/tools/linkedin/ops/posts-edit.mjs +71 -0
  92. package/firedrill/tools/linkedin/ops/posts-read.mjs +54 -0
  93. package/firedrill/tools/linkedin/ops/posts-write.mjs +74 -0
  94. package/firedrill/tools/linkedin/ops/profile.mjs +38 -0
  95. package/firedrill/tools/linkedin/ops/reactions.mjs +78 -0
  96. package/firedrill/tools/linkedin/ops/social.mjs +34 -0
  97. package/firedrill/world.json +2585 -0
  98. package/firedrill.json +5 -0
  99. package/package.json +52 -0
  100. package/starter.json +1833 -0
  101. package/test/client.mjs +71 -0
  102. package/test/conformance.mjs +20 -0
  103. package/test/fixtures.mjs +182 -0
  104. package/test/flows/comments.mjs +116 -0
  105. package/test/flows/every.mjs +38 -0
  106. package/test/flows/faults.mjs +56 -0
  107. package/test/flows/identity.mjs +70 -0
  108. package/test/flows/orgs.mjs +107 -0
  109. package/test/flows/posts.mjs +196 -0
  110. package/test/flows/reactions.mjs +101 -0
  111. 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
+ }