Skip to content

Commit d61eae3

Browse files
committed
untested canonical improvements, still need to parse out images
1 parent 5331db5 commit d61eae3

7 files changed

+80
-49
lines changed

functions/identify-new-content.ts

+13-14
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import { Octokit } from "octokit";
2-
import {
3-
EventBridgeClient,
4-
PutEventsCommand,
5-
} from "@aws-sdk/client-eventbridge";
2+
import { SFNClient, StartExecutionCommand } from '@aws-sdk/client-sfn';
63
import { getSecret } from "./utils/secrets";
74

8-
const eb = new EventBridgeClient({});
9-
5+
const sfn = new SFNClient({});
106
let octokit: Octokit;
117

128
export const handler = async (event: any) => {
@@ -135,14 +131,17 @@ const processNewContent = async (
135131
sendStatusEmail: boolean;
136132
}[]
137133
) => {
138-
const Entries = newContent.map((content) => ({
139-
Source: `cross-post`,
140-
DetailType: "process-new-content",
141-
Detail: JSON.stringify(content),
134+
const executions = await Promise.allSettled(newContent.map(async (content) => {
135+
const command = new StartExecutionCommand({
136+
stateMachineArn: process.env.STATE_MACHINE_ARN,
137+
input: JSON.stringify(content)
138+
});
139+
await sfn.send(command);
142140
}));
143141

144-
const putEventsCommand = new PutEventsCommand({
145-
Entries,
146-
});
147-
await eb.send(putEventsCommand);
142+
for (const execution of executions) {
143+
if (execution.status == 'rejected') {
144+
console.error(execution.reason);
145+
}
146+
}
148147
};

functions/parse-dev-post.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const handler = async (state: {
88
post: any;
99
format: string;
1010
articleCatalog: any;
11+
canonical?: string;
1112
}) => {
1213
const details = frontmatter(state.post);
1314
const links = getLinks(details.content);
@@ -45,7 +46,7 @@ const formatDevData = (
4546
} else {
4647
devContent = devContent.replace(
4748
link[1],
48-
`${process.env.BLOG_BASE_URL}${replacement.links.M.url.S}`
49+
`${process.env.AMPLIFY_BASE_URL}${replacement.links.M.url.S}`
4950
);
5051
}
5152
}
@@ -60,10 +61,12 @@ const formatDevData = (
6061
title: postDetail.data.title,
6162
published: true,
6263
main_image: postDetail.data.image,
63-
canonical_url: `${process.env.BLOG_BASE_URL}/${postDetail.data.slug.replace(
64-
/^\/|\/$/g,
65-
""
66-
)}`,
64+
...(process.env.CANONICAL === "dev" ? {} : {
65+
canonical_url: process.env.AMPLIFY_BASE_URL ? `${process.env.AMPLIFY_BASE_URL}/${postDetail.data.slug.replace(
66+
/^\/|\/$/g,
67+
""
68+
)}` : ``,
69+
}),
6770
description: postDetail.data.description,
6871
tags: [
6972
...postDetail.data.categories.map((c) => c.replace(/ /g, "")),

functions/parse-hashnode-post.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export const handler = async (state: {
88
post: any;
99
format: string;
1010
articleCatalog: any;
11+
canonical?: string;
1112
}) => {
1213
const details = frontmatter(state.post);
1314
const links = getLinks(details.content);
@@ -50,7 +51,7 @@ const formatHashnodeData = (
5051
} else {
5152
hashnodeContent = hashnodeContent.replace(
5253
link[1],
53-
`${process.env.BLOG_BASE_URL}${replacement.links.M.url.S}`
54+
`${process.env.AMPLIFY_BASE_URL}${replacement.links.M.url.S}`
5455
);
5556
}
5657
}
@@ -71,9 +72,9 @@ const formatHashnodeData = (
7172
contentMarkdown: hashnodeContent,
7273
coverImageURL: postDetail.data.image,
7374
isRepublished: {
74-
originalArticleURL: `${
75-
process.env.BLOG_BASE_URL
76-
}/${postDetail.data.slug.replace(/^\/|\/$/g, "")}`,
75+
...(process.env.CANONICAL === "hashnode" ? {} : {
76+
originalArticleURL: process.env.AMPLIFY_BASE_URL ? `${process.env.AMPLIFY_BASE_URL}/${postDetail.data.slug.replace(/^\/|\/$/g, "")}` : ``,
77+
}),
7778
},
7879
tags: [],
7980
subtitle: postDetail.data.description,

functions/parse-medium-post.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { getTweetUrl } from "./utils/getTweetUrl";
44

55
const frontmatter = require('@github-docs/frontmatter');
66

7-
export const handler = async (state: { post: any; format: string; articleCatalog: any; }) => {
7+
export const handler = async (state: { post: any; format: string; articleCatalog: any; canonical?: string; }) => {
88
const details = frontmatter(state.post);
99
const links = getLinks(details.content);
1010
const tweets = getTweets(details.content);
@@ -29,7 +29,7 @@ const formatMediumData = (postDetail: { data: { title: any; description: any; im
2929
if (replacement.links.M.mediumUrl && replacement.links.M.mediumUrl.S) {
3030
mediumContent = mediumContent.replace(link[1], replacement.links.M.mediumUrl.S);
3131
} else {
32-
mediumContent = mediumContent.replace(link[1], `${process.env.BLOG_BASE_URL}${replacement.links.M.url.S}`);
32+
mediumContent = mediumContent.replace(link[1], `${process.env.AMPLIFY_BASE_URL}${replacement.links.M.url.S}`);
3333
}
3434
}
3535
}
@@ -43,7 +43,12 @@ const formatMediumData = (postDetail: { data: { title: any; description: any; im
4343
title: postDetail.data.title,
4444
contentFormat: 'markdown',
4545
tags: [...postDetail.data.categories, ...postDetail.data.tags],
46-
canonicalUrl: `${process.env.BLOG_BASE_URL}/${postDetail.data.slug.replace(/^\/|\/$/g, '')}`,
46+
...(process.env.CANONICAL === "medium" ? {} : {
47+
canonical_url: process.env.AMPLIFY_BASE_URL ? `${process.env.AMPLIFY_BASE_URL}/${postDetail.data.slug.replace(
48+
/^\/|\/$/g,
49+
""
50+
)}` : ``,
51+
}),
4752
publishStatus: 'draft',
4853
notifyFollowers: true,
4954
content: mediumContent

lib/blog-crossposting-automation-stack.ts

+8-17
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ import { StackProps, Stack, CfnOutput, Duration } from "aws-cdk-lib";
22
import { EventBus, Rule } from "aws-cdk-lib/aws-events";
33
import {
44
LambdaFunction,
5-
SfnStateMachine,
65
} from "aws-cdk-lib/aws-events-targets";
76
import { Architecture, FunctionUrlAuthType, Runtime } from "aws-cdk-lib/aws-lambda";
8-
import { NodejsFunction } from "aws-cdk-lib/aws-lambda-nodejs";
7+
import { NodejsFunction, NodejsFunctionProps } from "aws-cdk-lib/aws-lambda-nodejs";
98
import { Secret } from "aws-cdk-lib/aws-secretsmanager";
109
import { Construct } from "constructs";
1110
import { join } from "path";
@@ -78,9 +77,9 @@ export class BlogCrosspostingAutomationStack extends Stack {
7877
secretName
7978
);
8079

81-
const lambdaProps = {
80+
const lambdaProps: NodejsFunctionProps = {
8281
architecture: Architecture.ARM_64,
83-
memory: 1024,
82+
memorySize: 1024,
8483
timeout: Duration.minutes(5),
8584
runtime: Runtime.NODEJS_18_X,
8685
environment: {
@@ -105,6 +104,7 @@ export class BlogCrosspostingAutomationStack extends Stack {
105104
eventBus,
106105
sendApiRequestFn,
107106
table,
107+
canonical,
108108
};
109109
if (devTo?.devOrganizationId) {
110110
const parseDevFn = new NodejsFunction(this, `ParseDevToFn`, {
@@ -114,7 +114,7 @@ export class BlogCrosspostingAutomationStack extends Stack {
114114
parseDevFn.addEnvironment("CANONICAL", canonical);
115115
parseDevFn.addEnvironment("DEV_ORG_ID", devTo.devOrganizationId);
116116
if (amplify) {
117-
parseDevFn.addEnvironment("BLOG_BASE_URL", amplify.blogBaseUrl);
117+
parseDevFn.addEnvironment("AMPLIFY_BASE_URL", amplify.blogBaseUrl);
118118
}
119119
crossPostStepFunctionProps.devTo = {
120120
fn: parseDevFn!,
@@ -127,7 +127,7 @@ export class BlogCrosspostingAutomationStack extends Stack {
127127
});
128128
parseHashnodeFn.addEnvironment("CANONICAL", canonical);
129129
if (amplify) {
130-
parseHashnodeFn.addEnvironment("BLOG_BASE_URL", amplify.blogBaseUrl);
130+
parseHashnodeFn.addEnvironment("AMPLIFY_BASE_URL", amplify.blogBaseUrl);
131131
}
132132
if (hashnode.hashnodePublicationId) {
133133
parseHashnodeFn.addEnvironment(
@@ -147,7 +147,7 @@ export class BlogCrosspostingAutomationStack extends Stack {
147147
});
148148
parseMediumFn.addEnvironment("CANONICAL", canonical);
149149
if (amplify) {
150-
parseMediumFn.addEnvironment("BLOG_BASE_URL", amplify.blogBaseUrl);
150+
parseMediumFn.addEnvironment("AMPLIFY_BASE_URL", amplify.blogBaseUrl);
151151
}
152152
crossPostStepFunctionProps.medium = {
153153
fn: parseMediumFn!,
@@ -187,7 +187,6 @@ export class BlogCrosspostingAutomationStack extends Stack {
187187
);
188188
}
189189
secret.grantRead(identifyNewContentFn);
190-
eventBus.grantPutEventsTo(identifyNewContentFn);
191190

192191
if (amplify?.amplifyProjectId) {
193192
new Rule(this, `NewArticlesRule`, {
@@ -228,16 +227,8 @@ export class BlogCrosspostingAutomationStack extends Stack {
228227
}
229228

230229
const { stateMachine } = new CrossPostStepFunction(this, `CrossPostStepFn`, crossPostStepFunctionProps);
230+
stateMachine.grantStartExecution(identifyNewContentFn);
231231
table.grantReadWriteData(stateMachine);
232232
eventBus.grantPutEventsTo(stateMachine);
233-
234-
new Rule(this, "CrossPostMachineRule", {
235-
eventBus,
236-
eventPattern: {
237-
source: [`cross-post`],
238-
detailType: ["process-new-content"],
239-
},
240-
targets: [new SfnStateMachine(stateMachine, {})],
241-
});
242233
}
243234
}

lib/step-function-branch.ts

+5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { Construct } from "constructs";
1919

2020
export interface StepFunctionBranchProps {
2121
hashnodeBlogUrl?: string;
22+
includeCanonical: boolean;
2223
parsePostFn: NodejsFunction;
2324
publishPayload: TaskInput;
2425
sendApiRequestFn: NodejsFunction;
@@ -34,6 +35,7 @@ export class StepFunctionBranch extends StateMachineFragment {
3435
super(scope, id);
3536
const {
3637
hashnodeBlogUrl,
38+
includeCanonical,
3739
parsePostFn,
3840
publishPayload,
3941
sendApiRequestFn,
@@ -61,6 +63,9 @@ export class StepFunctionBranch extends StateMachineFragment {
6163
const transform = new LambdaInvoke(this, `Transform`, {
6264
lambdaFunction: parsePostFn,
6365
payload: TaskInput.fromObject({
66+
...(includeCanonical ? {
67+
"canonical.$": `$.canonical.${format}Url`,
68+
} : {}),
6469
"post.$": "$.content",
6570
"articleCatalog.$": "$.catalog.Items",
6671
format,

lib/step-function.ts

+33-6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { Duration } from "aws-cdk-lib";
2626

2727
export interface CrossPostStepFunctionProps {
2828
adminEmail?: string;
29+
canonical: "dev" | "medium" | "hashnode" | "amplify";
2930
devTo?: {
3031
fn: NodejsFunction;
3132
};
@@ -48,6 +49,7 @@ export class CrossPostStepFunction extends Construct {
4849

4950
const {
5051
adminEmail,
52+
canonical,
5153
devTo,
5254
eventBus,
5355
hashnode,
@@ -156,10 +158,21 @@ export class CrossPostStepFunction extends Construct {
156158
loadArticleCatalog.addCatch(updateArticleRecordFailure);
157159

158160
// PARALLEL
159-
const parallel = new Parallel(this, "TransformAndPublish");
160-
loadArticleCatalog.next(parallel);
161+
const transformAndPublish = new Parallel(this, "TransformAndPublish");
162+
let transformAndPublishCanonical = transformAndPublish;
163+
if (canonical !== "amplify") {
164+
transformAndPublishCanonical = new Parallel(this, "TransformAndPublishCanonical", {
165+
resultPath: "$.canonical"
166+
});
167+
loadArticleCatalog.next(transformAndPublishCanonical);
168+
transformAndPublishCanonical.next(transformAndPublish);
169+
} else {
170+
loadArticleCatalog.next(transformAndPublish);
171+
}
172+
161173
if (devTo) {
162174
const devToBranch = new StepFunctionBranch(this, `Dev`, {
175+
includeCanonical: !(canonical === "dev" || canonical === "amplify"),
163176
parsePostFn: devTo.fn,
164177
publishPayload: TaskInput.fromObject({
165178
secretKey: "dev",
@@ -179,10 +192,15 @@ export class CrossPostStepFunction extends Construct {
179192
sendApiRequestFn,
180193
table,
181194
});
182-
parallel.branch(devToBranch.prefixStates());
195+
if (canonical === "dev") {
196+
transformAndPublishCanonical.branch(devToBranch.prefixStates());
197+
} else {
198+
transformAndPublish.branch(devToBranch.prefixStates());
199+
}
183200
}
184201
if (medium) {
185202
const mediumBranch = new StepFunctionBranch(this, `Medium`, {
203+
includeCanonical: !(canonical === "medium" || canonical === "amplify"),
186204
parsePostFn: medium.fn,
187205
publishPayload: TaskInput.fromObject({
188206
secretKey: "medium",
@@ -199,11 +217,16 @@ export class CrossPostStepFunction extends Construct {
199217
sendApiRequestFn,
200218
table,
201219
});
202-
parallel.branch(mediumBranch.prefixStates());
220+
if (canonical === "medium") {
221+
transformAndPublishCanonical.branch(mediumBranch.prefixStates());
222+
} else {
223+
transformAndPublish.branch(mediumBranch.prefixStates());
224+
}
203225
}
204226

205227
if (hashnode) {
206228
const hashnodeBranch = new StepFunctionBranch(this, `Hashnode`, {
229+
includeCanonical: !(canonical === "hashnode" || canonical === "amplify"),
207230
hashnodeBlogUrl: hashnode.url,
208231
parsePostFn: hashnode.fn,
209232
publishPayload: TaskInput.fromObject({
@@ -224,7 +247,11 @@ export class CrossPostStepFunction extends Construct {
224247
sendApiRequestFn,
225248
table,
226249
});
227-
parallel.branch(hashnodeBranch.prefixStates());
250+
if (canonical === "hashnode") {
251+
transformAndPublishCanonical.branch(hashnodeBranch.prefixStates());
252+
} else {
253+
transformAndPublish.branch(hashnodeBranch.prefixStates());
254+
}
228255
}
229256

230257
const formatFailureCheck = new Pass(this, "FormatFailureCheck", {
@@ -235,7 +262,7 @@ export class CrossPostStepFunction extends Construct {
235262
},
236263
},
237264
});
238-
parallel.next(formatFailureCheck);
265+
transformAndPublish.next(formatFailureCheck);
239266
const checkForFailures = new Pass(this, `CheckForFailures`, {
240267
parameters: {
241268
"results.$": "$.results",

0 commit comments

Comments
 (0)