Skip to content
This repository has been archived by the owner on Sep 12, 2019. It is now read-only.

Commit

Permalink
add it working
Browse files Browse the repository at this point in the history
  • Loading branch information
sw-yx committed Mar 20, 2019
1 parent 26ecd50 commit dc51eb2
Show file tree
Hide file tree
Showing 33 changed files with 927 additions and 30 deletions.
59 changes: 43 additions & 16 deletions src/commands/functions/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,29 @@ const { flags } = require('@oclif/command')
const Command = require('@netlify/cli-utils')
const inquirer = require('inquirer')
const readRepoURL = require('../../utils/readRepoURL')
const { createSiteAddon } = require('../../utils/addons')
const http = require('http')
const fetch = require('node-fetch')
const cp = require('child_process')
const { createAddon } = require('netlify/src/addons')

const templatesDir = path.resolve(__dirname, '../../functions-templates')

/**
* Be very clear what is the SOURCE (templates dir) vs the DEST (functions dir)
*/
class FunctionsCreateCommand extends Command {
async run() {
const { flags, args } = this.parse(FunctionsCreateCommand)
const { config } = this.netlify

const { config, api, site } = this.netlify
const accessToken = await this.authenticate()
const functionsDir = ensureFunctionDirExists(flags, config, this.log)

/* either download from URL or scaffold from template */
if (flags.url) {
await downloadFromURL(flags, args, functionsDir)
} else {
await scaffoldFromTemplate(flags, args, functionsDir, this.log)
await scaffoldFromTemplate(flags, args, functionsDir, api, site, accessToken, this.log)
}
}
}
Expand Down Expand Up @@ -83,23 +88,31 @@ async function getNameFromArgs(args, flags, defaultName) {

// pick template from our existing templates
async function pickTemplate() {
// let templates = fs.readdirSync(templatesDir).filter(x => x.split('.').length === 1) // only folders
const registry = require(path.join(templatesDir, 'template-registry.js'))
let templates = registry.sort((a, b) => (a.priority || 999) - (b.priority || 999)) // doesnt scale but will be ok for now
// doesnt scale but will be ok for now
const registries = ['js', 'ts', 'go'].flatMap(formatRegistryArrayForInquirer)
const { chosentemplate } = await inquirer.prompt([
{
name: 'chosentemplate',
message: 'pick a template',
type: 'list',
choices: templates.map(t => ({
// confusing but this is the format inquirer wants
name: t.description,
value: t.name,
short: t.name
}))
choices: registries
}
])
return registry.find(x => x.name === chosentemplate)
return chosentemplate
function formatRegistryArrayForInquirer(lang) {
const registry = require(path.join(templatesDir, lang, 'template-registry.js'))
.sort((a, b) => (a.priority || 999) - (b.priority || 999))
.map(t => {
t.lang = lang
return {
// confusing but this is the format inquirer wants
name: `[${lang}] ` + t.description,
value: t,
short: lang + '-' + t.name
}
})
return [new inquirer.Separator(`----[${lang.toUpperCase()}]----`), ...registry]
}
}

/* get functions dir (and make it if necessary) */
Expand Down Expand Up @@ -150,10 +163,10 @@ async function downloadFromURL(flags, args, functionsDir) {
}

// no --url flag specified, pick from a provided template
async function scaffoldFromTemplate(flags, args, functionsDir, log) {
const { onComplete, name: templateName } = await pickTemplate() // pull the rest of the metadata from the template
async function scaffoldFromTemplate(flags, args, functionsDir, api, site, accessToken, log) {
const { onComplete, name: templateName, lang, addons = [] } = await pickTemplate() // pull the rest of the metadata from the template

const pathToTemplate = path.join(templatesDir, templateName)
const pathToTemplate = path.join(templatesDir, lang, templateName)
if (!fs.existsSync(pathToTemplate)) {
throw new Error(`there isnt a corresponding folder to the selected name, ${templateName} template is misconfigured`)
}
Expand Down Expand Up @@ -184,6 +197,20 @@ async function scaffoldFromTemplate(flags, args, functionsDir, log) {
})
}

if (addons.length) {
const siteId = site.id
if (!siteId) {
this.log('No site id found, please run inside a site folder or `netlify link`')
return false
}
api.getSite({ siteId }).then(async siteData => {
const arr = addons.map(addonName => {
log('installing addon: ' + addonName)
return createSiteAddon(accessToken, addonName, siteId, siteData, log)
})
return Promise.all(arr)
})
}
if (onComplete) onComplete() // do whatever the template wants to do after it is scaffolded
})
}
Expand Down
15 changes: 1 addition & 14 deletions src/functions-templates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,4 @@ place new templates here and our CLI will pick it up. each template must be in i

we dont colocate this inside `src/commands/functions` because oclif will think it's a new command.

<!--
## providing metadata (and other functionality)
we split the file based on the `// --- Netlify Template Below -- //` string. everything below it is cloned as the template. everything above it can be required and run as a module for configuring the template. for now we simply export a `metadata` object that fits [`inquirer's choices spec`](https://www.npmjs.com/package/inquirer#question).
once the templating is done we can also call an `onComplete` hook to print a reminder or execute other logic - see `node-fetch.js` for an example.
you can optionally set a `priority` to pin display order.
in future we can think about other options we may want to offer.
## future dev thoughts
we will want a way to scale this to TS and Go as well. -->
every function should be registered with their respective `template-registry.js`.
18 changes: 18 additions & 0 deletions src/functions-templates/go/hello-world/hello-world.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package main

import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)

func handler(request events.APIGatewayProxyRequest) (*events.APIGatewayProxyResponse, error) {
return &events.APIGatewayProxyResponse{
StatusCode: 200,
Body: "Hello, World",
}, nil
}

func main() {
// Make the handler available for Remote Procedure Call by AWS Lambda
lambda.Start(handler)
}
12 changes: 12 additions & 0 deletions src/functions-templates/go/template-registry.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// every object should have:
// // a 'name' field that corresponds to a folder
// // "description" is just what shows in the CLI but we use the name as the identifier
// onComplete is optional.
// priority is optional - for controlling what shows first in CLI
module.exports = [
{
name: 'hello-world',
priority: 1,
description: 'Basic Hello World function in Golang'
}
]
36 changes: 36 additions & 0 deletions src/functions-templates/js/fauna-crud/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const faunadb = require('faunadb')

/* configure faunaDB Client with our secret */
const q = faunadb.query
const client = new faunadb.Client({
secret: process.env.FAUNADB_SERVER_SECRET
})

/* export our lambda function as named "handler" export */
exports.handler = (event, context, callback) => {
/* parse the string body into a useable JS object */
const data = JSON.parse(event.body)
console.log('Function `todo-create` invoked', data)
const todoItem = {
data: data
}
/* construct the fauna query */
return client
.query(q.Create(q.Ref('classes/todos'), todoItem))
.then(response => {
console.log('success', response)
/* Success! return the response with statusCode 200 */
return callback(null, {
statusCode: 200,
body: JSON.stringify(response)
})
})
.catch(error => {
console.log('error', error)
/* Error! return the error with statusCode 400 */
return callback(null, {
statusCode: 400,
body: JSON.stringify(error)
})
})
}
32 changes: 32 additions & 0 deletions src/functions-templates/js/fauna-crud/delete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* Import faunaDB sdk */
const faunadb = require('faunadb')

function getId(urlPath) {
return urlPath.match(/([^\/]*)\/*$/)[0]
}

const q = faunadb.query
const client = new faunadb.Client({
secret: process.env.FAUNADB_SERVER_SECRET
})

exports.handler = (event, context, callback) => {
const id = getId(event.path)
console.log(`Function 'todo-delete' invoked. delete id: ${id}`)
return client
.query(q.Delete(q.Ref(`classes/todos/${id}`)))
.then(response => {
console.log('success', response)
return callback(null, {
statusCode: 200,
body: JSON.stringify(response)
})
})
.catch(error => {
console.log('error', error)
return callback(null, {
statusCode: 400,
body: JSON.stringify(error)
})
})
}
14 changes: 14 additions & 0 deletions src/functions-templates/js/fauna-crud/fauna-crud.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
exports.handler = async (event, context, callback) => {
const { action } = event.queryStringParameters
switch (action) {
case 'create':
return require('./create').handler(event, context, callback)
case 'read':
return require('./read').handler(event, context, callback)
case 'update':
return require('./update').handler(event, context, callback)
case 'delete':
return require('./delete').handler(event, context, callback)
}
return { statusCode: 500, body: 'unrecognized action ' + action }
}
20 changes: 20 additions & 0 deletions src/functions-templates/js/fauna-crud/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "fauna-crud",
"version": "1.0.0",
"description": "netlify functions:create - CRUD functionality with Fauna DB",
"main": "fauna-crud.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"netlify",
"serverless",
"js",
"faunadb"
],
"author": "Netlify",
"license": "MIT",
"dependencies": {
"faunadb": "^2.6.1"
}
}
32 changes: 32 additions & 0 deletions src/functions-templates/js/fauna-crud/read.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/* Import faunaDB sdk */
const faunadb = require('faunadb')

function getId(urlPath) {
return urlPath.match(/([^\/]*)\/*$/)[0]
}

const q = faunadb.query
const client = new faunadb.Client({
secret: process.env.FAUNADB_SERVER_SECRET
})

exports.handler = (event, context, callback) => {
const id = getId(event.path)
console.log(`Function 'todo-read' invoked. Read id: ${id}`)
return client
.query(q.Get(q.Ref(`classes/todos/${id}`)))
.then(response => {
console.log('success', response)
return callback(null, {
statusCode: 200,
body: JSON.stringify(response)
})
})
.catch(error => {
console.log('error', error)
return callback(null, {
statusCode: 400,
body: JSON.stringify(error)
})
})
}
33 changes: 33 additions & 0 deletions src/functions-templates/js/fauna-crud/update.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* Import faunaDB sdk */
const faunadb = require('faunadb')

function getId(urlPath) {
return urlPath.match(/([^\/]*)\/*$/)[0]
}

const q = faunadb.query
const client = new faunadb.Client({
secret: process.env.FAUNADB_SERVER_SECRET
})

exports.handler = (event, context, callback) => {
const data = JSON.parse(event.body)
const id = getId(event.path)
console.log(`Function 'todo-update' invoked. update id: ${id}`)
return client
.query(q.Update(q.Ref(`classes/todos/${id}`), { data }))
.then(response => {
console.log('success', response)
return callback(null, {
statusCode: 200,
body: JSON.stringify(response)
})
})
.catch(error => {
console.log('error', error)
return callback(null, {
statusCode: 400,
body: JSON.stringify(error)
})
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
exports.handler = async (event, context) => {
console.log('protected function!')
// Reading the context.clientContext will give us the current user
const claims = context.clientContext && context.clientContext.user
console.log('user claims', claims)

if (!claims) {
console.log('No claims! Begone!')
return {
statusCode: 401,
body: JSON.stringify({
data: 'NOT ALLOWED'
})
}
}

return {
statusCode: 200,
body: JSON.stringify({
data: 'auth true'
})
}
}
19 changes: 19 additions & 0 deletions src/functions-templates/js/set-cookie/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "set-cookie",
"version": "1.0.0",
"description": "netlify functions:create - set a cookie with your Netlify Function",
"main": "set-cookie",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"netlify",
"serverless",
"js"
],
"author": "Netlify",
"license": "MIT",
"dependencies": {
"cookie": "^0.3.1"
}
}
Loading

0 comments on commit dc51eb2

Please sign in to comment.