Skip to content

Commit

Permalink
refactor and simplify renderURLSearchParams & flattenObject
Browse files Browse the repository at this point in the history
  • Loading branch information
atreya2011 committed Apr 25, 2021
1 parent 86a3893 commit ba9ba25
Showing 1 changed file with 70 additions and 64 deletions.
134 changes: 70 additions & 64 deletions generator/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,78 +196,86 @@ function getNotifyEntityArrivalSink<T>(notifyCallback: NotifyStreamEntityArrival
})
}
type RequestPayload = Record<string, unknown>;
type Primitive = string | boolean | number;
/**
* Checks if given value is of a primitive type
* @param {unknown} value
* @return {boolean}
*/
function isPrimitive(value: unknown): boolean {
return (
typeof value === "string" ||
typeof value === "number" ||
typeof value === "boolean"
);
}
/**
* Flattens a deeply nested object
*
* @param {Object} deeplyNestedObject
* Flattens a deeply nested request payload and returns an object
* with only primitive values and non-empty array of primitive values
* @param {RequestPayload} requestPayload
* @param {String} path
* @return {Record<string, unknown>}
* @return {Record<string, Primitive | Array<Primitive>}
*/
function flattenObject<T extends Record<string, unknown>>(
deeplyNestedObject: T,
function flattenRequestPayload<T extends RequestPayload>(
requestPayload: T,
path: string = ""
): T {
return Object.keys(deeplyNestedObject).reduce(
): Record<string, Primitive | Array<Primitive>> {
return Object.keys(requestPayload).reduce(
(acc: T, key: string): T => {
const value = deeplyNestedObject[key];
const newPath = Array.isArray(deeplyNestedObject)
? path + "." + key
: [path, key].filter(Boolean).join(".");
const isObject = [
typeof value === "object",
value !== null,
!(value instanceof Date),
!(value instanceof RegExp),
!(Array.isArray(value) && value.length === 0)
].every(Boolean);
return isObject
? { ...acc, ...flattenObject(value as Record<string, unknown>, newPath) }
: { ...acc, [newPath]: value };
const value = requestPayload[key];
const newPath = path ? [path, key].join(".") : key;
const isObject =
Object.prototype.toString.call(value) === "[object Object]";
const isNonEmptyPrimitiveArray =
Array.isArray(value) &&
value.every(v => isPrimitive(v)) &&
value.length > 0;
switch (true) {
case isObject:
acc = {
...acc,
...flattenRequestPayload(value as RequestPayload, newPath)
};
break;
case isPrimitive(value) || isNonEmptyPrimitiveArray:
acc = { ...acc, [newPath]: value };
break;
}
return acc;
},
{} as T
);
) as Record<string, Primitive | Array<Primitive>>;
}
/**
* Renders a deeply nested object into a string of URL search parameters
* by first flattening the object and then removing non-primitive array keys,
* non-primitive values and keys which are already present in the URL path.
* @param {Object} deeplyNestedObject
* @param {Array<string>} urlPathParams
* @return {String}
* Renders a deeply nested request payload into a string of URL search
* parameters by first flattening the request payload and then removing keys
* which are already present in the URL path.
* @param {RequestPayload} requestPayload
* @param {string[]} urlPathParams
* @return {string}
*/
export function renderURLSearchParams<T extends Record<string, unknown>>(
deeplyNestedObject: T,
export function renderURLSearchParams<T extends RequestPayload>(
requestPayload: T,
urlPathParams: string[] = []
): string {
const flattenedObject = flattenObject(deeplyNestedObject);
const flattenedRequestPayload = flattenRequestPayload(requestPayload);
const urlSearchParams = Object.keys(flattenedObject).reduce(
const urlSearchParams = Object.keys(flattenedRequestPayload).reduce(
(acc: string[][], key: string): string[][] => {
const parts = key.split(".");
const index = parts.findIndex(f => Number(f) || f === "0");
// if key does not contain only one numeric index as the last element
// then it is an array of objects
if (parts.length === index + 1 || index === -1) {
// remove array index to render url search params properly
// according to http.proto
// https://github.com/googleapis/googleapis/blob/master/google/api/http.proto
const keyWithoutArrayIndex =
index > -1 ? parts.slice(0, index).join(".") : key;
// values should only be primitive types and key should not be
// present in the url path as a parameter
const value = flattenedObject[key];
if (
(typeof value === "string" ||
typeof value === "boolean" ||
typeof value === "number") &&
!urlPathParams.find(f => f === key)
) {
acc = [...acc, [keyWithoutArrayIndex, value.toString()]];
// key should not be present in the url path as a parameter
const value = flattenedRequestPayload[key];
if (!urlPathParams.find(f => f === key)) {
if (Array.isArray(value)) {
acc = [...acc, ...value.map(m => [key, m.toString()])];
} else {
acc = [...acc, [key, value.toString()]];
}
}
Expand All @@ -276,9 +284,7 @@ export function renderURLSearchParams<T extends Record<string, unknown>>(
[] as string[][]
);
return urlSearchParams.length > 0
? "?" + new URLSearchParams(urlSearchParams).toString()
: "";
return "?" + new URLSearchParams(urlSearchParams).toString();
}
`

Expand Down Expand Up @@ -317,21 +323,21 @@ func renderURL(r *registry.Registry) func(method data.Method) string {
url := method.URL
reg := regexp.MustCompile("{([^}]+)}")
matches := reg.FindAllStringSubmatch(url, -1)
fieldList := "["
fieldsInPath := "["
if len(matches) > 0 {
log.Debugf("url matches %v", matches)
for _, m := range matches {
expToReplace := m[0]
fieldName := fieldNameFn(m[1])
part := fmt.Sprintf(`${req["%s"]}`, fieldName)
url = strings.ReplaceAll(url, expToReplace, part)
fieldList += fmt.Sprintf(`"%s", `, fieldName)
fieldsInPath += fmt.Sprintf(`"%s", `, fieldName)
}
}
fieldList = strings.TrimRight(fieldList, ", ") + "]"
fieldsInPath = strings.TrimRight(fieldsInPath, ", ") + "]"

if !method.ClientStreaming && method.HTTPMethod == "GET" {
url += fmt.Sprintf("${fm.renderURLSearchParams(req, %s)}", fieldList)
url += fmt.Sprintf("${fm.renderURLSearchParams(req, %s)}", fieldsInPath)
}

return url
Expand Down

0 comments on commit ba9ba25

Please sign in to comment.