Skip to content

Commit c421630

Browse files
feat: header serialization
1 parent 3421c4d commit c421630

Some content is hidden

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

51 files changed

+687
-604
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66
"lint:fix": "tsc --noemit && eslint \"./src\" --ext .ts,.tsx --fix"
77
},
88
"dependencies": {
9-
"@types/node": "^18.11.5",
10-
"@types/qs": "^6.9.7",
119
"axios": "^1.1.3",
1210
"form-data": "^4.0.0",
1311
"reflect-metadata": "^0.1.13"
1412
},
1513
"devDependencies": {
1614
"@typescript-eslint/eslint-plugin": "^5.40.0",
1715
"@typescript-eslint/parser": "^5.40.0",
16+
"@types/node": "^18.11.5",
17+
"@types/qs": "^6.9.7",
1818
"eslint": "^8.25.0",
1919
"eslint-config-prettier": "^8.5.0",
2020
"eslint-plugin-prettier": "^4.2.1",

src/internal/utils/headers.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import { ParamDecorator } from "./pathparams";
2+
import { ParseParamDecorator } from "./utils";
3+
4+
export const headerMetadataKey = "headerParam";
5+
6+
export function GetHeadersFromRequest(headerParams: any): any {
7+
if (headerParams == null) return;
8+
const headers: any = {};
9+
const fieldNames: string[] = Object.getOwnPropertyNames(headerParams);
10+
fieldNames.forEach((fname) => {
11+
const headerAnn: string = Reflect.getMetadata(
12+
headerMetadataKey,
13+
headerParams,
14+
fname
15+
);
16+
if (headerAnn == null) return;
17+
const headerDecorator: ParamDecorator = ParseParamDecorator(
18+
headerAnn,
19+
fname,
20+
"simple",
21+
false
22+
);
23+
if (headerDecorator == null) return;
24+
const value: string = SerializeHeader(
25+
headerParams[fname],
26+
headerDecorator.Explode
27+
);
28+
if (value != "") headers[headerDecorator.ParamName] = value;
29+
});
30+
return headers;
31+
}
32+
33+
function SerializeHeader(header: any, explode: boolean): string {
34+
const headerVals: string[] = [];
35+
if (Array.isArray(header)) {
36+
header.forEach((val: any) => {
37+
headerVals.push(String(val));
38+
});
39+
} else if (header instanceof Map) {
40+
header.forEach((headerVal, headerKey) => {
41+
if (explode) headerVals.push(`${headerKey}=${headerVal}`);
42+
else headerVals.push(`${headerKey},${headerVal}`);
43+
});
44+
} else if (header instanceof Object) {
45+
Object.getOwnPropertyNames(header).forEach((headerKey: string) => {
46+
if (explode) headerVals.push(`${headerKey}=${header[headerKey]}`);
47+
else headerVals.push(`${headerKey},${header[headerKey]}`);
48+
});
49+
} else {
50+
return String(header);
51+
}
52+
return headerVals.join(",");
53+
}

src/internal/utils/pathparams.ts

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,5 @@
11
export const ppMetadataKey = "pathParam";
22

3-
export function ParsePathParamDecorator(
4-
ppAnn: string,
5-
fName: string
6-
): ParamDecorator {
7-
// style=simple;explode=false;name=apiID
8-
let ppDecorator: ParamDecorator = new ParamDecorator(
9-
"simple",
10-
false,
11-
fName.toLowerCase()
12-
);
13-
ppAnn.split(";").forEach((ppAnnPart) => {
14-
const [ppKey, ppVal]: string[] = ppAnnPart.split("=");
15-
switch (ppKey) {
16-
case "style":
17-
ppDecorator.Style = ppVal;
18-
break;
19-
case "explode":
20-
ppDecorator.Explode = ppVal == "true";
21-
break;
22-
case "name":
23-
ppDecorator.ParamName = ppVal;
24-
}
25-
});
26-
return ppDecorator;
27-
}
28-
293
export function GetSimplePathParams(
304
paramName: string,
315
paramValue: any,
@@ -38,10 +12,18 @@ export function GetSimplePathParams(
3812
ppVals.push(String(param));
3913
});
4014
pathParams.set(paramName, ppVals.join(","));
15+
} else if (paramValue instanceof Map) {
16+
paramValue.forEach((paramVal, paramName) => {
17+
if (explode) ppVals.push(`${paramName}=${paramVal}`);
18+
else ppVals.push(`${paramName},${paramVal}`);
19+
});
20+
pathParams.set(paramName, ppVals.join(","));
4121
} else if (paramValue instanceof Object) {
4222
Object.getOwnPropertyNames(paramValue).forEach((paramName: string) => {
43-
if (explode) ppVals.push(`${paramName}=${paramValue[paramName]}`);
44-
else ppVals.push(`${paramName},${paramValue[paramName]}`);
23+
const paramFieldValue = paramValue[paramName];
24+
if (isEmpty(paramFieldValue)) return;
25+
else if (explode) ppVals.push(`${paramName}=${paramFieldValue}`);
26+
else ppVals.push(`${paramName},${paramFieldValue}`);
4527
});
4628
pathParams.set(paramName, ppVals.join(","));
4729
} else {
@@ -50,20 +32,28 @@ export function GetSimplePathParams(
5032
return pathParams;
5133
}
5234

35+
function isEmpty(value: any): boolean {
36+
// check for undefined, null, and NaN
37+
let res: boolean = false;
38+
if (typeof value === 'number') res = Number.isNaN(value);
39+
else if (typeof value === 'string') res = value === "";
40+
return res || value == null;
41+
}
42+
5343
export class ParamDecorator {
5444
Style: string;
5545
Explode: boolean;
5646
ParamName: string;
5747
Serialization?: string;
5848
constructor(
59-
Style: string,
60-
Explode: boolean,
61-
ParamName: string,
62-
Serialization?: string
49+
Style: string,
50+
Explode: boolean,
51+
ParamName: string,
52+
Serialization?: string
6353
) {
6454
this.Style = Style;
6555
this.Explode = Explode;
6656
this.ParamName = ParamName;
6757
this.Serialization = Serialization;
6858
}
69-
}
59+
}

src/internal/utils/queryparams.ts

Lines changed: 16 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
import { ParamsSerializerOptions } from "axios";
22
import qs from "qs";
33
import { ParamDecorator } from "./pathparams";
4+
import { ParseParamDecorator } from "./utils";
45

56
export const qpMetadataKey = "queryParam";
67

78
export function GetQueryParamSerializer(
8-
queryParams: any
9+
queryParams: any
910
): ParamsSerializerOptions {
1011
if (queryParams == null) return { encode: FormSerializerExplode };
1112
const fieldNames: string[] = Object.getOwnPropertyNames(queryParams);
1213
fieldNames.forEach((fname) => {
1314
const qpAnn: string = Reflect.getMetadata(
14-
qpMetadataKey,
15-
queryParams,
16-
fname
15+
qpMetadataKey,
16+
queryParams,
17+
fname
1718
);
1819
if (qpAnn == null) return { encode: (params: unknown) => "" };
19-
const qpDecorator: ParamDecorator = ParseQueryParamDecorator(qpAnn, fname);
20+
const qpDecorator: ParamDecorator = ParseParamDecorator(
21+
qpAnn,
22+
fname,
23+
"form",
24+
true
25+
);
2026
if (qpDecorator == null) return;
2127
if (qpDecorator.Serialization === "json")
2228
return {
@@ -60,8 +66,8 @@ function FormSerializer(params: unknown): string {
6066
query.push(`${key}=${values}`);
6167
} else {
6268
const values: string = Object.entries(Object.assign({}, value))
63-
.map(([objKey, objValue]) => `${objKey},${objValue}`)
64-
.join(",");
69+
.map(([objKey, objValue]) => `${objKey},${objValue}`)
70+
.join(",");
6571
query.push(`${key}=${values}`);
6672
}
6773
});
@@ -76,39 +82,10 @@ function FormSerializerExplode(params: unknown): string {
7682
query.push(value.map((aValue) => `${key}=${aValue}`).join("&"));
7783
} else
7884
query.push(
79-
Object.entries(Object.assign({}, value))
80-
.map(([objKey, objValue]) => `${objKey}=${objValue}`)
81-
.join("&")
85+
Object.entries(Object.assign({}, value))
86+
.map(([objKey, objValue]) => `${objKey}=${objValue}`)
87+
.join("&")
8288
);
8389
});
8490
return query.join("&");
8591
}
86-
87-
function ParseQueryParamDecorator(
88-
qpAnn: string,
89-
fName: string
90-
): ParamDecorator {
91-
// style=simple;explode=false;name=apiID
92-
const qpDecorator: ParamDecorator = new ParamDecorator(
93-
"form",
94-
true,
95-
fName.toLowerCase()
96-
);
97-
qpAnn.split(";").forEach((qpAnnPart) => {
98-
const [qpKey, qpVal]: string[] = qpAnnPart.split("=");
99-
switch (qpKey) {
100-
case "style":
101-
qpDecorator.Style = qpVal;
102-
break;
103-
case "explode":
104-
qpDecorator.Explode = qpVal == "true";
105-
break;
106-
case "name":
107-
qpDecorator.ParamName = qpVal;
108-
break;
109-
case "serialization":
110-
qpDecorator.Serialization = qpVal;
111-
}
112-
});
113-
return qpDecorator;
114-
}

src/internal/utils/utils.ts

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import "reflect-metadata";
33
import {
44
GetSimplePathParams,
55
ParamDecorator,
6-
ParsePathParamDecorator,
76
ppMetadataKey,
87
} from "./pathparams";
98

@@ -73,22 +72,22 @@ export class SpeakeasyBase {
7372
if (isSpeakeasyBase(prop.type)) {
7473
(this as any)[prop.key] = new prop.type(value);
7574
} else if (
76-
prop.type.name == "Array" &&
77-
isSpeakeasyBase(prop.elemType)
75+
prop.type.name == "Array" &&
76+
isSpeakeasyBase(prop.elemType)
7877
) {
7978
(this as any)[prop.key] = handleArray(
80-
value,
81-
prop.elemType,
82-
prop.elemDepth
79+
value,
80+
prop.elemType,
81+
prop.elemDepth
8382
);
8483
} else if (
85-
prop.type.name == "Object" &&
86-
isSpeakeasyBase(prop.elemType)
84+
prop.type.name == "Object" &&
85+
isSpeakeasyBase(prop.elemType)
8786
) {
8887
(this as any)[prop.key] = handleObject(
89-
value,
90-
prop.elemType,
91-
prop.elemDepth
88+
value,
89+
prop.elemType,
90+
prop.elemDepth
9291
);
9392
} else {
9493
(this as any)[prop.key] = value;
@@ -100,8 +99,8 @@ export class SpeakeasyBase {
10099
}
101100

102101
export function Metadata<
103-
T extends SpeakeasyBase = Record<string | symbol, unknown>
104-
>(params?: {
102+
T extends SpeakeasyBase = Record<string | symbol, unknown>
103+
>(params?: {
105104
data?: string;
106105
elemType?: { new (): T };
107106
elemDepth?: number;
@@ -137,8 +136,8 @@ export function Metadata<
137136
}
138137

139138
export function ReplaceParameters(
140-
stringWithParams: string,
141-
params: Map<string, string>
139+
stringWithParams: string,
140+
params: Map<string, string>
142141
): string {
143142
let res: string = stringWithParams;
144143
params.forEach((value, key) => {
@@ -149,24 +148,29 @@ export function ReplaceParameters(
149148
}
150149

151150
export function GenerateURL(
152-
serverURL: string,
153-
path: string,
154-
pathParams: any
151+
serverURL: string,
152+
path: string,
153+
pathParams: any
155154
): string {
156155
const url: string = serverURL.replace(/\/$/, "") + path;
157156
const parsedParameters: Map<string, string> = new Map<string, string>();
158157
const fieldNames: string[] = Object.getOwnPropertyNames(pathParams);
159158
fieldNames.forEach((fname) => {
160159
const ppAnn: string = Reflect.getMetadata(ppMetadataKey, pathParams, fname);
161160
if (ppAnn == null) return;
162-
const ppDecorator: ParamDecorator = ParsePathParamDecorator(ppAnn, fname);
161+
const ppDecorator: ParamDecorator = ParseParamDecorator(
162+
ppAnn,
163+
fname,
164+
"simple",
165+
false
166+
);
163167
if (ppDecorator == null) return;
164168
switch (ppDecorator.Style) {
165169
case "simple":
166170
const simpleParams: Map<string, string> = GetSimplePathParams(
167-
ppDecorator.ParamName,
168-
pathParams[fname],
169-
ppDecorator.Explode
171+
ppDecorator.ParamName,
172+
pathParams[fname],
173+
ppDecorator.Explode
170174
);
171175
simpleParams.forEach((value, key) => {
172176
parsedParameters.set(key, value);
@@ -175,3 +179,35 @@ export function GenerateURL(
175179
});
176180
return ReplaceParameters(url, parsedParameters);
177181
}
182+
183+
export function ParseParamDecorator(
184+
ann: string,
185+
fName: string,
186+
defaultStyle: string,
187+
defaultExplode: boolean
188+
): ParamDecorator {
189+
// style=simple;explode=false;name=apiID
190+
const decorator: ParamDecorator = new ParamDecorator(
191+
defaultStyle,
192+
defaultExplode,
193+
fName.toLowerCase()
194+
);
195+
196+
ann.split(";").forEach((annPart) => {
197+
const [paramKey, paramVal]: string[] = annPart.split("=");
198+
switch (paramKey) {
199+
case "style":
200+
decorator.Style = paramVal;
201+
break;
202+
case "explode":
203+
decorator.Explode = paramVal == "true";
204+
break;
205+
case "name":
206+
decorator.ParamName = paramVal;
207+
break;
208+
case "serialization":
209+
decorator.Serialization = paramVal;
210+
}
211+
});
212+
return decorator;
213+
}

0 commit comments

Comments
 (0)