Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce C# code samples #107

Merged
merged 10 commits into from
Oct 4, 2024
108 changes: 108 additions & 0 deletions src/lib/code-sample/csharp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { camelCase, pascalCase } from 'change-case'

import type { Json, NonNullJson } from 'lib/json.js'

import { createJsonResponse } from './json.js'
import type { CodeSampleDefinition, Context } from './schema.js'

export const createCsharpRequest = (
{ request }: CodeSampleDefinition,
_context: Context,
): string => {
const parts = request.path.split('/').slice(1)
const requestArgs = formatCsharpArgs(request.parameters)

return `seam.${parts.map((p) => pascalCase(p)).join('.')}(${requestArgs})`
}

const formatCsharpArgs = (jsonParams: NonNullJson): string =>
Object.entries(jsonParams as Record<string, Json>)
.map(([key, value]) => {
const formattedValue = formatCsharpValue(key, value)
return `${camelCase(key)}: ${formattedValue}`
})
.join(', ')

const formatCsharpValue = (key: string, value: Json): string => {
if (value == null) return 'null'
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 formatCsharpArray(key, value)
}
if (typeof value === 'object') {
return formatCsharpObject(value)
}
throw new Error(`Unsupported type: ${typeof value}`)
}

const formatCsharpArray = (key: string, value: Json[]): string => {
if (value.length === 0) {
return 'new object[] { }'
}

const formattedItems = value.map((v) => formatCsharpValue(key, v))
const item = value[0]
if (item == null) {
throw new Error(`Null value in response array for '${key}'`)
}

const arrayType = isPrimitiveValue(item)
? getPrimitiveTypeName(item)
: 'object'

return `new ${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 'float'
case 'boolean':
return 'bool'
default:
throw new Error(`Unsupported type: ${typeof value}`)
}
}

const formatCsharpObject = (value: Record<string, Json>): string => {
if (Object.keys(value).length === 0) {
return 'new { }'
}

const formattedEntries = Object.entries(value)
.map(
([objKey, val]) =>
`${camelCase(objKey)} = ${formatCsharpValue(objKey, val)}`,
)
.join(', ')

return `new { ${formattedEntries} }`
}

export const createCsharpResponse = (
codeSampleDefinition: CodeSampleDefinition,
context: Context,
): string => {
const { response, title } = codeSampleDefinition
const { endpoint } = context

if (endpoint.response.responseType === 'void') return 'null'

const { responseKey, resourceType } = endpoint.response
const responseValue = response?.body?.[responseKey]

if (responseValue == null) {
throw new Error(`Missing ${responseKey} for '${title}'`)
}

return Array.isArray(responseValue)
? `System.Collections.Generic.List\`1[Seam.Model.${pascalCase(resourceType)}]`
: createJsonResponse(codeSampleDefinition, context)
}
20 changes: 19 additions & 1 deletion src/lib/code-sample/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from 'lib/code-sample/seam-cli.js'
import { JsonSchema } from 'lib/json.js'

import { createCsharpRequest, createCsharpResponse } from './csharp.js'
import { formatCodeRecords } from './format.js'
import { createGoRequest, createGoResponse } from './go.js'
import { createJavaRequest, createJavaResponse } from './java.js'
Expand Down Expand Up @@ -51,12 +52,22 @@ const CodeSampleSyntaxSchema = z.enum([
'bash',
'go',
'java',
'csharp',
])

export type CodeSampleSyntax = z.infer<typeof CodeSampleSyntaxSchema>

const CodeSchema = z.record(
z.enum(['javascript', 'python', 'php', 'ruby', 'seam_cli', 'go', 'java']),
z.enum([
'javascript',
'python',
'php',
'ruby',
'seam_cli',
'go',
'java',
'csharp',
]),
z.object({
title: z.string().min(1),
request: z.string(),
Expand Down Expand Up @@ -136,6 +147,13 @@ export const createCodeSample = async (
request_syntax: 'java',
response_syntax: 'json',
},
csharp: {
title: 'C#',
request: createCsharpRequest(codeSampleDefinition, context),
response: createCsharpResponse(codeSampleDefinition, context),
request_syntax: 'csharp',
response_syntax: 'json',
},
}

return {
Expand Down
Loading