Skip to content

Commit d4d8a55

Browse files
Merge pull request #105 from bootgs/feature/new-response-features
Feature/new response features
2 parents c25d7b2 + a23204e commit d4d8a55

43 files changed

Lines changed: 1486 additions & 282 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 326 additions & 52 deletions
Large diffs are not rendered by default.

src/controller/AsyncBootApplication.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isString } from "apps-script-utils";
2-
import { ApplicationConfig, AppsScriptMenuProxy } from "../domain/types";
2+
import { ApplicationConfig, AppsScriptMenuProxy, HttpRequest, HttpResponse } from "../domain/types";
33
import { AppsScriptEventType, RequestMethod } from "../domain/enums";
44
import { BaseBootApplication } from "./BaseBootApplication";
55

@@ -59,8 +59,12 @@ export class AsyncBootApplication extends BaseBootApplication {
5959
public async doGet(
6060
event: GoogleAppsScript.Events.DoGet
6161
): Promise<GoogleAppsScript.HTML.HtmlOutput | GoogleAppsScript.Content.TextOutput | string> {
62-
const { request, response } = this.handleHttpRequestInternal(RequestMethod.GET, event);
63-
const resolvedResponse = response instanceof Promise ? await response : response;
62+
const {
63+
request,
64+
response
65+
}: { request: HttpRequest; response: HttpResponse | Promise<HttpResponse> } =
66+
this.handleHttpRequestInternal(RequestMethod.GET, event);
67+
const resolvedResponse: HttpResponse = response instanceof Promise ? await response : response;
6468
return this._responseBuilder.wrap(request, resolvedResponse);
6569
}
6670

@@ -73,8 +77,12 @@ export class AsyncBootApplication extends BaseBootApplication {
7377
public async doPost(
7478
event: GoogleAppsScript.Events.DoPost
7579
): Promise<GoogleAppsScript.HTML.HtmlOutput | GoogleAppsScript.Content.TextOutput | string> {
76-
const { request, response } = this.handleHttpRequestInternal(RequestMethod.POST, event);
77-
const resolvedResponse = response instanceof Promise ? await response : response;
80+
const {
81+
request,
82+
response
83+
}: { request: HttpRequest; response: HttpResponse | Promise<HttpResponse> } =
84+
this.handleHttpRequestInternal(RequestMethod.POST, event);
85+
const resolvedResponse: HttpResponse = response instanceof Promise ? await response : response;
7886
return this._responseBuilder.wrap(request, resolvedResponse);
7987
}
8088

src/controller/BaseBootApplication.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
Provider,
1111
RouteMetadata
1212
} from "../domain/types";
13-
import { RequestMethod } from "../domain/enums";
13+
import { ContentMimeType, RequestMethod } from "../domain/enums";
1414
import {
1515
EventDispatcher,
1616
RequestFactory,
@@ -155,8 +155,11 @@ export abstract class BaseBootApplication {
155155
req: HttpRequest,
156156
status: number | undefined,
157157
headers: HttpHeaders | undefined,
158-
data: unknown
159-
): HttpResponse => this._responseBuilder.create(req, status, headers, data)
158+
data: unknown,
159+
produce?: ContentMimeType,
160+
isResponseBody?: boolean
161+
): HttpResponse =>
162+
this._responseBuilder.create(req, status, headers, data, produce, isResponseBody)
160163
);
161164

162165
return { request, response };

src/controller/BootApplication.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isString } from "apps-script-utils";
2-
import { ApplicationConfig, AppsScriptMenuProxy, HttpResponse } from "../domain/types";
2+
import { ApplicationConfig, AppsScriptMenuProxy, HttpRequest, HttpResponse } from "../domain/types";
33
import { AppsScriptEventType, RequestMethod } from "../domain/enums";
44
import { BaseBootApplication } from "./BaseBootApplication";
55

@@ -59,7 +59,11 @@ export class BootApplication extends BaseBootApplication {
5959
public doGet(
6060
event: GoogleAppsScript.Events.DoGet
6161
): GoogleAppsScript.HTML.HtmlOutput | GoogleAppsScript.Content.TextOutput | string {
62-
const { request, response } = this.handleHttpRequestInternal(RequestMethod.GET, event);
62+
const {
63+
request,
64+
response
65+
}: { request: HttpRequest; response: HttpResponse | Promise<HttpResponse> } =
66+
this.handleHttpRequestInternal(RequestMethod.GET, event);
6367

6468
if (response instanceof Promise) {
6569
return response.then((res: HttpResponse): any =>
@@ -79,7 +83,11 @@ export class BootApplication extends BaseBootApplication {
7983
public doPost(
8084
event: GoogleAppsScript.Events.DoPost
8185
): GoogleAppsScript.HTML.HtmlOutput | GoogleAppsScript.Content.TextOutput | string {
82-
const { request, response } = this.handleHttpRequestInternal(RequestMethod.POST, event);
86+
const {
87+
request,
88+
response
89+
}: { request: HttpRequest; response: HttpResponse | Promise<HttpResponse> } =
90+
this.handleHttpRequestInternal(RequestMethod.POST, event);
8391

8492
if (response instanceof Promise) {
8593
return response.then((res: HttpResponse): any =>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import { RESPONSE_BODY_METADATA } from "../../domain/constants";
2+
3+
/**
4+
* Decorator that tells the controller that the return object should be serialized
5+
* directly into the HTTP response body.
6+
*
7+
* In this framework, this is the default behavior for all controller methods,
8+
* but this decorator is provided for compatibility with Spring Boot patterns.
9+
*
10+
* @returns {MethodDecorator & ClassDecorator} A decorator.
11+
*
12+
* @example
13+
* ```TypeScript
14+
* import { ResponseBody, GetMapping, Controller } from "bootgs";
15+
*
16+
* @Controller()
17+
* class MyController {
18+
* @GetMapping("/data")
19+
* @ResponseBody
20+
* getData() {
21+
* return { message: "Hello" };
22+
* }
23+
* }
24+
* ```
25+
*/
26+
export function ResponseBody(): MethodDecorator & ClassDecorator {
27+
return (
28+
target: object,
29+
propertyKey?: string | symbol,
30+
descriptor?: TypedPropertyDescriptor<any>
31+
): void => {
32+
if (propertyKey) {
33+
if (descriptor && descriptor.value) {
34+
Reflect.defineMetadata(RESPONSE_BODY_METADATA, true, descriptor.value);
35+
} else {
36+
Reflect.defineMetadata(RESPONSE_BODY_METADATA, true, target, propertyKey);
37+
}
38+
} else {
39+
Reflect.defineMetadata(RESPONSE_BODY_METADATA, true, target);
40+
}
41+
};
42+
}

src/controller/decorators/RestController.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1-
import { HttpController } from "../../controller/decorators";
1+
import { HttpController } from "./HttpController";
2+
import { ResponseBody } from "./ResponseBody";
23

34
/**
45
* Decorator that marks a class as a REST controller.
56
*
7+
* @param {string} [path] - The base path for all routes in the controller.
8+
* @returns {ClassDecorator} A decorator.
9+
*
610
* @example
711
* ```TypeScript
812
* import { RestController, Get, Param } from "bootgs";
@@ -16,4 +20,9 @@ import { HttpController } from "../../controller/decorators";
1620
* }
1721
* ```
1822
*/
19-
export const RestController = HttpController;
23+
export function RestController(path?: string): ClassDecorator {
24+
return (target: object): void => {
25+
HttpController(path)(target as any);
26+
ResponseBody()(target as any);
27+
};
28+
}

src/controller/decorators/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ export * from "./ResponseStatus";
1616
export * from "./ControllerAdvice";
1717
export * from "./ExceptionHandler";
1818
export * from "./Value";
19+
export * from "./ResponseBody";

src/controller/decorators/routing/DeleteMapping.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { RequestMethod } from "../../../domain/enums";
2-
import { createHttpDecorator } from "../../../repository";
2+
import { createHttpDecorator, HttpDecoratorOptions } from "../../../repository";
33

44
/**
55
* Route handler decorator for HTTP DELETE requests.
66
*
7-
* @param {string} [path] - Route path (optional).
7+
* @param {string | HttpDecoratorOptions} [options] - Route path or options (optional).
88
* @returns {MethodDecorator} A method decorator.
99
*
1010
* @example

src/controller/decorators/routing/GetMapping.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { RequestMethod } from "../../../domain/enums";
2-
import { createHttpDecorator } from "../../../repository";
2+
import { createHttpDecorator, HttpDecoratorOptions } from "../../../repository";
33

44
/**
55
* Route handler decorator for HTTP GET requests.
66
*
7-
* @param {string} [path] - Route path (optional).
7+
* @param {string | HttpDecoratorOptions} [options] - Route path or options (optional).
88
* @returns {MethodDecorator} A method decorator.
99
*
1010
* @example

src/controller/decorators/routing/HeadMapping.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { RequestMethod } from "../../../domain/enums";
2-
import { createHttpDecorator } from "../../../repository";
2+
import { createHttpDecorator, HttpDecoratorOptions } from "../../../repository";
33

44
/**
55
* Route handler decorator for HTTP HEAD requests.
66
*
7-
* @param {string} [path] - Route path (optional).
7+
* @param {string | HttpDecoratorOptions} [options] - Route path or options (optional).
88
* @returns {MethodDecorator} A method decorator.
99
*
1010
* @example

0 commit comments

Comments
 (0)