Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for azure function v4 event source #529

Merged
merged 5 commits into from
Jun 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ exports.handler = handler

## Azure

### Async Azure Function (v3) handler wrapper
### Async Azure Function v3/v4 handler wrapper

The only Azure Function specific code you need to write is a simple `index.js` and a `function.json` like below.

Expand Down
2 changes: 2 additions & 0 deletions __tests__/integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ describe.each(EACH_MATRIX)('%s:%s: integration tests', (eventSourceName, framewo
delete response.multiValueHeaders.etag
delete response.multiValueHeaders['last-modified']
break
case 'azureHttpFunctionV4':
case 'azureHttpFunctionV3':
expectedResponse.body = Buffer.from(samLogoBase64, 'base64')
expectedResponse.isBase64Encoded = false
Expand Down Expand Up @@ -424,6 +425,7 @@ describe.each(EACH_MATRIX)('%s:%s: integration tests', (eventSourceName, framewo
})

switch (eventSourceName) {
case 'azureHttpFunctionV4':
case 'azureHttpFunctionV3':
expectedResponse.cookies = [
{
Expand Down
2 changes: 1 addition & 1 deletion examples/azure-http-function-v3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ These prerequisites are required to run and debug your functions locally.

- Visual Studio Code
- Visual Studio Code - Azure Functions extension
- Azure Functions Core Tools
- Azure Functions Core Tools (`npm install -g azure-functions-core-tools@3 --unsafe-perm true`)
- Node.js

_For further information on how to run an Azure Function locally please check [Develop Azure Functions by using Visual Studio Code](https://docs.microsoft.com/en-us/azure/azure-functions/functions-develop-vs-code?tabs=nodejs)._
Expand Down
14 changes: 7 additions & 7 deletions examples/azure-http-function-v3/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/azure-http-function-v3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"start": "func start"
},
"dependencies": {
"@vendia/serverless-express": "4.5.2",
"@vendia/serverless-express": "4.8.2",
"body-parser": "1.19.1",
"compression": "^1.7.4",
"cors": "2.8.5",
Expand Down
99 changes: 99 additions & 0 deletions examples/azure-http-function-v4/HttpExample/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
const path = require('path')
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const compression = require('compression')
const { getCurrentInvoke } = require('../../../src/index') // require('@vendia/serverless-express')
const ejs = require('ejs').__express
const app = express()
const router = express.Router()

app.set('view engine', 'ejs')
app.engine('.ejs', ejs)

router.use(compression())
router.use(cors())
router.use(bodyParser.json())
router.use(bodyParser.urlencoded({ extended: true }))

// NOTE: tests can't find the views directory without this
app.set('views', path.join(__dirname, 'views'))

router.get('/api', (req, res) => {
const currentInvoke = getCurrentInvoke()
const { event = {} } = currentInvoke
const { requestContext = {} } = event
const { domainName = 'localhost:7071' } = requestContext
const apiUrl = `https://${domainName}`
res.render('index', { apiUrl })
})

router.get('/api/vendia', (req, res) => {
res.sendFile(path.join(__dirname, 'vendia-logo.png'))
})

router.get('/api/users', (req, res) => {
res.json(users)
})

router.get('/api/users/:userId', (req, res) => {
const user = getUser(req.params.userId)

if (!user) return res.status(404).json({})

return res.json(user)
})

router.post('/api/users', (req, res) => {
const user = {
id: ++userIdCounter,
name: req.body.name
}
users.push(user)
res.status(201).json(user)
})

router.put('/api/users/:userId', (req, res) => {
const user = getUser(req.params.userId)

if (!user) return res.status(404).json({})

user.name = req.body.name
res.json(user)
})

router.delete('/api/users/:userId', (req, res) => {
const userIndex = getUserIndex(req.params.userId)

if (userIndex === -1) return res.status(404).json({})

users.splice(userIndex, 1)
res.json(users)
})

router.get('/api/cookie', (req, res) => {
res.cookie('Foo', 'bar')
res.cookie('Fizz', 'buzz')
res.json({})
})

const getUser = (userId) => users.find(u => u.id === parseInt(userId))
const getUserIndex = (userId) => users.findIndex(u => u.id === parseInt(userId))

// Ephemeral in-memory data store
const users = [{
id: 1,
name: 'Joe'
}, {
id: 2,
name: 'Jane'
}]
let userIdCounter = users.length

// The serverless-express library creates a server and listens on a Unix
// Domain Socket for you, so you can remove the usual call to app.listen.
// app.listen(3000)
app.use('/', router)

// Export your express server so you can import it in the lambda function.
module.exports = app
16 changes: 16 additions & 0 deletions examples/azure-http-function-v4/HttpExample/function.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"route": "{*segments}"
},
{
"type": "http",
"direction": "out",
"name": "$return"
}
]
}
7 changes: 7 additions & 0 deletions examples/azure-http-function-v4/HttpExample/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
const serverlessExpress = require('../../../src/index') // require('@vendia/serverless-express')
const app = require('./app')
const cachedServerlessExpress = serverlessExpress({ app })

module.exports = async function (context, req) {
return cachedServerlessExpress(context, req)
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
115 changes: 115 additions & 0 deletions examples/azure-http-function-v4/HttpExample/views/index.ejs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<!DOCTYPE html>
<html>

<head>
<title>My Serverless Application</title>
<style>
body {
width: 650px;
margin: auto;
}

h1 {
text-align: center;
}

.resources>h2 {
margin-bottom: 0;
}

.resource>h3,
.resource>p {
display: inline-block;
margin-bottom: 0.5rem;
}

.resource>code {
display: block;
background-color: #eff0f1;
color: #393318;
padding: 5px;
font-family: Consolas, Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace, sans-serif;
white-space: nowrap;
overflow-x: auto;
}

.vendia-logo {
display: block;
width: 10rem;
margin: auto;
}

form {
margin-bottom: 1rem;
}

.form-group {
padding-bottom: 1rem;
}

label {
display: block;
}
</style>
</head>

<body>
<h1>My Serverless Application</h1><img class="vendia-logo" src="/api/vendia">
<p>Welcome to your Azure Function serverless application. This example application has several resources configured for you to explore. State is stored in memory in a given container, and is therefore ephemeral.</p>
<section class="resources">
<h2>Resources</h2>
<section class="resource">
<h3><a class="resource-example-link" href="/api/users">GET /users</a></h3>
<p> &nbsp;Returns a list of all users.</p><code>curl <span><%= apiUrl %></span>/users -H 'accept: application/json'</code>
</section>
<section class="resource">
<h3>POST /users</h3>
<p> &nbsp;Creates a new user.</p><code>curl <span><%= apiUrl %></span>/api/users -X POST -d '{"name":"Sam"}' -H 'accept: application/json'</code>
</section>
<section class="resource">
<h3><a class="resource-example-link" href="/api/users/1">GET /users/:userId</a></h3>
<p> &nbsp;Returns a single user.</p><code>curl <span><%= apiUrl %></span>/api/users/1 -H 'accept: application/json'</code>
</section>
<section class="resource">
<h3>PUT /users/:userId</h3>
<p> &nbsp;Updates an existing user.</p><code>curl <span><%= apiUrl %></span>/api/users/1 -X PUT -d '{"name":"Samantha"}' -H 'accept: application/json'</code>
</section>
<section class="resource">
<h3>DELETE /users/:userId</h3>
<p> &nbsp;Deletes an existing user.</p><code>curl <span><%= apiUrl %></span>/api/users/1 -X DELETE -H 'accept: application/json'</code>
</section>
</section>
<section class="form">
<h2>Form</h2>
<p>Experiment with POST and PUT via the form below. Leave the id empty to create a new user (POST) or enter an id to update a user's name (PUT).</p>
<form>
<div class="form-group"><label for="idField">user id</label><input type="text" name="id" id="idField" placeholder="optional"></div>
<div class="form-group"><label for="nameField">name</label><input type="text" name="name" id="nameField"></div><input type="submit">
</form>
</section>
<script>
var form = document.querySelector('form')
form.addEventListener('submit', function(event) {
event.preventDefault()
var id = document.getElementById('idField').value
var name = document.getElementById('nameField').value
var endpoint = id ? 'users/' + id : 'users'

if (!window.fetch) {
alert('Your browser does not have fetch support, which this demo uses. Try again in a modern browser (https://caniuse.com/fetch) or modify the example to use an alternate approach (e.g. XMLHttpRequest or your library of choice.)')
}

return fetch(endpoint, {
method: id ? 'PUT' : 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({
name: name
})
})
})
</script>
</body>

</html>
42 changes: 42 additions & 0 deletions examples/azure-http-function-v4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Example - Azure Function HTTP v4

This example uses the local tools provided by Microsoft to simulate an Azure Function environment. This means there is no Azure Account required to run this example.

## Run local requirements

These prerequisites are required to run and debug your functions locally.

- Visual Studio Code
- Visual Studio Code - Azure Functions extension
- Azure Functions Core Tools (`npm install -g azure-functions-core-tools@4 --unsafe-perm true`)
- Node.js

_For further information on how to run an Azure Function locally please check [Develop Azure Functions by using Visual Studio Code](https://docs.microsoft.com/en-us/azure/azure-functions/functions-develop-vs-code?tabs=nodejs)._


## Installation & Usage

After all requirements are fulfilled you can install all dependencies for the `azure-http-function-v4` with npm.

```bash
cd examples/azure-http-function-v4
npm install
npm run start
```



After you run the last command you should see an output like this:

```bash
Azure Functions Core Tools
Core Tools Version: 4.0.4590
Function Runtime Version: 4.5.2.18383


Functions:

HttpExample: http://localhost:7071/api/{*segments}
```

Now you can navigate to `http://localhost:7071/api/` to display the `My Serverless Application` page.
15 changes: 15 additions & 0 deletions examples/azure-http-function-v4/host.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[3.3.0, 4.0.0)"
}
}
8 changes: 8 additions & 0 deletions examples/azure-http-function-v4/local.settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"IsEncrypted": false,
"Values": {
"FUNCTIONS_WORKER_RUNTIME": "node",
"AzureWebJobsStorage": "",
"FUNCTIONS_EXTENSION_VERSION": "~4"
}
}