gg-express 1.0.75 → 1.0.81

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "gg-express",
3
- "version": "1.0.75",
3
+ "version": "1.0.81",
4
4
  "description": "",
5
- "main": "dist/GGExpress.js",
5
+ "main": "dist/main.js",
6
6
  "scripts": {
7
7
  "build": "tsc && npm version patch --no-git-tag-version",
8
8
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/express": "^5.0.0",
27
- "typescript": "^5.8.3"
27
+ "typescript": "^5.9.3"
28
28
  },
29
29
  "keywords": [
30
30
  "express",
package/src/GGExpress.ts CHANGED
@@ -107,7 +107,7 @@ type MyResponse<
107
107
 
108
108
  const myExpressRouteList: {
109
109
  method: "get" | "post" | "put" | "delete"
110
- url: string | string[]
110
+ url: string
111
111
  requireParams: requireParamsStructure
112
112
  responseStructure: responseStructure
113
113
  }[] = []
@@ -148,11 +148,19 @@ export default class GGExpress<appName extends string> {
148
148
  ) => any
149
149
  >
150
150
  ) {
151
- myExpressRouteList.push({
152
- method: method,
153
- url: url,
154
- requireParams: options.requireParams,
155
- responseStructure: options.responseStructure,
151
+ let tempURL
152
+ if (Array.isArray(url)) {
153
+ tempURL = url
154
+ } else {
155
+ tempURL = [url]
156
+ }
157
+ tempURL.map((url) => {
158
+ myExpressRouteList.push({
159
+ method: method,
160
+ url: url,
161
+ requireParams: options.requireParams,
162
+ responseStructure: options.responseStructure,
163
+ })
156
164
  })
157
165
 
158
166
  return this.express[method](
package/src/main.ts ADDED
@@ -0,0 +1,3 @@
1
+ import GGExpress from "./GGExpress"
2
+ import GGExpressV2 from "./v2/GGExpressV2"
3
+ export { GGExpress, GGExpressV2 }
package/src/run.test.ts CHANGED
@@ -88,10 +88,8 @@ function run() {
88
88
  }
89
89
  )
90
90
 
91
- ggapp.get(
92
- ["/api/seed/memo/id",
93
- "/api/hotel/memo/id"
94
- ],
91
+ ggapp.get(
92
+ ["/api/seed/memo/id", "/api/hotel/memo/id"],
95
93
  {
96
94
  requireParams: {
97
95
  parameter: { files: "string[]" },
@@ -12,6 +12,30 @@ export interface staticRouteInterface_hotel {
12
12
  data: { id: number; name: string }[]
13
13
  }
14
14
  }
15
+ "/api/seed/memo/id": {
16
+ requireParams: {
17
+ parameter: { files: string[] }
18
+ data: { id: number; name: "A" | "B" }[]
19
+ }
20
+ responseStructure: {
21
+ status: "SUCCESS" | "ERROR"
22
+ message: string
23
+ parameter: { numberOfPeople: number; itemName: string }
24
+ data: { id: number; name: string }[]
25
+ }
26
+ }
27
+ "/api/hotel/memo/id": {
28
+ requireParams: {
29
+ parameter: { files: string[] }
30
+ data: { id: number; name: "A" | "B" }[]
31
+ }
32
+ responseStructure: {
33
+ status: "SUCCESS" | "ERROR"
34
+ message: string
35
+ parameter: { numberOfPeople: number; itemName: string }
36
+ data: { id: number; name: string }[]
37
+ }
38
+ }
15
39
  }
16
40
  post: {
17
41
  "/api/hotel/item": {
@@ -0,0 +1,190 @@
1
+ import { NextFunction, Request, Response } from "express"
2
+ import Express from "express-serve-static-core"
3
+ import fs from "fs"
4
+ import { convertRoot, InputParent } from "./typeResolver"
5
+ import { generateStaticRouteFileV2 } from "./generateStaticRouteFileV2"
6
+ import path from "path"
7
+ import { generateGGApi_v2 } from "./generateGGApi_v2"
8
+
9
+ type Unarray<T> = T extends (infer U)[] ? U : T
10
+ export type Method = "get" | "post" | "put" | "delete"
11
+
12
+ type ReqByMethod<M extends Method, RQ extends InputParent> = M extends "get"
13
+ ? convertRoot<MyRequestQuery<RQ>>
14
+ : convertRoot<MyRequestBody<RQ>>
15
+
16
+ type MyRequestQuery<T extends InputParent> = Request<{}, {}, {}, T, {}>
17
+ type MyRequestBody<T extends InputParent> = Request<{}, {}, T, {}, {}>
18
+
19
+ type MyResponse<T extends InputParent> = Response<{
20
+ status: "SUCCESS" | "ERROR" | "SERVER_ERROR"
21
+ message: string
22
+ data: convertRoot<T>
23
+ }>
24
+
25
+ const myExpressRouteList: {
26
+ method: "get" | "post" | "put" | "delete"
27
+ url: string
28
+ requireParams: InputParent
29
+ responseStructure: InputParent
30
+ }[] = []
31
+
32
+ export default class GGExpressV2<appName extends string> {
33
+ public express: Express.Express
34
+ private outputPath: string[]
35
+ appName: string
36
+ constructor(app: Express.Express, appName: appName, outputPath: string[]) {
37
+ this.express = app
38
+ this.outputPath = outputPath
39
+ this.appName = appName
40
+ }
41
+
42
+ private rootMethod<
43
+ M extends Method,
44
+ RQ extends InputParent,
45
+ RS extends InputParent
46
+ >(
47
+ method: M,
48
+ url: string | string[],
49
+ options: {
50
+ requireParams: RQ
51
+ responseStructure: RS
52
+ },
53
+ ...middlewares: Array<
54
+ (
55
+ req: ReqByMethod<M, RQ>,
56
+ res: MyResponse<RS>,
57
+ next: NextFunction
58
+ ) => void | Promise<void>
59
+ >
60
+ ): Express.Express {
61
+ let tempURL
62
+ if (Array.isArray(url)) {
63
+ tempURL = url
64
+ } else {
65
+ tempURL = [url]
66
+ }
67
+ tempURL.map((url) => {
68
+ myExpressRouteList.push({
69
+ method: method,
70
+ url: url,
71
+ requireParams: options.requireParams,
72
+ responseStructure: {
73
+ ...options.responseStructure,
74
+ },
75
+ })
76
+ })
77
+
78
+ return this.express[method](
79
+ url,
80
+ // Express handler ต้องกว้าง
81
+ (req: Request, res: Response, next: NextFunction) => next(),
82
+
83
+ // แต่ middleware ของคุณจะถูก wrap ทีละตัว
84
+ ...middlewares.map(
85
+ (mw) =>
86
+ ((req: Request, res: Response, next: NextFunction) =>
87
+ mw(
88
+ req as unknown as ReqByMethod<M, RQ>,
89
+ res as MyResponse<RS>,
90
+ next
91
+ )) as Express.RequestHandler
92
+ )
93
+ )
94
+ }
95
+ get<RQ extends InputParent, RS extends InputParent>(
96
+ url:
97
+ | `/api/${appName | "seed"}/${string}`
98
+ | `/api/${appName | "seed"}/${string}`[],
99
+ options: {
100
+ requireParams: RQ
101
+ responseStructure: RS
102
+ },
103
+ ...middlewares: Array<
104
+ (
105
+ req: ReqByMethod<"get", RQ>,
106
+ res: MyResponse<RS>,
107
+ next: NextFunction
108
+ ) => any
109
+ >
110
+ ) {
111
+ return this.rootMethod("get", url, options, ...middlewares)
112
+ }
113
+
114
+ post<RQ extends InputParent, RS extends InputParent>(
115
+ url:
116
+ | `/api/${appName | "seed"}/${string}`
117
+ | `/api/${appName | "seed"}/${string}`[],
118
+ options: {
119
+ requireParams: RQ
120
+ responseStructure: RS
121
+ },
122
+ ...middlewares: Array<
123
+ (
124
+ req: ReqByMethod<"post", RQ>,
125
+ res: MyResponse<RS>,
126
+ next: NextFunction
127
+ ) => any
128
+ >
129
+ ) {
130
+ return this.rootMethod("post", url, options, ...middlewares)
131
+ }
132
+ put<RQ extends InputParent, RS extends InputParent>(
133
+ url:
134
+ | `/api/${appName | "seed"}/${string}`
135
+ | `/api/${appName | "seed"}/${string}`[],
136
+ options: {
137
+ requireParams: RQ
138
+ responseStructure: RS
139
+ },
140
+ ...middlewares: Array<
141
+ (
142
+ req: ReqByMethod<"put", RQ>,
143
+ res: MyResponse<RS>,
144
+ next: NextFunction
145
+ ) => any
146
+ >
147
+ ) {
148
+ return this.rootMethod("put", url, options, ...middlewares)
149
+ }
150
+ delete<RQ extends InputParent, RS extends InputParent>(
151
+ url:
152
+ | `/api/${appName | "seed"}/${string}`
153
+ | `/api/${appName | "seed"}/${string}`[],
154
+ options: {
155
+ requireParams: RQ
156
+ responseStructure: RS
157
+ },
158
+ ...middlewares: Array<
159
+ (
160
+ req: ReqByMethod<"delete", RQ>,
161
+ res: MyResponse<RS>,
162
+ next: NextFunction
163
+ ) => any
164
+ >
165
+ ) {
166
+ return this.rootMethod("delete", url, options, ...middlewares)
167
+ }
168
+ public async generateAPIFiles() {
169
+ await this.generateStaticRouteFile()
170
+ console.log(`GGExpressV2.ts : ${this.appName} - generate staticRoute done.`)
171
+ }
172
+
173
+ private async generateStaticRouteFile() {
174
+ const staticRouteCode = generateStaticRouteFileV2(
175
+ this.appName,
176
+ myExpressRouteList
177
+ )
178
+
179
+ for (let row of this.outputPath) {
180
+ fs.writeFileSync(
181
+ path.join(row, `staticRouteInterface_${this.appName}_v2.ts`),
182
+ staticRouteCode
183
+ )
184
+ await generateGGApi_v2(
185
+ this.appName,
186
+ path.join(row, `apiConnector_${this.appName}_v2.ts`)
187
+ )
188
+ }
189
+ }
190
+ }
@@ -0,0 +1,79 @@
1
+ import fs from "fs"
2
+
3
+ export function generateGGApi_v2(
4
+ appName: string,
5
+ filePathWithFileName: string
6
+ ) {
7
+ const tempInterfaceName = `staticRouteInterface_${appName}_v2`
8
+ const code = `
9
+ import axios from "axios"
10
+ import { ${tempInterfaceName} } from "./${tempInterfaceName}"
11
+
12
+ export class GGApi_v2 {
13
+ constructor() {}
14
+
15
+ get<T extends keyof ${tempInterfaceName}["get"]>(
16
+ url: T,
17
+ requireParams: ${tempInterfaceName}["get"][T]["requireParams"]
18
+ ): Promise<${tempInterfaceName}["get"][T]["responseStructure"]> {
19
+ return new Promise((resolve, reject) => {
20
+ axios
21
+ .get(url as string, { params: { data: requireParams } })
22
+ .then((response) => {
23
+ resolve(response.data)
24
+ })
25
+ .catch((error) => {
26
+ reject(error)
27
+ })
28
+ })
29
+ }
30
+
31
+ post<T extends keyof ${tempInterfaceName}["post"]>(
32
+ url: T,
33
+ requireParams: ${tempInterfaceName}["post"][T]["requireParams"]
34
+ ): Promise<${tempInterfaceName}["post"][T]["responseStructure"]> {
35
+ return new Promise((resolve, reject) => {
36
+ axios
37
+ .post(url as string, { data: requireParams })
38
+ .then((response) => {
39
+ resolve(response.data)
40
+ })
41
+ .catch((error) => {
42
+ reject(error)
43
+ })
44
+ })
45
+ }
46
+ put<T extends keyof ${tempInterfaceName}["put"]>(
47
+ url: T,
48
+ requireParams: ${tempInterfaceName}["put"][T]["requireParams"]
49
+ ): Promise<${tempInterfaceName}["put"][T]["responseStructure"]> {
50
+ return new Promise((resolve, reject) => {
51
+ axios
52
+ .put(url as string, { data: requireParams })
53
+ .then((response) => {
54
+ resolve(response.data)
55
+ })
56
+ .catch((error) => {
57
+ reject(error)
58
+ })
59
+ })
60
+ }
61
+ delete<T extends keyof ${tempInterfaceName}["delete"]>(
62
+ url: T,
63
+ requireParams: ${tempInterfaceName}["delete"][T]["requireParams"]
64
+ ): Promise<${tempInterfaceName}["delete"][T]["responseStructure"]> {
65
+ return new Promise((resolve, reject) => {
66
+ axios
67
+ .delete(url as string, { data: { data: requireParams } })
68
+ .then((response) => {
69
+ resolve(response.data)
70
+ })
71
+ .catch((error) => {
72
+ reject(error)
73
+ })
74
+ })
75
+ }
76
+ }
77
+ `
78
+ fs.writeFileSync(filePathWithFileName, code)
79
+ }
@@ -0,0 +1,127 @@
1
+ import { Method } from "./GGExpressV2"
2
+ import { CustomType, InputNest, InputParent, toArray } from "./typeResolver"
3
+ import fs from "fs"
4
+ type routeList = {
5
+ method: Method
6
+ url: string
7
+ requireParams: InputParent
8
+ responseStructure: InputParent
9
+ }
10
+
11
+ type PrepareRouteStucture = {
12
+ [K in Method]: {
13
+ [key in string]: {
14
+ requireParams: string
15
+ responseStructure: string
16
+ }
17
+ }
18
+ }
19
+
20
+ type MyResponse = {
21
+ [url in string]: {}
22
+ }
23
+ export function prepareExportInterfaceData(data: routeList[]) {
24
+ let prepareRoute: PrepareRouteStucture = {
25
+ get: {},
26
+ post: {},
27
+ put: {},
28
+ delete: {},
29
+ }
30
+ prepareRoute = extract(data, prepareRoute)
31
+ return prepareRoute
32
+ }
33
+ function extract(
34
+ allData: routeList[],
35
+ prepareRoute: PrepareRouteStucture
36
+ ): PrepareRouteStucture {
37
+ for (const route of allData) {
38
+ prepareRoute[route.method][route.url] = {
39
+ requireParams: parentInputToCode(route.requireParams),
40
+ responseStructure: parentInputToCode(route.responseStructure),
41
+ }
42
+ }
43
+
44
+ return prepareRoute
45
+ }
46
+
47
+ function parentInputToCode(data: InputParent) {
48
+ const recursiveExtract = (target: InputParent, parentKeyName: string) => {
49
+ let result = ""
50
+ // check nest pimitive -------------------
51
+ if (typeof target === "string") {
52
+ const isUndefinedAble = (target as string).includes("?")
53
+ ? "undefined"
54
+ : ""
55
+ const isNullAble = (target as string).includes("~") ? "null" : ""
56
+ const isArray = (target as string).includes("[]") ? "[]" : ""
57
+ const varType = (target as string).replace(/\[\]|\?|\~/g, "")
58
+ let result = [`${varType}`, isUndefinedAble, isNullAble]
59
+ .filter((row) => row !== "")
60
+ .join(" | ")
61
+ if (isArray) result = `(${result})[]`
62
+ else result
63
+ return `${parentKeyName} : ${result}`
64
+ }
65
+
66
+ // check object ---------------------------
67
+ else if (typeof target === "object" && Array.isArray(target) === false) {
68
+ const keyList = Object.keys(target)
69
+ const tempResult = []
70
+ for (const keyName of keyList) {
71
+ //@ts-ignore
72
+ const recusiveResult = recursiveExtract(target[keyName], keyName)
73
+ tempResult.push(recusiveResult)
74
+ }
75
+ result = `${result} \n ${parentKeyName} : { ${tempResult.join(",")} }`
76
+ }
77
+
78
+ // check array ---------------------------
79
+ else if (Array.isArray(target)) {
80
+ // enum
81
+ if (
82
+ typeof target[0] === "number" ||
83
+ typeof target[0] === "string" ||
84
+ target[0] === null ||
85
+ target[0] === undefined
86
+ )
87
+ return `${parentKeyName} : [${(target as string[])
88
+ .map((word) => ` "${word}" `)
89
+ .join(",")}] `
90
+ // nest or input nest
91
+ else if (typeof target[0] === "object")
92
+ for (const row of target as InputNest[] | InputParent[]) {
93
+ result = result + " " + recursiveExtract(row, parentKeyName) + "[] "
94
+ }
95
+ }
96
+ return `${result}`
97
+ }
98
+ const result = recursiveExtract(data, "data")
99
+ return `${result} `
100
+ }
101
+ export function generateStaticRouteFileV2(appName: string, data: routeList[]) {
102
+ const interfaceName = `staticRouteInterface_${appName}_v2`
103
+ const generateEachUrl = (data: PrepareRouteStucture[Method]) => {
104
+ const keyName = Object.keys(data)
105
+ return keyName
106
+ .map(
107
+ (keyName) => `
108
+ "${keyName}" : {
109
+ requireParams : { ${data[keyName].requireParams} },
110
+ responseStructure : { ${data[keyName].responseStructure},
111
+ status: "SUCCESS" | "ERROR",
112
+ message: string }
113
+ }`
114
+ )
115
+ .join(", ")
116
+ }
117
+ const preparedData = prepareExportInterfaceData(data)
118
+ let code = `
119
+ export interface ${interfaceName} {
120
+ get : { ${generateEachUrl(preparedData.get)} },
121
+ post : { ${generateEachUrl(preparedData.post)} },
122
+ put : { ${generateEachUrl(preparedData.put)} },
123
+ delete : { ${generateEachUrl(preparedData.delete)} },
124
+ }
125
+ `
126
+ return code
127
+ }
@@ -0,0 +1,69 @@
1
+
2
+ import axios from "axios"
3
+ import { staticRouteInterface_hotel_v2 } from "./staticRouteInterface_hotel_v2"
4
+
5
+ export class GGApi_v2 {
6
+ constructor() {}
7
+
8
+ get<T extends keyof staticRouteInterface_hotel_v2["get"]>(
9
+ url: T,
10
+ requireParams: staticRouteInterface_hotel_v2["get"][T]["requireParams"]
11
+ ): Promise<staticRouteInterface_hotel_v2["get"][T]["responseStructure"]> {
12
+ return new Promise((resolve, reject) => {
13
+ axios
14
+ .get(url as string, { params: { data: requireParams } })
15
+ .then((response) => {
16
+ resolve(response.data)
17
+ })
18
+ .catch((error) => {
19
+ reject(error)
20
+ })
21
+ })
22
+ }
23
+
24
+ post<T extends keyof staticRouteInterface_hotel_v2["post"]>(
25
+ url: T,
26
+ requireParams: staticRouteInterface_hotel_v2["post"][T]["requireParams"]
27
+ ): Promise<staticRouteInterface_hotel_v2["post"][T]["responseStructure"]> {
28
+ return new Promise((resolve, reject) => {
29
+ axios
30
+ .post(url as string, { data: requireParams })
31
+ .then((response) => {
32
+ resolve(response.data)
33
+ })
34
+ .catch((error) => {
35
+ reject(error)
36
+ })
37
+ })
38
+ }
39
+ put<T extends keyof staticRouteInterface_hotel_v2["put"]>(
40
+ url: T,
41
+ requireParams: staticRouteInterface_hotel_v2["put"][T]["requireParams"]
42
+ ): Promise<staticRouteInterface_hotel_v2["put"][T]["responseStructure"]> {
43
+ return new Promise((resolve, reject) => {
44
+ axios
45
+ .put(url as string, { data: requireParams })
46
+ .then((response) => {
47
+ resolve(response.data)
48
+ })
49
+ .catch((error) => {
50
+ reject(error)
51
+ })
52
+ })
53
+ }
54
+ delete<T extends keyof staticRouteInterface_hotel_v2["delete"]>(
55
+ url: T,
56
+ requireParams: staticRouteInterface_hotel_v2["delete"][T]["requireParams"]
57
+ ): Promise<staticRouteInterface_hotel_v2["delete"][T]["responseStructure"]> {
58
+ return new Promise((resolve, reject) => {
59
+ axios
60
+ .delete(url as string, { data: { data: requireParams } })
61
+ .then((response) => {
62
+ resolve(response.data)
63
+ })
64
+ .catch((error) => {
65
+ reject(error)
66
+ })
67
+ })
68
+ }
69
+ }
@@ -0,0 +1,26 @@
1
+
2
+ export interface staticRouteInterface_hotel_v2 {
3
+ get : {
4
+ "/api/hotel/users/id" : {
5
+ requireParams : {
6
+ data : { id : number } },
7
+ responseStructure : {
8
+ data : {
9
+ userData : { id : number,name : string }[] } ,
10
+ status: "SUCCESS" | "ERROR",
11
+ message: string }
12
+ } },
13
+ post : {
14
+ "/api/hotel/booking/id" : {
15
+ requireParams : {
16
+ data : { id : number } },
17
+ responseStructure : {
18
+ data : {
19
+ bookingData : { id : number,roomNumber : string } } ,
20
+ status: "SUCCESS" | "ERROR",
21
+ message: string }
22
+ } },
23
+ put : { },
24
+ delete : { },
25
+ }
26
+
@@ -0,0 +1,74 @@
1
+ import express, { Express } from "express"
2
+ import GGExpressV2 from "./GGExpressV2"
3
+ import { toArray } from "./typeResolver"
4
+ import { GGApi_v2 } from "./output/apiConnector_hotel_v2"
5
+ // import { GGApi_v2 } from "./output/apiConnector_hotel_v2"
6
+ function run() {
7
+ const app = express()
8
+ const ggapp = new GGExpressV2<"hotel">(app, "hotel", ["./output"])
9
+ ggapp.get(
10
+ "/api/hotel/users/id",
11
+ {
12
+ requireParams: {
13
+ id: "number",
14
+ },
15
+ responseStructure: {
16
+ userData: toArray({
17
+ id: "number",
18
+ name: "string",
19
+ }),
20
+ },
21
+ },
22
+ (req, res, next) => {
23
+ console.log(req.query)
24
+ return res.json({
25
+ message: "",
26
+ status: "SUCCESS",
27
+ data: { userData: [] },
28
+ })
29
+ }
30
+ )
31
+ ggapp.post(
32
+ "/api/hotel/booking/id",
33
+ {
34
+ requireParams: {
35
+ id: "number",
36
+ },
37
+ responseStructure: {
38
+ bookingData: {
39
+ id: "number",
40
+ roomNumber: "string",
41
+ },
42
+ },
43
+ },
44
+ (req, res, next) => {
45
+ console.log(req.query)
46
+ return res.json({
47
+ message: "",
48
+ status: "SUCCESS",
49
+ data: {
50
+ bookingData: {
51
+ id: 2,
52
+ roomNumber: "dd",
53
+ },
54
+ },
55
+ })
56
+ }
57
+ )
58
+
59
+ app.listen(3002, async () => {
60
+ await ggapp.generateAPIFiles()
61
+ console.log("done")
62
+ process.exit(0)
63
+ })
64
+ }
65
+ run()
66
+
67
+ const x = new GGApi_v2()
68
+ x.post("/api/hotel/booking/id", {
69
+ data: { id: 1 },
70
+ }).then((response) => {
71
+ response.status === "ERROR"
72
+ response.data.bookingData
73
+ response.data.bookingData.roomNumber
74
+ })