@seamapi/blueprint
Version:
Build tools for the Seam API using this blueprint.
214 lines (213 loc) • 9.07 kB
JavaScript
import { pascalCase } from 'change-case';
const defaultGoPackageName = 'api';
const goPackageBasePath = 'github.com/seamapi/go';
export const createGoRequest = ({ request }, _context) => {
const isReqWithParams = Object.keys(request.parameters).length !== 0;
const goPackageName = getGoPackageName(request.path);
const goSdkImports = generateImports({
goPackageName,
isReqWithParams,
});
const requestStructName = getRequestStructName(request.path);
const formattedArgs = formatGoRequestArgs(request.parameters, {
goPackageName,
requestStructName,
});
const goSdkRequestArgs = `context.Background()${isReqWithParams ? `,\n${goPackageName}.${requestStructName}{\n${formattedArgs},\n}` : ''}`;
const pathParts = request.path.split('/');
return `package main
${goSdkImports}
func main() {
client${pathParts.map((p) => pascalCase(p)).join('.')}(${isReqWithParams ? '\n' : ''}${goSdkRequestArgs},\n)
}
`.trim();
};
const getGoPackageName = (path) => {
if (!isPathNested(path)) {
return defaultGoPackageName;
}
const firstPathPart = path.split('/').slice(1)[1];
if (firstPathPart == null) {
throw new Error(`Invalid path: missing second part in "${path}"`);
}
return firstPathPart.replace(/_/g, '');
};
const isPathNested = (path) => path.split('/').slice(1).length > 2;
const generateImports = ({ goPackageName, isReqWithParams, }) => {
const imports = [];
if (isReqWithParams) {
const defaultPackageImport = `import ${defaultGoPackageName} "${goPackageBasePath}"`;
imports.push(defaultPackageImport);
}
if (goPackageName !== defaultGoPackageName && isReqWithParams) {
const nestedPackageImport = `import ${goPackageName} "${goPackageBasePath}/${goPackageName}"`;
imports.push(nestedPackageImport);
}
return imports.join('\n');
};
const getRequestStructName = (path) => {
const requestStructNameSuffix = 'Request';
return isPathNested(path)
? `${pascalCase(removeUntilSecondSlash(path))}${requestStructNameSuffix}`
: `${pascalCase(path)}${requestStructNameSuffix}`;
};
const removeUntilSecondSlash = (str) => str.replace(/^\/[^/]*/, '');
const formatGoRequestArgs = (jsonParams, context) => Object.entries(jsonParams)
.map(([paramKey, paramValue]) => {
const formattedValue = formatGoRequestArgValue(paramKey, paramValue, context);
return `${pascalCase(paramKey)}: ${formattedValue}`;
})
.join(',\n');
const formatGoRequestArgValue = (key, value, context) => {
if (value == null)
return 'nil';
if (typeof value === 'string')
return `${defaultGoPackageName}.String("${value}")`;
if (typeof value === 'boolean')
return `${defaultGoPackageName}.Bool(${value})`;
if (typeof value === 'number')
return `${defaultGoPackageName}.Float64(${value})`;
if (Array.isArray(value)) {
return formatGoRequestArrayValue(key, value, context);
}
if (typeof value === 'object') {
return formatGoRequestObjectValue(key, value, context);
}
throw new Error(`Unsupported type: ${typeof value}`);
};
const formatGoRequestArrayValue = (key, value, context) => {
if (value.length === 0) {
// in Go there's no way define an empty array without specifying type
// and code samples definitions don't include the type annotations
return 'nil';
}
const formattedItems = value.map((v) => formatGoRequestArgValue(key, v, context));
const item = value[0];
if (item == null) {
throw new Error(`Null value in response array for '${key}'`);
}
const { goPackageName, requestStructName } = context;
const arrayType = isPrimitiveValue(item)
? getPrimitiveTypeName(item)
: `${goPackageName}.${pascalCase(`${requestStructName} ${key}`)}Item`;
return `[${value.length}]${arrayType}{${formattedItems.join(', ')}}`;
};
const isPrimitiveValue = (value) => typeof value !== 'object' && value !== null;
const getPrimitiveTypeName = (value) => {
switch (typeof value) {
case 'string':
return 'string';
case 'number':
return 'float64';
case 'boolean':
return 'bool';
}
};
const formatGoRequestObjectValue = (key, value, context) => {
if (Object.keys(value).length === 0) {
return 'struct{}{}';
}
const formattedEntries = Object.entries(value)
.map(([objKey, val]) => `${pascalCase(objKey)}: ${formatGoRequestArgValue(objKey, val, context)}`)
.join(', ');
const { goPackageName, requestStructName } = context;
return `${goPackageName}.${pascalCase(`${requestStructName} ${key}`)}{${formattedEntries}}`;
};
export const createGoResponse = ({ response, title }, context) => {
const { endpoint } = context;
if (endpoint.response.responseType === 'void')
return 'nil';
const { responseKey, resourceType } = endpoint.response;
const responseValue = response?.body?.[responseKey];
if (responseValue == null) {
throw new Error(`Missing ${responseKey} for '${title}'`);
}
const responseResourceGoStructName = pascalCase(resourceType);
return Array.isArray(responseValue)
? formatGoArrayResponse(responseValue, responseResourceGoStructName, title)
: formatGoResponse(responseValue, responseResourceGoStructName);
};
const formatGoArrayResponse = (responseArray, responseResourceGoStructName, title) => {
const formattedItems = responseArray
.map((v) => {
if (v == null) {
throw new Error(`Null value in response array for '${title}'`);
}
return formatGoResponse(v, responseResourceGoStructName);
})
.join(', ');
return `[]${defaultGoPackageName}.${responseResourceGoStructName}{${formattedItems}}`;
};
const formatGoResponse = (responseParams, responseResourceGoStructName) => {
const params = formatGoResponseParams(responseParams, responseResourceGoStructName);
return `${defaultGoPackageName}.${responseResourceGoStructName}{${params}}`;
};
const formatGoResponseParams = (jsonParams, responseResourceGoStructName) => Object.entries(jsonParams)
.map(([paramKey, paramValue]) => {
const formattedValue = formatGoResponseParamValue({
key: paramKey,
value: paramValue,
propertyChain: [],
}, responseResourceGoStructName);
return `${pascalCase(paramKey)}: ${formattedValue}`;
})
.join(', ');
const formatGoResponseParamValue = ({ key, value, propertyChain, }, responseResourceGoStructName) => {
if (value === null)
return 'nil';
if (typeof value === 'boolean')
return value.toString();
if (typeof value === 'number')
return value.toString();
if (typeof value === 'string')
return `"${value}"`;
if (Array.isArray(value)) {
return formatGoResponseArrayValue({ key, value, propertyChain }, responseResourceGoStructName);
}
if (typeof value === 'object') {
return formatGoResponseObjectValue({ key, value, propertyChain }, responseResourceGoStructName);
}
throw new Error(`Unsupported type: ${typeof value}`);
};
const formatGoResponseArrayValue = ({ key, value, propertyChain, }, responseResourceGoStructName) => {
if (value.length === 0) {
return 'nil';
}
const rawItem = value[0];
if (rawItem == null) {
throw new Error(`Null value in response array for '${key}'`);
}
const updatedPropertyChain = [...propertyChain, key];
const formattedItems = value.map((v) => formatGoResponseParamValue({ key, value: v, propertyChain: updatedPropertyChain }, responseResourceGoStructName));
if (isPrimitiveValue(rawItem)) {
const arrayType = getPrimitiveTypeName(rawItem);
return `[]${arrayType}{${formattedItems.join(', ')}}`;
}
else {
const structName = getStructName(updatedPropertyChain, responseResourceGoStructName);
return `[]${structName}{${formattedItems.join(', ')}}`;
}
};
const formatGoResponseObjectValue = ({ key, value, propertyChain, }, responseResourceGoStructName) => {
if (Object.keys(value).length === 0) {
return 'struct{}{}';
}
const updatedPropertyChain = [...propertyChain, key];
const structName = getStructName(updatedPropertyChain, responseResourceGoStructName);
const formattedEntries = Object.entries(value)
.map(([objKey, val]) => {
const formattedValue = formatGoResponseParamValue({
key: objKey,
value: val,
propertyChain: updatedPropertyChain,
}, responseResourceGoStructName);
return `${pascalCase(objKey)}: ${formattedValue}`;
})
.join(', ');
return `${defaultGoPackageName}.${structName}{${formattedEntries}}`;
};
const getStructName = (propertyChain, responseResourceGoStructName) => {
const fullPropertyChain = [responseResourceGoStructName, ...propertyChain];
return pascalCase(fullPropertyChain.join('_'));
};
//# sourceMappingURL=go.js.map