Skip to content

Commit

Permalink
Add HTTP response Helper & Make initNammatham create Azure Functions …
Browse files Browse the repository at this point in the history
…for default (#131)
  • Loading branch information
mildronize authored Apr 11, 2024
2 parents 803fe33 + b849ed4 commit dd3c50b
Show file tree
Hide file tree
Showing 20 changed files with 176 additions and 90 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,5 @@ inversify-express-utils

.azurite
tmp
.nx
.nx
sample_code
23 changes: 11 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ Type-safe Serverless Library for Azure Functions and friends
>
> Note: [Nammatham v1](https://www.npmjs.com/package/nammatham) is currently in maintenance mode. no new features are actively being developed
You're reading v2 docs


| Version | Status | Azure Functions <br>Node.js Lib | Branch | Build Status |
| ------- | ----------- | ----------------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
Expand All @@ -37,31 +39,28 @@ Nammatham (นามธรรม in Thai, pronounced `/naam ma tham/`, means **a
npm install nammatham@alpha
```

You can also install independently
```bash
npm install @nammatham/core @nammatham/azure-functions @nammatham/express
```

### Example

You can see [examples](examples) or follow the minimal app getting started below:

> `initNammatham.create()` is a factory function for creating Nammatham App, it's a wrapper for Azure Functions App.
```typescript
import { initNammatham, AzureFunctionsAdapter, expressPlugin } from "nammatham";
import { initNammatham, expressPlugin } from 'nammatham';

const n = initNammatham.create(new AzureFunctionsAdapter());
const n = initNammatham.create();
const func = n.func;
const app = n.app;

const helloFunction = func
.httpGet('hello', {
route: 'hello-world',
})
.handler(async ({trigger, context}) => {
context.log('HTTP trigger function processed a request.');
context.debug(`Http function processed request for url "${trigger.url}"`);
const name = trigger.query.get('name') || (await trigger.text()) || 'world';
return { body: `Hello, ${name}!` };
.handler(async c => {
c.context.log('HTTP trigger function processed a request.');
c.context.debug(`Http function processed request for url "${c.trigger.url}"`);
const name = c.trigger.query.get('name') || (await c.trigger.text()) || 'world';
return c.text(`Hello, ${name}!`);
});

app.addFunctions(helloFunction);
Expand Down
14 changes: 7 additions & 7 deletions examples/azure-functions-minimal/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AzureFunctionsAdapter, initNammatham, expressPlugin } from 'nammatham';
import { initNammatham, expressPlugin } from 'nammatham';

const n = initNammatham.create(new AzureFunctionsAdapter());
const n = initNammatham.create();
const func = n.func;
const app = n.app;

Expand All @@ -9,11 +9,11 @@ app.addFunctions(
.httpGet('hello', {
route: 'hello-world',
})
.handler(async ({ trigger, context }) => {
context.log('HTTP trigger function processed a request.');
context.debug(`Http function processed request for url "${trigger.url}"`);
const name = trigger.query.get('name') || (await trigger.text()) || 'world';
return { body: `Hello, ${name}!` };
.handler(async c => {
c.context.log('HTTP trigger function processed a request.');
c.context.debug(`Http function processed request for url "${c.trigger.url}"`);
const name = c.trigger.query.get('name') || (await c.trigger.text()) || 'world';
return c.text(`Hello, ${name}!`);
})
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default func
.timer('watcher', {
schedule: '*/5 * * * * *',
})
.handler(async ({trigger, context}) => {
context.info('Timer triggered!');
.handler(async ({ trigger, context }) => {
context.info('Timer triggered!');
trigger.isPastDue ? context.info('Timer is past due!') : null;
});
4 changes: 2 additions & 2 deletions examples/azure-functions-timer-trigger/src/nammatham.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { initNammatham, AzureFunctionsAdapter } from 'nammatham';
import { initNammatham } from 'nammatham';

const n = initNammatham.create(new AzureFunctionsAdapter());
const n = initNammatham.create();
n.func;
// ^?
export const func = n.func;
Expand Down
12 changes: 5 additions & 7 deletions examples/azure-functions-with-inversify/src/functions/hello.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,10 @@ export default func
.setContext({
services,
})
.handler(async ({ trigger, context, services }) => {
context.log('HTTP trigger function processed a request.');
.handler(async c => {
c.context.log('HTTP trigger function processed a request.');

return {
jsonBody: {
data: 'hello world' + services.dataService.getData(),
},
};
return c.json({
data: 'hello world' + services.dataService.getData(),
});
});
4 changes: 2 additions & 2 deletions examples/azure-functions-with-inversify/src/nammatham.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { initNammatham, AzureFunctionsAdapter } from 'nammatham';
import { initNammatham } from 'nammatham';

const n = initNammatham.create(new AzureFunctionsAdapter());
const n = initNammatham.create();
n.func;
// ^?
export const func = n.func;
Expand Down
19 changes: 9 additions & 10 deletions examples/azure-functions-with-test/__test__/helloFunction.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { expect, test } from 'vitest';
import helloFunc from '../src/functions/hello';
import { HttpRequest, InvocationContext } from '@azure/functions';
import { NammathamContext } from 'nammatham';

test('helloFunc', async () => {
const handler = helloFunc.getHandler();
const result = await handler({
context: new InvocationContext(),
trigger: new HttpRequest({
method: 'GET',
url: 'http://localhost:3000/api/hello-world',
query: {
name: 'world',
},
}),
});
const nammathamConext = new NammathamContext(new InvocationContext(), new HttpRequest({
method: 'GET',
url: 'http://localhost:3000/api/hello-world',
query: {
name: 'world',
},
}))
const result = await handler(nammathamConext);

expect(result).toEqual({
jsonBody: {
Expand Down
19 changes: 8 additions & 11 deletions examples/azure-functions-with-test/src/functions/hello.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,17 @@ export default func
.httpGet('hello', {
route: 'hello-world',
})
.handler(async ({ trigger, context }) => {
context.log('HTTP trigger function processed a request.');
context.debug(`Http function processed request for url "${trigger.url}"`);
const name = trigger.query.get('name') || (await trigger.text()) || 'world';
.handler(async c => {
c.context.log('HTTP trigger function processed a request.');
c.context.debug(`Http function processed request for url "${c.trigger.url}"`);
const name = c.trigger.query.get('name') || (await c.trigger.text()) || 'world';
if (name === 'error') {
throw new Error('this is an error');
}
const result = {
return c.json({
data: {
name: name,
message: `Hello, ${name}!`
}
}
return {
jsonBody: result,
}
message: `Hello, ${name}!`,
},
});
});
3 changes: 1 addition & 2 deletions examples/azure-functions-with-test/src/nammatham.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { initNammatham } from 'nammatham';
import { AzureFunctionsAdapter } from 'nammatham';

const n = initNammatham.create(new AzureFunctionsAdapter());
const n = initNammatham.create();

export const func = n.func;
export const app = n.app;
4 changes: 2 additions & 2 deletions examples/azure-functions-with-trpc/src/nammatham.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { initNammatham, AzureFunctionsAdapter } from 'nammatham';
import { initNammatham } from 'nammatham';

const n = initNammatham.create(new AzureFunctionsAdapter());
const n = initNammatham.create();

export const func = n.func;
export const app = n.app;
12 changes: 5 additions & 7 deletions examples/azure-functions/src/functions/blob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,10 @@ export default func
extraInputs: [blobInput],
extraOutputs: [blobOutput],
})
.handler(({ trigger, context }) => {
context.log('function processed work item:', trigger);
const blobInputValue = context.extraInputs.get(blobOutput);
.handler(c => {
c.context.log('function processed work item:', c.trigger);
const blobInputValue = c.context.extraInputs.get(blobOutput);

context.extraOutputs.set(blobOutput, blobInputValue);
return {
body: `Hello ${blobInputValue}`,
};
c.context.extraOutputs.set(blobOutput, blobInputValue);
return c.text(`Hello ${blobInputValue}`);
});
19 changes: 8 additions & 11 deletions examples/azure-functions/src/functions/hello.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,17 @@ export default func
.httpGet('hello', {
route: 'hello-world',
})
.handler(async ({ trigger, context }) => {
context.log('HTTP trigger function processed a request.');
context.debug(`Http function processed request for url "${trigger.url}"`);
const name = trigger.query.get('name') || (await trigger.text()) || 'world';
.handler(async c => {
c.context.log('HTTP trigger function processed a request.');
c.context.debug(`Http function processed request for url "${c.trigger.url}"`);
const name = c.trigger.query.get('name') || (await c.trigger.text()) || 'world';
if (name === 'error') {
throw new Error('this is an error');
}
const result = {
return c.json({
data: {
name: name,
message: `Hello, ${name}!`
}
}
return {
jsonBody: result,
}
message: `Hello, ${name}!`,
},
});
});
4 changes: 2 additions & 2 deletions examples/azure-functions/src/nammatham.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { initNammatham, AzureFunctionsAdapter } from 'nammatham';
import { initNammatham } from 'nammatham';

const n = initNammatham.create(new AzureFunctionsAdapter());
const n = initNammatham.create();
n.func;
// ^?
export const func = n.func;
Expand Down
7 changes: 4 additions & 3 deletions packages/azure-functions/src/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export class AzureFunctionsHandler<
// eslint-disable-next-line @typescript-eslint/ban-types
ExtraContext extends Record<string, unknown> = {}
> extends BaseHandler<HandlerFunction<TTriggerType, TReturnType, ExtraContext>> {
context: ExtraContext = {} as ExtraContext;
extraContext: ExtraContext = {} as ExtraContext;
protected funcHandler!: HandlerFunction<TTriggerType, TReturnType, ExtraContext>;

constructor(
Expand All @@ -31,7 +31,8 @@ export class AzureFunctionsHandler<
build(): AzureFunctionsEndpoint<TTriggerType, TReturnType> {
const invokeHandler = (triggerInput: TTriggerType, innocationContext: InvocationContext) => {
const nammathamContext = new NammathamContext(innocationContext, triggerInput);
return this.funcHandler({ ...nammathamContext, ...this.context });
Object.assign(nammathamContext, this.extraContext);
return this.funcHandler(nammathamContext as NammathamContext<TTriggerType> & ExtraContext);
};
return {
...this.functionOption,
Expand All @@ -43,7 +44,7 @@ export class AzureFunctionsHandler<
}

setContext<NewItem extends Record<string, unknown>>(context: NewItem) {
this.context = { ...this.context, ...context };
this.extraContext = { ...this.extraContext, ...context };
return this as AzureFunctionsHandler<TTriggerType, TReturnType, ExtraContext & NewItem>;
}

Expand Down
73 changes: 73 additions & 0 deletions packages/azure-functions/src/http/HttpResponse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as type from '@azure/functions';

/**
* Re-export from Azure Functions;
*/
export type AzureHttpResponse = type.HttpResponseInit | type.HttpResponse;
export type Request = type.HttpRequest;

/**
* Http Response Builder wrapping around azure/functions
*/
export type Header = Record<string, string>;
export class HttpResponse {
protected _headers: Header;
protected _cookies: NonNullable<type.HttpResponseInit['cookies']> = [];
protected _httpResponse: type.HttpResponseInit;

constructor(responseInit?: Omit<type.HttpResponseInit, 'headers'> & { headers: Header }) {
this._httpResponse = {
...responseInit,
};
this._headers = responseInit?.headers ?? {};
}

private build(): type.HttpResponseInit {
const result = {
...this._httpResponse,
};
if (Object.values(this._headers).length > 0) {
result.headers = this._headers;
}

if (this._cookies.length > 0) {
result.cookies = this._cookies;
}
return result;
}

public text(body?: string) {
return new type.HttpResponse({
...this.build(),
body,
});
}

public json<T extends object>(jsonBody: T): AzureHttpResponse {
return {
...this.build(),
jsonBody,
};
}

public httpResponse(responseInit?: type.HttpResponseInit) {
return new type.HttpResponse(responseInit);
}

public header(key: string, value: string) {
this._headers[key] = value;
return this;
}

public headers(headers: Header) {
for (const [key, value] of Object.entries(headers)) {
this._headers[key] = value;
}
return this;
}

public status(status: number) {
this._httpResponse.status = status;
return this;
}
}
6 changes: 3 additions & 3 deletions packages/azure-functions/src/nammatham-context.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { InvocationContext } from '@azure/functions';
import { type InvocationContext } from '@azure/functions';

import { NammathamContextBase } from '@nammatham/core';
import { HttpResponse } from './http/HttpResponse';

export class NammathamContext<TTriggerType> extends NammathamContextBase {
export class NammathamContext<TTriggerType> extends HttpResponse {
constructor(public readonly context: InvocationContext, public readonly trigger: TTriggerType) {
super();
}
Expand Down
Loading

0 comments on commit dd3c50b

Please sign in to comment.