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

Add blueprint API #4

Merged
merged 6 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,30 @@ Build tools for the Seam API using this blueprint.

TODO

## Motivation

TODO

## Installation

Add this as a dependency to your project using [npm] with

```
$ npm install @seamapi/blueprint
$ npm install --save-dev @seamapi/blueprint
```

[npm]: https://www.npmjs.com/

## Usage

```ts
import { createBlueprint } from '@seamapi/blueprint'
import * as types from '@seamapi/types'

const blueprint = createBlueprint(types)
console.log(JSON.stringify(blueprint)
```

## Development and Testing

### Quickstart
Expand Down
24 changes: 24 additions & 0 deletions examples/blueprint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Builder, Command, Describe, Handler } from 'landlubber'

import { createBlueprint, type TypesModule } from '@seamapi/blueprint'

interface Options {
moduleName: string
}

export const command: Command = 'blueprint [moduleName]'

export const describe: Describe = 'Create a blueprint from a type module'

export const builder: Builder = {
moduleName: {
type: 'string',
default: '@seamapi/types/connect',
describe: 'Module name or path to import',
},
}

export const handler: Handler<Options> = async ({ moduleName, logger }) => {
const types = (await import(moduleName)) as TypesModule
logger.info({ data: createBlueprint(types) }, 'blueprint')
}
4 changes: 2 additions & 2 deletions examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import landlubber from 'landlubber'

import * as todo from './todo.js'
import * as blueprint from './blueprint.js'

const commands = [todo]
const commands = [blueprint]

await landlubber(commands).parse()
23 changes: 0 additions & 23 deletions examples/todo.ts

This file was deleted.

26 changes: 25 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"npm": ">= 9.0.0"
},
"devDependencies": {
"@seamapi/types": "1.184.0",
"@types/node": "^20.8.10",
"ava": "^6.0.1",
"c8": "^10.1.2",
Expand All @@ -83,6 +84,7 @@
"tsc-alias": "^1.8.2",
"tsup": "^8.0.1",
"tsx": "^4.6.2",
"typescript": "~5.3.3"
"typescript": "~5.3.3",
"zod": "^3.23.8"
}
}
15 changes: 15 additions & 0 deletions src/lib/blueprint.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import type { Openapi } from './openapi.js'

export interface Blueprint {
name: string
}

export interface TypesModule {
openapi: Openapi
}

export const createBlueprint = ({ openapi }: TypesModule): Blueprint => {
return {
name: openapi.info.title,
}
}
6 changes: 5 additions & 1 deletion src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export { todo } from './todo.js'
export {
type Blueprint,
createBlueprint,
type TypesModule,
} from './blueprint.js'
5 changes: 5 additions & 0 deletions src/lib/openapi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface Openapi {
info: {
title: string
}
}
7 changes: 0 additions & 7 deletions src/lib/todo.test.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/lib/todo.ts

This file was deleted.

10 changes: 10 additions & 0 deletions test/blueprint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import test from 'ava'

import { createBlueprint } from '@seamapi/blueprint'

import * as types from './fixtures/types/index.js'

test('createBlueprint', (t) => {
const blueprint = createBlueprint(types)
t.snapshot(blueprint, 'blueprint')
})
8 changes: 8 additions & 0 deletions test/fixtures/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import * as schemas from './schemas.js'

export { schemas }

export * from './model-types.js'
export { default as openapi } from './openapi.js'
export * from './route-schemas.js'
export * from './route-types.js'
5 changes: 5 additions & 0 deletions test/fixtures/types/model-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { z } from 'zod'

import type { foo } from './schemas.js'

export type Foo = z.infer<typeof foo>
79 changes: 79 additions & 0 deletions test/fixtures/types/openapi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
export default {
openapi: '3.0.0',
info: { title: 'Foo', version: '1.0.0' },
servers: [{ url: 'https://example.com' }],
tags: [{ description: 'foos', name: '/foos' }],
components: {
schemas: {
foo: {
type: 'object',
properties: {
foo_id: {
description: 'Foo id',
format: 'uuid',
type: 'string',
},
name: {
description: 'Foo name',
type: 'string',
},
},
required: ['foo_id', 'name'],
},
},
},
paths: {
'/foos/get': {
get: {
operationId: 'foosGetGet',
responses: {
200: {
content: {
'application/json': {
schema: {
properties: {
ok: { type: 'boolean' },
foo: { $ref: '#/components/schemas/foo' },
},
required: ['foo', 'ok'],
type: 'object',
},
},
},
description: 'OK',
},
400: { description: 'Bad Request' },
401: { description: 'Unauthorized' },
},
security: [],
summary: '/foos/get',
tags: ['/foos'],
},
post: {
operationId: 'foosGetPost',
responses: {
200: {
content: {
'application/json': {
schema: {
properties: {
ok: { type: 'boolean' },
foo: { $ref: '#/components/schemas/foo' },
},
required: ['foo', 'ok'],
type: 'object',
},
},
},
description: 'OK',
},
400: { description: 'Bad Request' },
401: { description: 'Unauthorized' },
},
security: [],
summary: '/foos/get',
tags: ['/foos'],
},
},
},
}
13 changes: 13 additions & 0 deletions test/fixtures/types/route-schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { z } from 'zod'

import * as schemas from './schemas.js'

export const routes = {
'/foos/get': {
auth: 'none',
methods: ['GET', 'POST'],
jsonResponse: z.object({
foo: schemas.foo,
}),
},
} as const
13 changes: 13 additions & 0 deletions test/fixtures/types/route-specs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { z } from 'zod'

import * as schemas from './schemas.js'

export const routes = {
'/foos/get': {
auth: 'none',
methods: ['GET', 'POST'],
jsonResponse: z.object({
foo: schemas.foo,
}),
},
} as const
25 changes: 25 additions & 0 deletions test/fixtures/types/route-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
export interface Routes {
'/foos/get': {
route: '/foos/get'
method: 'GET' | 'POST'
queryParams: Record<string, unknown>
jsonBody: Record<string, unknown>
commonParams: Record<string, unknown>
formData: Record<string, unknown>
jsonResponse: {
foo: {
foo_id: string
name: string
}
}
}
}

export type RouteResponse<Path extends keyof Routes> =
Routes[Path]['jsonResponse']

export type RouteRequestBody<Path extends keyof Routes> =
Routes[Path]['jsonBody'] & Routes[Path]['commonParams']

export type RouteRequestParams<Path extends keyof Routes> =
Routes[Path]['queryParams'] & Routes[Path]['commonParams']
6 changes: 6 additions & 0 deletions test/fixtures/types/schemas.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { z } from 'zod'

export const foo = z.object({
foo_id: z.string().uuid(),
name: z.string(),
})
9 changes: 9 additions & 0 deletions test/seamapi-blueprint.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { openapi } from '@seamapi/types/connect'
import test from 'ava'

import { createBlueprint } from '@seamapi/blueprint'

test('createBlueprint', (t) => {
const blueprint = createBlueprint({ openapi })
t.snapshot(blueprint, 'blueprint')
})
13 changes: 13 additions & 0 deletions test/snapshots/blueprint.test.ts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Snapshot report for `test/blueprint.test.ts`

The actual snapshot is saved in `blueprint.test.ts.snap`.

Generated by [AVA](https://avajs.dev).

## createBlueprint

> blueprint

{
name: 'Foo',
}
Binary file added test/snapshots/blueprint.test.ts.snap
Binary file not shown.
Loading