Skip to content

Instantly share code, notes, and snippets.

@r-brown
Last active August 5, 2020 16:51
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save r-brown/528a5e3264ce37f12ccc37249bbcb7f1 to your computer and use it in GitHub Desktop.
Save r-brown/528a5e3264ce37f12ccc37249bbcb7f1 to your computer and use it in GitHub Desktop.
'use strict';
const https = require('https');
const http = require('http');
const $url = require('url');
const querystring = require('querystring');
const CORS_HEADERS = {
'Access-Control-Allow-Methods': 'OPTIONS,POST',
'Access-Control-Allow-Headers': 'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Credentials': true,
};
const request = (options) => {
const { data, url } = options;
const urlPieces = $url.parse(url);
delete options.data;
delete options.url;
const config = {
method: 'GET',
headers: {},
hostname: urlPieces.hostname,
path: urlPieces.path,
port: urlPieces.port,
...options,
};
const defaultHeaders = {
Accept: 'application/json',
'Content-Type': 'application/json',
'User-agent': 'NetLicensing Lambda',
};
config.headers = { ...defaultHeaders, ...config.headers };
const isGet = (config.method.toLocaleLowerCase() === 'get');
const isPost = (config.method.toLocaleLowerCase() === 'post');
const query = (function () {
if (data && !Object.keys(data).length) {
return '';
}
if (isGet) {
return querystring.stringify(data);
}
return (config.headers['Content-Type'] === 'application/x-www-form-urlencoded')
? querystring.stringify(data)
: JSON.stringify(data);
}());
if (isGet) {
if (query) {
config.path = `${config.path}?${query}`;
}
}
if (isPost) {
if (query) {
config.headers['Content-Length'] = query.length;
}
}
return new Promise((resolve, reject) => {
const httpProv = urlPieces.protocol === 'http:' ? http : https;
const req = httpProv.request(config, (res) => {
const { statusCode } = res;
if (statusCode < 200 || statusCode >= 300) {
reject(res);
return;
}
let resData = '';
res.on('data', (chunk) => {
resData += chunk;
});
res.on('end', () => {
// remove \n from body
try {
resData = JSON.stringify(JSON.parse(resData));
} catch (e) {
if (!(e instanceof SyntaxError)) {
throw e;
}
}
if (statusCode < 200 || statusCode >= 300) {
const e = new Error('Bad request!');
e.response = { ...res, data: resData };
reject(e);
} else {
resolve({ ...res, data: resData });
}
});
});
req.on('error', (e) => {
reject(e);
});
if (isPost && query) {
req.write(query);
}
// send the request
req.end();
});
};
const itemToObjectConverter = (item) => {
const itemToObject = (source) => {
const handle = {};
if (source.property && source.property.length) {
source.property.forEach((p) => {
// if value is string(boolean) type cast value to boolean
handle[p.name] = (p.value === 'true' || p.value === 'false')
? (p.value === 'true')
: p.value;
});
}
if (source.list && source.list.length) {
source.list.forEach((l) => {
handle[l.name] = itemToObject(l);
});
}
if (Array.isArray(source)) {
source.forEach((v) => {
handle[v.type] = itemToObject(v);
});
return handle;
}
return handle;
};
return itemToObject(item);
};
exports.handler = async (event) => {
const { httpMethod } = event;
if (httpMethod === 'OPTIONS') {
return { headers: CORS_HEADERS, statusCode: 204 };
}
// environment variables
const {
env: {
CLIENT_ID,
CLIENT_SECRET,
GITHUB_ACCESS_TOKEN_URL,
GITHUB_USER_PROFILE_URL,
GITHUB_EDUCATION_URL,
SLACK_WEBHOOK_URL,
NLIC_BASE_URL,
NLIC_APIKEY_OPERATION,
NLIC_STUDENT_LICENSE_TEMPLATE_NUMBER,
NLIC_TEACHER_LICENSE_TEMPLATE_NUMBER,
},
} = process;
// input data
const { vendorNumber, code } = (event.body) ? JSON.parse(event.body) : {};
// information to be sent to Slack
const fields = [];
try {
// check vendor number
if (!vendorNumber) {
throw new Error('Missing vendor number!');
}
// check vendor code
if (!code) {
throw new Error('Missing GitHub code!');
}
fields.push(
{
title: 'Vendor Number',
value: vendorNumber,
short: true,
},
);
// get access token
const { data: oauthData } = await request({
url: GITHUB_ACCESS_TOKEN_URL,
method: 'POST',
data: {
code,
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
state: vendorNumber,
},
});
const { access_token: accessToken, error, error_description: errorDescription } = JSON.parse(oauthData);
if (error) {
throw new Error(errorDescription);
}
// get email and login
const { data: gitHubData } = await request({
url: GITHUB_USER_PROFILE_URL,
method: 'GET',
headers: {
Authorization: `token ${accessToken}`,
},
});
const { login, email } = JSON.parse(gitHubData);
fields.push(
{
title: 'GitHub Handle',
value: `<https://github.com/${login}|${login}>`,
short: true,
},
{
title: 'GitHub EMail',
value: email,
short: true,
},
);
// get student state
const { data: educationData } = await request({
url: GITHUB_EDUCATION_URL,
method: 'GET',
headers: {
Authorization: `token ${accessToken}`,
'faculty-check-preview': 'true',
},
});
const { student, faculty } = JSON.parse(educationData);
// create license
if (student || faculty) {
const person = (student) ? 'Student' : 'Teacher';
fields.push(
{
title: 'Qualified as',
value: person,
short: true,
},
);
const licenseTemplateNumber = (student)
? NLIC_STUDENT_LICENSE_TEMPLATE_NUMBER
: NLIC_TEACHER_LICENSE_TEMPLATE_NUMBER;
const { data: nlicData } = await request({
url: `${NLIC_BASE_URL}/core/v2/rest/license`,
method: 'POST',
headers: {
Authorization: `Basic ${Buffer.from(`apiKey:${NLIC_APIKEY_OPERATION}`).toString('base64')}`,
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded',
},
data: {
licenseTemplateNumber,
licenseeNumber: vendorNumber,
active: true,
},
});
const { items: { item } } = JSON.parse(nlicData);
const { License: license } = itemToObjectConverter(item);
fields.push(
{
title: 'License Number',
value: license.number,
short: true,
},
);
}
// send to slack
await request({
url: SLACK_WEBHOOK_URL,
method: 'POST',
data: {
text: '*Github Student Developer Pack application*',
attachments: [
{
fields,
color: (student || faculty) ? 'good' : 'warning',
},
],
},
});
return { headers: CORS_HEADERS, statusCode: 200, body: JSON.stringify({ student, faculty }) };
} catch (e) {
fields.push(
{
title: 'Github Code',
value: code,
short: true,
},
);
await request({
url: SLACK_WEBHOOK_URL,
method: 'POST',
data: {
text: '*Github Student Developer Pack application*',
attachments: [
{
title: e.message,
pretext: '*ERROR!!!*',
color: 'danger',
text: `\`\`\`${e.stack}\`\`\``,
mrkdwn_in: ['text', 'pretext'],
},
{
fields,
title: 'Context',
color: 'danger',
},
],
},
});
return { headers: CORS_HEADERS, statusCode: 400, body: JSON.stringify({ message: e.message }) };
}
};
@r-brown
Copy link
Author

r-brown commented Aug 5, 2020

AWS Lambda script to assign NetLicensing Student & Teacher Plan to GitHub Student Developer Pack participants.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment