Skip to content

Commit

Permalink
moved all components
Browse files Browse the repository at this point in the history
  • Loading branch information
eahefnawy committed Jan 30, 2019
1 parent 2cf12a2 commit a82fda8
Show file tree
Hide file tree
Showing 17 changed files with 225 additions and 172 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.js
Expand Up @@ -15,6 +15,9 @@ module.exports = {
jsx: true
}
},
globals: {
on: true // for the Socket file
},
rules: {
'array-bracket-spacing': [
'error',
Expand Down
19 changes: 10 additions & 9 deletions README.md
Expand Up @@ -7,22 +7,23 @@ git clone https://github.com/serverless/v2.git
cd v2
```

### Demo
### Run Example

Run the Parent component from components directory to see everything happening in action.
Run the `app` example to see how everything fits together.

```bash
Parent (master)$ cd components/Parent
Parent (master)$ node ../../bin/v2
app (master)$ cd examples/app
app (master)$ node ../../bin/v2

Status: Parent Deployment Succeeded
Status: My App Deployed

Socket URL: wss://98531tq37e.execute-api.us-east-1.amazonaws.com/dev/
Website URL: http://my-app-dev-pqso3.s3-website-us-east-1.amazonaws.com

Arn: parent:arn

Parent (master)$
app (master)$
```

**Note:** Checkout the Parent & Child Components code to understand how everything fits together. Lot's of helpful comments there. Also checkout the Component class to see how the magic happens!
**Note:** Checkout the `app` example code to see how components are consumed via a `serverless.js` file. Also checkout real-world component code in the components directory.

### How To Use A Serverless Component Programmatically

Expand Down
33 changes: 0 additions & 33 deletions components/Child/serverless.js

This file was deleted.

9 changes: 7 additions & 2 deletions components/Lambda/serverless.js
Expand Up @@ -43,7 +43,7 @@ class Lambda extends Component {
const config = mergeDeep(defaults, inputs)

const lambda = new aws.Lambda()
const role = new Role(`${this.id}.lambdaRole`)
const role = new Role(`${this.id}.role`)

config.role = config.role || (await role(config))

Expand All @@ -65,6 +65,11 @@ class Lambda extends Component {
}
}

if (this.state.name && this.state.name !== config.name) {
this.cli.status(`Removing Previous Lambda`)
await deleteLambda({ lambda, name: this.state.name })
}

this.state.name = config.name
this.state.arn = config.arn
this.save()
Expand All @@ -89,7 +94,7 @@ class Lambda extends Component {

this.cli.status(`Removing Lambda`)

const role = new Role(`${this.id}.lambdaRole`)
const role = new Role(`${this.id}.role`)

// there's no need to pass role name as input
// since it's saved in the Role component state
Expand Down
32 changes: 0 additions & 32 deletions components/Parent/serverless.js

This file was deleted.

85 changes: 85 additions & 0 deletions components/RealtimeApp/serverless.js
@@ -0,0 +1,85 @@
const { mergeDeep, all } = require('../../src/utils')

const Component = require('../Component/serverless')
const Socket = require('../Socket/serverless')
const Website = require('../Website/serverless')

// private helper functionn
const getConfig = (inputs) => {
const defaults = {
name: 'realtimeApp',
stage: 'dev',
description: 'Realtime App',
region: 'us-east-1',
frontend: {
code: './frontend',
assets: '.',
envFileLocation: './src/env.js',
env: {},
buildCmd: null
},
backend: {
code: './backend',
memory: 512,
timeout: 10,
env: {}
}
}

const config = mergeDeep(defaults, inputs)

config.backend.name = `${config.name}-${config.stage}`
config.backend.description = config.description
config.backend.credentials = config.credentials
config.backend.region = config.region

config.frontend.name = `${config.name}-${config.stage}`
config.frontend.credentials = config.credentials
config.frontend.region = config.region

return config
}

class RealtimeApp extends Component {
async default(inputs = {}) {
this.cli.status('Deploying Realtime App')
const config = getConfig(inputs)

const website = new Website(`${this.id}.website`)
const socket = new Socket(`${this.id}.socket`)

// deploy in parallel like a boss!
const outputs = await all([website(config.frontend), socket(config.backend)])
const websiteOutputs = outputs[0]
const socketOutputs = outputs[1]

this.cli.success('Realtime App Deployed')
this.cli.log('')
this.cli.output('Socket URL', ` ${socketOutputs.websockets.url}`)
this.cli.output('Website URL', `${websiteOutputs.url}`)

// this high level component doesn't need to save any state!

return { website: websiteOutputs, socket: socketOutputs }
}

async remove() {
// this remove function just calls remove on the child components
// it doesn't even need any inputs at all since all is available in children state!
this.cli.status('Removing Realtime App')

const website = new Website(`${this.id}.website`)
const socket = new Socket(`${this.id}.socket`)

const outputs = await all([website.remove(), socket.remove()])

const websiteOutputs = outputs[0]
const socketOutputs = outputs[1]

this.cli.success('Realtime App Removed')

return { website: websiteOutputs, socket: socketOutputs }
}
}

module.exports = RealtimeApp
9 changes: 7 additions & 2 deletions components/Role/serverless.js
Expand Up @@ -26,7 +26,7 @@ const defaults = {
class Role extends Component {
async default(inputs = {}) {
const config = mergeDeep(defaults, inputs)
const iam = new aws.IAM()
const iam = new aws.IAM(inputs)

const prevRole = await getRole({ iam, ...config })

Expand All @@ -48,6 +48,11 @@ class Role extends Component {
}
}

if (this.state.name && this.state.name !== config.name) {
this.cli.status(`Removing Previous Role`)
await deleteRole({ iam, name: this.state.name, policy: config.policy })
}

this.state.arn = config.arn
this.state.name = config.name
this.save()
Expand All @@ -66,7 +71,7 @@ class Role extends Component {
const config = mergeDeep(defaults, inputs)
config.name = inputs.name || this.state.name || defaults.name

const iam = new aws.IAM()
const iam = new aws.IAM(inputs)
this.cli.status(`Removing Role`)
await deleteRole({ iam, ...config })

Expand Down
10 changes: 5 additions & 5 deletions components/Socket/serverless.js
Expand Up @@ -45,24 +45,24 @@ class Socket extends Component {
this.cli.success(`Socket Deployed`)
this.cli.output('URL', ` ${websocketsOutputs.url}`)

return { lambdaOutputs, websocketsOutputs }
return { lambda: lambdaOutputs, websockets: websocketsOutputs }
}

async remove(inputs = {}) {
this.cli.status(`Removing Socket`)

const lambda = new Lambda(`${this.id}.lambda`)
const websockets = new Lambda(`${this.id}.websockets`)
const websockets = new WebSockets(`${this.id}.websockets`)

const lambdaOutputs = await lambda.remove(inputs)
const websocketsOutputs = await websockets.remove(inputs)
const lambdaOutputs = await lambda.remove()
const websocketsOutputs = await websockets.remove()

this.state = {}
this.save()

this.cli.success(`Socket Removed`)

return { lambdaOutputs, websocketsOutputs }
return { lambda: lambdaOutputs, websockets: websocketsOutputs }
}

/* /\
Expand Down
45 changes: 16 additions & 29 deletions components/WebSockets/serverless.js
@@ -1,8 +1,5 @@
const aws = require('aws-sdk')
const { pick, mergeDeep, filter, keys, not, map, all } = require('../../src/utils')

const Component = require('../Component/serverless')

const {
getApiId,
createApi,
Expand All @@ -16,19 +13,16 @@ const {
getWebsocketUrl
} = require('./utils')

const Component = require('../Component/serverless')

const outputs = ['name', 'stage', 'description', 'routeSelectionExpression', 'routes', 'id', 'url']

const defaults = {
name: 'serverless',
stage: 'dev',
description: 'Serverless WebSockets',
routeSelectionExpression: '$request.body.action',
routes: {
// todo validation?
$connect: '',
$disconnect: '',
$default: ''
},
routes: {}, // key (route): value (lambda arn)
region: 'us-east-1'
}

Expand All @@ -38,34 +32,24 @@ class WebSockets extends Component {
const apig2 = new aws.ApiGatewayV2()
const lambda = new aws.Lambda()

config.id = await getApiId({ apig2, name: config.name })
config.id = await getApiId({ apig2, id: config.id || this.state.id }) // validate with provider

const definedRoutes = keys(config.routes || {})
const providerRoutes = await getRoutes({ apig2, id: config.id })

if (!config.id) {
this.cli.status(`Creating WebSockets`)

config.id = await createApi({
apig2,
name: config.name,
description: config.description,
routeSelectionExpression: config.routeSelectionExpression
})
config.id = await createApi({ apig2, ...config })
} else {
this.cli.status(`Updating WebSockets`)

await updateApi({
apig2,
id: config.id,
description: config.description,
routeSelectionExpression: config.routeSelectionExpression
})
await updateApi({ apig2, ...config })
}

const routesToDeploy = filter((route) => not(providerRoutes.includes(route)), definedRoutes)
const routesToRemove = filter((route) => not(definedRoutes.includes(route)), providerRoutes)

this.cli.status(`Updating Routes`)

// deploy defined routes that does not exist in provider
await all(
map(async (route) => {
Expand All @@ -83,7 +67,13 @@ class WebSockets extends Component {

config.url = getWebsocketUrl({ id: config.id, region: config.region, stage: config.stage })

this.state.name = config.name
// if the user has changed the id,
// remove the previous API
if (this.state.id && this.state.id !== config.id) {
this.cli.status(`Removing Previous WebSockets`)
await removeApi({ apig2, id: config.id })
}

this.state.id = config.id
this.state.url = config.url
this.save()
Expand All @@ -103,13 +93,10 @@ class WebSockets extends Component {
}

async remove(inputs = {}) {
// todo remove by id from state
const config = { ...defaults, ...inputs }
config.name = inputs.name || this.state.name
config.id = config.id || this.state.id
const apig2 = new aws.ApiGatewayV2()

config.id = await getApiId({ apig2, name: config.name })

if (config.id) {
this.cli.status(`Removing WebSockets`)
await removeApi({ apig2, id: config.id })
Expand Down

0 comments on commit a82fda8

Please sign in to comment.