Skip to content

Commit 099d4ca

Browse files
committed
poc: separate submission function into sync and background
1 parent b760c8c commit 099d4ca

File tree

4 files changed

+217
-146
lines changed

4 files changed

+217
-146
lines changed

netlify.toml

+3
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@
22
command = "npm run build-ci"
33
# skip build when changes are isolated to README.md or .all-contributorsrc
44
ignore = "git diff --quiet HEAD^ HEAD ':(exclude)README.md' ':(exclude).all-contributorsrc' "
5+
[dev]
6+
framework = "gatsby"
7+
targetPort = 8000

netlify/functions/submission-background.js

+5-129
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,16 @@ const sharp = require('sharp');
55
// event.body expected to be:
66
// {
77
// title: "Something",
8-
// image: "base64string="
98
// authorName: "Coding Train",
109
// authorUrl: "https://thecodingtrain.com",
11-
// authorEmail: "help@thecodingtrain.com",
12-
// authorTwitter: "@thecodingtrain",
13-
// authorInstagram: "@the.coding.train"
1410
// url: "https://thecodingtrain.com/tracks",
1511
// challenge: "01-test",
12+
// imageUrl: "https://github.com/branch/image.png",
13+
// branchName: "showcase-coding-train-1234567890"
1614
// }
1715

18-
async function validateImage(imageBase64) {
19-
const supportedImageFormatsToExtensions = new Map([
20-
['png', 'png'],
21-
['jpeg', 'jpg']
22-
]);
23-
24-
try {
25-
const { info } = await sharp(Buffer.from(imageBase64, 'base64')).toBuffer({
26-
resolveWithObject: true
27-
});
28-
29-
const imageExtension = supportedImageFormatsToExtensions.get(info.format);
30-
31-
return imageExtension
32-
? { imageExtension }
33-
: { error: `Unsupported image format: ${info.format}` };
34-
} catch (e) {
35-
return { error: "Can't decode submitted image" };
36-
}
37-
}
38-
3916
exports.handler = async function (event) {
40-
console.log('Handler called with: ', event.body);
17+
console.log('Background handler called with: ', event.body);
4118

4219
if (!process.env.GITHUB_TOKEN) {
4320
console.error('GitHub Token not loaded');
@@ -47,112 +24,11 @@ exports.handler = async function (event) {
4724
// parse payload
4825
const postInfo = JSON.parse(event.body);
4926

50-
// validate image and extract its extension
51-
const { error, imageExtension } = await validateImage(postInfo.image);
52-
if (error) {
53-
console.error(error);
54-
return;
55-
}
56-
5727
// Shared properties
58-
const unix = Math.floor(Date.now() / 1000);
5928
const owner = 'CodingTrain';
6029
const repo = 'thecodingtrain.com';
61-
const showcasePath = `content/videos/challenges/${postInfo.challenge}/showcase`;
62-
const jsonPath = `${showcasePath}/contribution-${unix}.json`;
63-
const imagePath = `${showcasePath}/contribution-${unix}.${imageExtension}`;
6430
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
6531

66-
/**
67-
Get the SHA of the main branch
68-
**/
69-
const shaRes = await octokit.request(
70-
`GET /repos/${owner}/${repo}/git/ref/heads/main`
71-
);
72-
const mainSha = shaRes.data.object.sha;
73-
74-
/**
75-
Make a new branch
76-
**/
77-
const branchName = slugify(
78-
`showcase-${slugify(postInfo.authorName)}-${unix}`.toLowerCase()
79-
);
80-
81-
const branchRes = await octokit.request(
82-
`POST /repos/${owner}/${repo}/git/refs`,
83-
{
84-
ref: `refs/heads/${branchName}`,
85-
sha: mainSha
86-
}
87-
);
88-
89-
/**
90-
Add the JSON file
91-
**/
92-
const json = {
93-
title: postInfo.title,
94-
author: {
95-
name: postInfo.authorName
96-
},
97-
url: postInfo.url,
98-
submittedOn: new Date().toISOString()
99-
};
100-
101-
if (postInfo.authorUrl) {
102-
json.author.url = postInfo.authorUrl;
103-
}
104-
105-
// Including the social media handles in the JSON for future reference
106-
if (postInfo.authorTwitter) {
107-
json.author.twitter = postInfo.authorTwitter;
108-
}
109-
110-
if (postInfo.authorInstagram) {
111-
json.author.instagram = postInfo.authorInstagram;
112-
}
113-
114-
const jsonString = JSON.stringify(json, null, 2) + '\n';
115-
const jsonContent = Buffer.from(jsonString).toString('base64');
116-
117-
const jsonOpts = {
118-
branch: branchName,
119-
message: 'Added contribution JSON file',
120-
content: jsonContent
121-
};
122-
123-
if (postInfo.authorName && postInfo.authorEmail) {
124-
jsonOpts.committer = {
125-
name: postInfo.authorName,
126-
email: postInfo.authorEmail
127-
};
128-
}
129-
130-
const jsonRes = await octokit.request(
131-
`PUT /repos/${owner}/${repo}/contents/${jsonPath}`,
132-
jsonOpts
133-
);
134-
135-
/**
136-
Add the image
137-
**/
138-
const imageOpts = {
139-
branch: branchName,
140-
message: 'Added contribution image file',
141-
content: postInfo.image
142-
};
143-
144-
if (postInfo.authorName && postInfo.authorEmail) {
145-
imageOpts.committer = {
146-
name: postInfo.authorName,
147-
email: postInfo.authorEmail
148-
};
149-
}
150-
151-
const imageRes = await octokit.request(
152-
`PUT /repos/${owner}/${repo}/contents/${imagePath}`,
153-
imageOpts
154-
);
155-
15632
/**
15733
Make a PR to main
15834
**/
@@ -169,8 +45,8 @@ exports.handler = async function (event) {
16945
: postInfo.authorName
17046
}
17147
172-
![preview image](${imageRes.data.content.download_url})`,
173-
head: branchName,
48+
![preview image](${postInfo.imageUrl})`,
49+
head: postInfo.branchName,
17450
base: 'main'
17551
});
17652

netlify/functions/submission-sync.js

+191
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
const { Octokit } = require('@octokit/core');
2+
const slugify = require('slugify');
3+
const sharp = require('sharp');
4+
5+
// event.body expected to be:
6+
// {
7+
// title: "Something",
8+
// image: "base64string=",
9+
// authorName: "Coding Train",
10+
// authorUrl: "https://thecodingtrain.com",
11+
// authorEmail: "help@thecodingtrain.com",
12+
// authorTwitter: "@thecodingtrain",
13+
// authorInstagram: "@the.coding.train"
14+
// url: "https://thecodingtrain.com/tracks",
15+
// challenge: "01-test",
16+
// }
17+
18+
async function validateImage(imageBase64) {
19+
const supportedImageFormatsToExtensions = new Map([
20+
['png', 'png'],
21+
['jpeg', 'jpg']
22+
]);
23+
24+
try {
25+
const { info } = await sharp(Buffer.from(imageBase64, 'base64')).toBuffer({
26+
resolveWithObject: true
27+
});
28+
29+
const imageExtension = supportedImageFormatsToExtensions.get(info.format);
30+
31+
return imageExtension
32+
? { imageExtension }
33+
: { error: `Unsupported image format: ${info.format}` };
34+
} catch (e) {
35+
return { error: "Can't decode submitted image" };
36+
}
37+
}
38+
39+
exports.handler = async function (event, context) {
40+
console.log('Sync handler called with', event.body);
41+
42+
if (!process.env.GITHUB_TOKEN) {
43+
console.error('GitHub Token not loaded');
44+
return {
45+
statusCode: 500,
46+
body: JSON.stringify({ error: 'GitHub Token not loaded' })
47+
};
48+
}
49+
50+
// parse payload
51+
const postInfo = JSON.parse(event.body);
52+
53+
// validate image and extract its extension
54+
const { error, imageExtension } = await validateImage(postInfo.image);
55+
if (error) {
56+
console.error(error);
57+
return {
58+
statusCode: 400,
59+
body: JSON.stringify({ error })
60+
};
61+
}
62+
63+
// Shared properties
64+
const unix = Math.floor(Date.now() / 1000);
65+
const owner = 'CodingTrain';
66+
const repo = 'thecodingtrain.com';
67+
const showcasePath = `content/videos/challenges/${postInfo.challenge}/showcase`;
68+
const jsonPath = `${showcasePath}/contribution-${unix}.json`;
69+
const imagePath = `${showcasePath}/contribution-${unix}.${imageExtension}`;
70+
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
71+
72+
/**
73+
Get the SHA of the main branch
74+
**/
75+
const shaRes = await octokit.request(
76+
`GET /repos/${owner}/${repo}/git/ref/heads/main`
77+
);
78+
const mainSha = shaRes.data.object.sha;
79+
80+
/**
81+
Make a new branch
82+
**/
83+
const branchName = slugify(
84+
`showcase-${slugify(postInfo.authorName)}-${unix}`.toLowerCase()
85+
);
86+
87+
const branchRes = await octokit.request(
88+
`POST /repos/${owner}/${repo}/git/refs`,
89+
{
90+
ref: `refs/heads/${branchName}`,
91+
sha: mainSha
92+
}
93+
);
94+
95+
/**
96+
Add the JSON file
97+
**/
98+
const json = {
99+
title: postInfo.title,
100+
author: {
101+
name: postInfo.authorName
102+
},
103+
url: postInfo.url,
104+
submittedOn: new Date().toISOString()
105+
};
106+
107+
if (postInfo.authorUrl) {
108+
json.author.url = postInfo.authorUrl;
109+
}
110+
111+
// Including the social media handles in the JSON for future reference
112+
if (postInfo.authorTwitter) {
113+
json.author.twitter = postInfo.authorTwitter;
114+
}
115+
116+
if (postInfo.authorInstagram) {
117+
json.author.instagram = postInfo.authorInstagram;
118+
}
119+
120+
const jsonString = JSON.stringify(json, null, 2) + '\n';
121+
const jsonContent = Buffer.from(jsonString).toString('base64');
122+
123+
const jsonOpts = {
124+
branch: branchName,
125+
message: 'Added contribution JSON file',
126+
content: jsonContent
127+
};
128+
129+
if (postInfo.authorName && postInfo.authorEmail) {
130+
jsonOpts.committer = {
131+
name: postInfo.authorName,
132+
email: postInfo.authorEmail
133+
};
134+
}
135+
136+
const jsonRes = await octokit.request(
137+
`PUT /repos/${owner}/${repo}/contents/${jsonPath}`,
138+
jsonOpts
139+
);
140+
141+
/**
142+
Add the image
143+
**/
144+
const imageOpts = {
145+
branch: branchName,
146+
message: 'Added contribution image file',
147+
content: postInfo.image
148+
};
149+
150+
if (postInfo.authorName && postInfo.authorEmail) {
151+
imageOpts.committer = {
152+
name: postInfo.authorName,
153+
email: postInfo.authorEmail
154+
};
155+
}
156+
157+
const imageRes = await octokit.request(
158+
`PUT /repos/${owner}/${repo}/contents/${imagePath}`,
159+
imageOpts
160+
);
161+
162+
/**
163+
Pass the data to the background function
164+
**/
165+
await fetch(
166+
event.rawUrl.replace('submission-sync', 'submission-background'),
167+
{
168+
method: 'POST',
169+
body: JSON.stringify({
170+
title: postInfo.title,
171+
authorName: postInfo.authorName,
172+
authorUrl: postInfo.authorUrl,
173+
url: postInfo.url,
174+
challenge: postInfo.challenge,
175+
imageUrl: imageRes.data.content.download_url,
176+
branchName: branchName
177+
}),
178+
headers: {
179+
'Content-Type': 'application/json'
180+
}
181+
}
182+
);
183+
184+
return {
185+
statusCode: 200,
186+
body: JSON.stringify({
187+
status: 'success',
188+
branchName: branchName
189+
})
190+
};
191+
};

0 commit comments

Comments
 (0)