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

Hono Middleware for handling function.json (metadata) #142

Open
wants to merge 26 commits into
base: v3.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
349dfc7
feat(cli): poc function.json
mildronize May 11, 2024
1088a13
feat(cli): poc function.json & hono middleware
mildronize May 14, 2024
c1c6193
chore(cli): use tsup instead of tsc
mildronize May 14, 2024
939c47e
chore(cli): add comment for az func logging
mildronize May 14, 2024
2c98e68
misc(cli): cleaning
mildronize May 14, 2024
6d6d71b
feat(cli): add basic prop of func context in the middleware
mildronize May 14, 2024
681fe5c
refactor(nammatham): libs
mildronize May 15, 2024
b12bd78
chore(nammatham): exclude command in the main package
mildronize May 15, 2024
f748da2
fix(example): fix node example
mildronize May 15, 2024
1d83e1e
refactor(nammatham): rename class
mildronize May 15, 2024
042ed9e
feat(nammatham): add get/post http trigger helper
mildronize May 15, 2024
263ecee
feat(nammatham): handle functions metadata
mildronize May 15, 2024
96ad895
refactor(nammatham): azure function metadata
mildronize May 15, 2024
cb603ba
fix(example): fix node example
mildronize May 16, 2024
0e0ee34
misc(example): clean code
mildronize May 16, 2024
a792187
fix(nammatham): fix bun error on windows when using default import
mildronize May 16, 2024
a62d428
chore(infra): add e2e for local
mildronize May 16, 2024
83bd323
fix(infra): fix e2e for local test
mildronize May 17, 2024
ff616b0
fix(infra): split e2e-local to another project for seperate cases
mildronize May 17, 2024
7664717
fix(github-actions): Install Azure Functions Core Tools
mildronize May 17, 2024
569d4e9
fix(github-actions): download artifact path
mildronize May 17, 2024
1fe1610
fix(github-actions): health check on local e2e
mildronize May 17, 2024
f936eeb
fix(github-actions): fix permission denied from build artifact
mildronize May 17, 2024
c0c42ae
feat(github-actions): Run deploy to azure and e2e when push event only
mildronize May 17, 2024
beb3602
feat(github-actions): add pnpm & npm cache
mildronize May 17, 2024
2429aea
fix(github-actions): temp disable npm global cache until know how to
mildronize May 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 153 additions & 9 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,29 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: ${{env.node_version }}
- uses: pnpm/action-setup@v3

- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: ${{ env.pnpm_version }}
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install

- run: pnpm install
- name: Export the matrix for testing
id: deploy-matrix
run: pnpm exec nx run @infra/azure-functions:github-actions
Expand All @@ -61,11 +78,27 @@ jobs:
with:
node-version: ${{ matrix.version }}

- uses: pnpm/action-setup@v3
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: ${{ env.pnpm_version }}
- run: pnpm install
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install

- run: pnpm build

Expand All @@ -84,7 +117,101 @@ jobs:
path: examples/with-${{ matrix.runtime }}/.nmt/dist
retention-days: 1

e2e-local:
runs-on: ${{ matrix.os }}
needs:
- build
- get-matrix
timeout-minutes: 10
env:
artifact_download_path: build-functions
strategy:
matrix:
include: ${{fromJson(needs.get-matrix.outputs.matrix)}}

steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1

- name: Use Node.js ${{ matrix.version }}
if: matrix.runtime == 'node'
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.version }}

- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: ${{ env.pnpm_version }}
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install

# - uses: actions/cache@v4
# name: Cache global npm packages
# with:
# # a ternary operator
# # Ref: https://docs.github.com/en/actions/learn-github-actions/expressions#example
# path: ${{ runner.os == 'Windows' && '~/AppData/Roaming/npm-cache' || '~/.npm' }}
# key: ${{ runner.os }}-node-global-npm
# restore-keys: |
# ${{ runner.os }}-node-global-npm

- name: Install Azure Functions Core Tools
run: npm i -g azure-functions-core-tools@4 --unsafe-perm true

# - name: Cache global node_modules
# uses: actions/cache@v4
# with:
# path: ${{ runner.os == 'Windows' && '~/AppData/Roaming/npm/node_modules' || '/usr/local/lib/node_modules' }}
# key: ${{ runner.os }}-node-${{ github.sha }}-global-modules
# restore-keys: |
# ${{ runner.os }}-node-global-modules

- name: Azure Functions Version
run: func --version

- name: Download Artifact
uses: actions/download-artifact@v4
with:
name: ${{ matrix.resource_identifier_key }}
path: ${{ github.workspace }}/${{ env.artifact_download_path}}

- name: Set execute permissions (Linux/macOS)
if: runner.os != 'Windows'
run: |
chmod +x ${{ github.workspace }}/${{ env.artifact_download_path}}/main
ls -la ${{ github.workspace }}/${{ env.artifact_download_path}}

- name: Set execute permissions (Windows)
if: runner.os == 'Windows'
run: |
icacls.exe "${{ github.workspace }}/${{ env.artifact_download_path}}/main.exe" /grant Everyone:F
icacls.exe "${{ github.workspace }}/${{ env.artifact_download_path}}/main.exe" /T /C /Q
Get-ChildItem -Path "${{ github.workspace }}/${{ env.artifact_download_path}}" -Force | Format-Table -Property Mode,LastWriteTime,Length,Name

- name: Run E2E tests locally
run: pnpm exec nx run @infra/e2e-local:test
env:
CURRENT_WORKING_DIR: ${{ github.workspace }}/${{ env.artifact_download_path}}

deploy:
# Only run this job on push events
if: github.event_name == 'push'
runs-on: ubuntu-latest
needs:
- build
Expand All @@ -111,8 +238,9 @@ jobs:
app-name: nmt-e2e-${{ matrix.target }}-${{ secrets[matrix.resource_identifier_key] }}
package: .

e2e:
if: always()
e2e-azure:
# Only run this job on push events
if: github.event_name == 'push'
runs-on: ubuntu-latest
needs:
- build
Expand All @@ -133,14 +261,30 @@ jobs:
with:
node-version: ${{ env.node_version }}

- uses: pnpm/action-setup@v3
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
version: ${{ env.pnpm_version }}
- run: pnpm install
run_install: false

- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV

- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-

- name: Install dependencies
run: pnpm install

- name: Run E2E tests
run: pnpm exec nx run @infra/azure-functions:test
run: pnpm exec nx run @infra/azure-functions:test:e2e-remote
env:
AZURE_FUNCTIONS_URL: https://nmt-e2e-${{ matrix.target }}-${{ secrets[matrix.resource_identifier_key] }}.azurewebsites.net
AZURE_FUNCTIONS_API_KEY: ${{ secrets.AZURE_FUNCTIONS_HOST_KEY }}
39 changes: 39 additions & 0 deletions docs/bun.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Bun

## Issues

Summary:
- `lodash_default is not a function` error when using `lodash.camelcase` package
- Using default import of `lodash` package may cause this issue

Deployment at:
- https://github.com/thaitype/nammatham/actions/runs/9105382778
- Executable File from `bun-win-x64` target
- Using bun to compile into single executable file

Platform:

- Azure Functions Windows
- Bun v1.1.8 (Windows x64)

Package:

- lodash.camelcase v4.3.0
- nammatham on [commit 3f3b7b7 (2024-05-15)](https://github.com/thaitype/nammatham/commit/96ad8958c843553b0156d968b72e1d285c85041f)
- Build with tsup v8.0.1

```
Bun v1.1.8 (Windows x64)
n/a
at B:/~BUN/root/main.exe:3061:12
at http (B:/~BUN/root/main.exe:2983:5)
at http (B:/~BUN/root/main.exe:3031:13)
TypeError: lodash_default is not a function. (In 'lodash_default(options.name ?? options.route)', 'lodash_default' is undefined)
^
3031 | name: lodash_default(options.name ?? options.route),
3030 | this.functions.push({
3029 | }
3028 | throw new Error("Route or Name is required");
3027 | if (options.route === undefined && options.name === undefined) {
3026 | http(options) {
```
57 changes: 57 additions & 0 deletions docs/function-json.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Azure Functions Custom Handler

https://github.com/Azure/azure-functions-host/issues/5615
[HttpWorker]Add configuration flag in opt-out of http trigger request forward

https://github.com/anthonychu/azure-functions-deno-worker/blob/main/mod.ts

## Trace Log from Azure Function hosts

this is for `enableForwardingHttpRequest` to be `true`


Normal State

If only one output binding or input binding is used, the path should be with prefix `/api`

```
Invocation Request:Method: GET, RequestUri: 'http://127.0.0.1:63950/api/SimpleHttpTrigger', Version: 1.1, Content: <null>, Headers:
{
Accept: */*
Connection: keep-alive
Host: localhost:7071
User-Agent: PostmanRuntime/7.37.3
User-Agent: Azure-Functions-Host/4.33.2.22572
Accept-Encoding: gzip, deflate, br
Cache-Control: no-cache
Postman-Token: e6c94d37-a597-4b97-a88d-28c7e3d74748
X-Azure-Functions-HostVersion: 4.33.2.22572
X-Azure-Functions-InvocationId: b986fcf6-b00a-4a70-a8df-88ce7d39a321
}
```

with other bindings

If multiple output bindings are used, the path should be without prefix `/api`

```
Invocation Request:Method: POST, RequestUri: 'http: //127.0.0.1:64093/SimpleHttpTrigger', Version: 1.1, Content: System.Net.Http.ObjectContent`1[Microsoft.Azure.WebJobs.Script.Workers.Http.HttpScriptInvocationContext], Headers:

{
X-Azure-Functions-HostVersion: 4.33.2.22572
X-Azure-Functions-InvocationId: fa4fcd1a-a965-431e-8f10-a15c07b4fe2a
User-Agent: Azure-Functions-Host/4.33.2.22572
Content-Type: application/json; charset=utf-8
}

{"Data":{"req":{"Url":"http://localhost:7071/api/SimpleHttpTrigger","Method":"GET","Query":{},"Headers":{"Accept":["*/*"],"Connection":["keep-alive"],"Host":["localhost:7071"],"User-Agent":["PostmanRuntime/7.37.3"],"Accept-Encoding":["gzip, deflate, br"],"Cache-Control":["no-cache"],"Postman-Token":["f7cc026e-5cb0-4b7a-8f70-ed394a0e97ef"]},"Params":{},"Identities":[{"AuthenticationType":null,"IsAuthenticated":false,"Actor":null,"BootstrapContext":null,"Claims":[],"Label":null,"Name":null,"NameClaimType":"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name","RoleClaimType":"http://schemas.microsoft.com/ws/2008/06/identity/claims/role"}]}},"Metadata":{"Query":{},"Headers":{"Accept":"*/*","Connection":"keep-alive","Host":"localhost:7071","User-Agent":"PostmanRuntime/7.37.3","Accept-Encoding":"gzip, deflate, br","Cache-Control":"no-cache","Postman-Token":"f7cc026e-5cb0-4b7a-8f70-ed394a0e97ef"},"sys":{"MethodName":"SimpleHttpTrigger","UtcNow":"2024-05-14T15:14:15.128919Z","RandGuid":"7a85f722-f6ba-4add-9a45-7ed862be02ae"}}}

Sending invocation for function: 'SimpleHttpTrigger' invocationId: 'fa4fcd1a-a965-431e-8f10-a15c07b4fe2a'
```

## Logging

- Cannot specify log level in response return https://github.com/Azure/azure-functions-host/issues/6608
- Using `enableForwardingHttpRequest` to be `false`, the log will be shown in the app insights.
- JSON Logging may need more work in KQL to query https://github.com/Azure/azure-functions-host/issues/6608#issuecomment-779621939
- Logging cannot see the timestamp in the log
6 changes: 3 additions & 3 deletions examples/with-bun/nammatham.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ const nammathamConfig = {
excludedTypes: 'Request',
},
},
// logLevel: {
// default: 'Trace',
// },
logLevel: {
default: 'Trace',
},
},
},
};
Expand Down
3 changes: 2 additions & 1 deletion examples/with-bun/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
"license": "MIT",
"dependencies": {
"hono": "^4.3.2",
"nammatham": "3.0.0-canary.0"
"nammatham": "workspace:*"
},
"devDependencies": {
"@types/bun": "^1.1.2",
"tsx": "^4.9.1",
"typescript": "^5.4.5"
}
Expand Down
32 changes: 18 additions & 14 deletions examples/with-bun/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { Hono } from 'hono';
import { HonoAzureMiddleware, register } from 'nammatham';

const app = new Hono().basePath('/api');
// DO NOT SET `basePath` for Hono App, Azure Functions will handle it
const app = new Hono();

app.get('/SimpleHttpTrigger', c => {
const userAgent = c.req.header('user-agent');
console.log(`user agent is: ${userAgent}`);
const func = new HonoAzureMiddleware();

const invocationId = c.req.header('x-azure-functions-invocationid');
console.log(`invocationid is: ${invocationId}`);
app.all(...func.get('/SimpleHttpTrigger'), c => {
// Getting the function context
const context = c.var.context;

return c.text('Hello World from bun worker');
});
context.log('JavaScript HTTP trigger function processed a request.');
context.log(`invocationid is: ${context.invocationId}`);
context.log(`The third log message.`);

const port = parseInt(process.env.FUNCTIONS_CUSTOMHANDLER_PORT || '4000');
console.log(`Start server on on http://localhost:${port}`);
return context.json({
hello: 'world',
});
});

export default {
port,
fetch: app.fetch
};
export default register({
fetch: app.fetch,
func,
});
4 changes: 2 additions & 2 deletions examples/with-node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"license": "MIT",
"dependencies": {
"@hono/node-server": "^1.8.1",
"hono": "^4.3.2",
"nammatham": "3.0.0-canary.0"
"hono": "^4.3.6",
"nammatham": "workspace:*"
},
"devDependencies": {
"tsx": "^4.9.1",
Expand Down
Loading