-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add Go code samples * ci: Generate code * ci: Format code * Fix go request and response string value samples * ci: Generate code * Make go responses to return json, correctly determine go package name * Imporve readability and structure * ci: Generate code * ci: Format code * Improve var name * Format * Add go package import * ci: Generate code * Improve readability * ci: Generate code * Fix sdk imports * ci: Generate code * Imporve go package name determination and nested imports logic * ci: Format code * Improve var names * More renames * Improve array property value handling * Improve array item check * Improve object property value handling * Improve code readability and structure * Improve naming * Add comment * Reorg function order * Update src/lib/code-sample/go.ts Co-authored-by: Evan Sosenko <[email protected]> * Fix var names casing * ci: Format code --------- Co-authored-by: Seam Bot <[email protected]> Co-authored-by: Evan Sosenko <[email protected]>
- Loading branch information
1 parent
090aa96
commit 1c27ac7
Showing
4 changed files
with
489 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
import { pascalCase } from 'change-case' | ||
|
||
import type { Json, NonNullJson } from 'lib/json.js' | ||
|
||
import { createJsonResponse } from './json.js' | ||
import type { CodeSampleDefinition, Context } from './schema.js' | ||
|
||
const defaultGoPackageName = 'api' | ||
const goPackageBasePath = 'github.com/seamapi/go' | ||
|
||
export const createGoRequest = ( | ||
{ request }: CodeSampleDefinition, | ||
_context: Context, | ||
): string => { | ||
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 = formatGoArgs(request.parameters) | ||
const goSdkRequestArgs = `context.Background()${isReqWithParams ? `, ${goPackageName}.${requestStructName}(${formattedArgs})` : ''}` | ||
|
||
const pathParts = request.path.split('/') | ||
|
||
return `${goSdkImports} | ||
client${pathParts.map((p) => pascalCase(p)).join('.')}(${goSdkRequestArgs}) | ||
`.trim() | ||
} | ||
|
||
const getGoPackageName = (path: string): string => { | ||
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: string): boolean => | ||
path.split('/').slice(1).length > 2 | ||
|
||
const generateImports = ({ | ||
goPackageName, | ||
isReqWithParams, | ||
}: { | ||
goPackageName: string | ||
isReqWithParams: boolean | ||
}): string => { | ||
const imports: string[] = [] | ||
|
||
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: string): string => { | ||
const requestStructNameSuffix = 'Request' | ||
|
||
return isPathNested(path) | ||
? `${pascalCase(removeUntilSecondSlash(path))}${requestStructNameSuffix}` | ||
: `${pascalCase(path)}${requestStructNameSuffix}` | ||
} | ||
|
||
const removeUntilSecondSlash = (str: string): string => | ||
str.replace(/^\/[^/]*/, '') | ||
|
||
const formatGoArgs = (jsonParams: NonNullJson): string => | ||
Object.entries(jsonParams as Record<string, Json>) | ||
.map(([paramKey, paramValue]) => { | ||
const formattedValue = formatGoValue({ value: paramValue, key: paramKey }) | ||
return `${pascalCase(paramKey)}: ${formattedValue}` | ||
}) | ||
.join(', ') | ||
|
||
const formatGoValue = ({ | ||
value, | ||
key, | ||
}: { | ||
value: Json | ||
key: string | ||
}): string => { | ||
if (value == null) return 'nil' | ||
if (typeof value === 'string') return `api.String("${value}")` | ||
if (typeof value === 'boolean') return `api.Bool(${value})` | ||
if (typeof value === 'number') return `api.Float64(${value})` | ||
|
||
if (Array.isArray(value)) { | ||
return formatGoArray(value, key) | ||
} | ||
|
||
if (typeof value === 'object') { | ||
return formatGoObject(value, key) | ||
} | ||
|
||
throw new Error(`Unsupported type: ${typeof value}`) | ||
} | ||
|
||
const formatGoArray = (value: Json[], key: string): string => { | ||
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) => formatGoValue({ value: v, key })) | ||
const item = value[0] | ||
if (item == null) { | ||
throw new Error(`Null value in response array for '${key}'`) | ||
} | ||
|
||
const arrayType = isPrimitiveValue(item) | ||
? getPrimitiveTypeName(item) | ||
: `api.${pascalCase(key)}` | ||
|
||
return `[${value.length}]${arrayType}{${formattedItems.join(', ')}}` | ||
} | ||
|
||
const isPrimitiveValue = (value: Json): boolean => | ||
value != null && typeof value !== 'object' | ||
|
||
const getPrimitiveTypeName = (value: Json): string => { | ||
switch (typeof value) { | ||
case 'string': | ||
return 'string' | ||
case 'number': | ||
return 'float64' | ||
case 'boolean': | ||
return 'bool' | ||
default: | ||
throw new Error(`Unsupported type: ${typeof value}`) | ||
} | ||
} | ||
|
||
const formatGoObject = (value: Record<string, Json>, key: string): string => { | ||
if (Object.keys(value).length === 0) { | ||
return 'struct{}{}' | ||
} | ||
|
||
const formattedEntries = Object.entries(value) | ||
.map( | ||
([objKey, val]) => | ||
`${pascalCase(objKey)}: ${formatGoValue({ value: val, key: objKey })}`, | ||
) | ||
.join(', ') | ||
|
||
return `api.${pascalCase(key)}{${formattedEntries}}` | ||
} | ||
|
||
export const createGoResponse = createJsonResponse |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.