Last active
August 5, 2020 16:51
-
-
Save r-brown/528a5e3264ce37f12ccc37249bbcb7f1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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 }) }; | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
AWS Lambda script to assign NetLicensing Student & Teacher Plan to GitHub Student Developer Pack participants.