tyranid-openapi
Advanced tools
Comparing version 0.2.1 to 0.2.2
@@ -10,2 +10,8 @@ /** | ||
}; | ||
export declare const WHERE: { | ||
name: string; | ||
in: string; | ||
type: string; | ||
description: string; | ||
}; | ||
export declare const MIN_DATE: { | ||
@@ -12,0 +18,0 @@ name: string; |
@@ -18,2 +18,14 @@ "use strict"; | ||
}; | ||
exports.WHERE = { | ||
name: '$where', | ||
in: 'query', | ||
type: 'string', | ||
description: ` | ||
Match a property against a value. This will work with enumerations. | ||
For example \`?$where=:layerType:tasks\` | ||
searches the \`layerType\` property for the value \`tasks\`. Nested properties | ||
can be specified using dot syntax (for example: \`?$search=info.nested.name:ben\`) | ||
`.trim() | ||
}; | ||
exports.MIN_DATE = { | ||
@@ -74,2 +86,3 @@ name: '$minDate', | ||
exports.SEARCH_STRING, | ||
exports.WHERE, | ||
exports.MIN_DATE, | ||
@@ -76,0 +89,0 @@ exports.MAX_DATE, |
{ | ||
"name": "tyranid-openapi", | ||
"version": "0.2.1", | ||
"version": "0.2.2", | ||
"description": "Open API spec and express app generator for tyranid", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
211
README.md
@@ -1,4 +0,213 @@ | ||
## Open API / Swagger spec and express app generator for Tyranid | ||
# OpenAPI / Swagger spec generator for [Tyranid](http://tyranid.org/) | ||
[![npm version](https://badge.fury.io/js/tyranid-openapi.svg)](https://badge.fury.io/js/tyranid-openapi) | ||
[![Build Status](https://travis-ci.org/CrossLead/tyranid-openapi.svg?branch=master)](https://travis-ci.org/CrossLead/tyranid-openapi) | ||
[![codecov](https://codecov.io/gh/CrossLead/tyranid-openapi/branch/master/graph/badge.svg)](https://codecov.io/gh/CrossLead/tyranid-openapi) | ||
This project provides a way to generate a complete + valid [openAPI spec](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md) by adding schema annotations to your [tyranid](http://tyranid.org/) models. | ||
**NOTE**: this library only creates the spec itself, implementation is left to the app code for now (but might be a good feature for this library later on). | ||
## Contents | ||
- [Schema Options](#schema-options) | ||
- [Generating the spec](#generating-the-spec) | ||
- [Exposing a single collection](#exposing-a-single-collection) | ||
- ["Parent" collections](#parent-collections) | ||
- [Renaming and Restricting certain properties](#renaming-and-restricting-certain-properties) | ||
- [Partitioning a collection into multiple "virtual" collections](#partitioning-a-collection-into-multiple-virtual-collections) | ||
## Schema Options | ||
There are various options for configuring how the tyranid models appear to the public api. You can see the `CollectionSchemaOptions` (specified in as an object on the schema definition named `openAPI`) and `FieldSchemaOptions` (specified on specific fields in the schema as a property named `openAPI`) interfaces in [`./src/interfaces.ts`](./src/interfaces.ts). | ||
## Generating the Spec | ||
Once you have specified some collections to be exposed by annotating the schemas, you can generate the actual spec file as follows... | ||
```typescript | ||
import { Tyr } from 'tyranid'; | ||
import { spec } from 'tyranid-openapi'; | ||
import { writeFileSync } from 'fs'; | ||
import { join } from 'path'; | ||
// async function for bootstrap | ||
(async () => { | ||
// bootstrapping without db to just get schema graph | ||
await Tyr.config({ | ||
validate: [{ glob: join(__dirname, './models/*.js') }] | ||
}); | ||
// generate yaml openAPI spec string | ||
const specString = spec(Tyr, { yaml: true }); | ||
// write out to file | ||
writeFileSync(join(__dirname, './my-api.yaml'), specString); | ||
})().catch(console.error); | ||
``` | ||
## Exposing a single collection | ||
The most basic way to expose a collection to the public api is by setting the `openAPI` flag to `true`, and marking a few properties to show. For example... | ||
```typescript | ||
import { Tyr } from 'tyranid'; | ||
export default new Tyr.Collection({ | ||
id: 't01', | ||
name: 'metric', | ||
dbName: 'metrics', | ||
// this indicates that we want GET/PUT/POST/DELETE endpoints | ||
// for `/metrics` and `/metrics/{_id}`. Additionally, `read:metrics` and | ||
// `write:metrics` scopes will be created. | ||
openAPI: true, | ||
fields: { | ||
_id: { is: 'mongoid' }, | ||
organizationId: { is: 'mongoid' }, | ||
// here we mark that we want to expose this field to the api | ||
// also, since it is marked `required`, api users will recieve | ||
// an error if they do not POST/PUT data that includes it. | ||
name: { is: 'string', openAPI: true, required: true } | ||
}, | ||
}); | ||
``` | ||
## "Parent" collections | ||
Some times there might be several collections which you want to have fall under a single set of permission scopes. Additionally, you want the api url to be nested. | ||
For example, say we have a `metrics` collection containing metadata about individual metrics (like usage analytics or budget numbers) as well as a `metricObservations` collection which contains the actual time series. To keep `metricObservations` in the same permission scope as `metrics`, we would add the following annotation... | ||
```typescript | ||
export default new Tyr.Collection({ | ||
id: 'mtg', | ||
name: 'metricTarget', | ||
dbName: 'metricTargets', | ||
openAPI: { | ||
// define the parent collection, this means that | ||
// the api paths will be nested under metrics, so | ||
// `/metrics/{_id}/observations`, `/metrics/{_id}/observations/{_id}` | ||
parent: 'metric', | ||
// here we say that we want to use the permission scopes | ||
// (`read:metrics`/`write:metrics`) for this collection | ||
useParentScope: true | ||
}, | ||
fields: { | ||
_id: { is: 'mongoid' }, | ||
metricId: { link: 'metric', openAPI: true }, | ||
date: { is: 'date', openAPI: true }, | ||
value: { is: 'double', openAPI: true }, | ||
organizationId: { is: 'mongoid' }, | ||
excludeProperty: { is: 'string' }, | ||
type: { | ||
link: 'metricTargetType', openAPI: true | ||
} | ||
}, | ||
}); | ||
``` | ||
## Renaming and Restricting certain properties | ||
If there are certain properties that are automatically generated by the database (like timestamps), you can mark them to only be returned / accepted from read endpoints. Additionally, you can provide different public names for properties... | ||
```typescript | ||
import { Tyr } from 'tyranid'; | ||
export default new Tyr.Collection({ | ||
id: 't01', | ||
name: 'metric', | ||
dbName: 'metrics', | ||
openAPI: true, | ||
fields: { | ||
_id: { is: 'mongoid' }, | ||
organizationId: { is: 'mongoid' }, | ||
name: { is: 'string', openAPI: true, required: true }, | ||
privateNamedProperty: { | ||
is: 'string', | ||
openAPI: { | ||
// only allow this property in results of GET / PUT / POST, | ||
// do not allow it to be sent as part of an update (PUT/POST) | ||
include: 'read', | ||
// in the spec, this property will be called `publicName` | ||
name: 'publicName' | ||
} | ||
} | ||
}, | ||
}); | ||
``` | ||
## Partitioning a collection into multiple "virtual" collections | ||
If there is a single database collection which represents several distinct types of data which you wish to expose in different endpoints, you can "partition" the collection... | ||
```typescript | ||
import { Tyr } from 'tyranid'; | ||
export default new Tyr.Collection({ | ||
id: 'i01', | ||
name: 'item', | ||
dbName: 'items', | ||
openAPI: { | ||
// this will create two new types for the api: `Plan` + `Task` + `Project`, | ||
// with `/task`, `/project`, and `/plan` endpoints, etc... | ||
partition: [ | ||
{ | ||
name: 'plan', | ||
// here we define a mongodb query to separate the | ||
// data into their specific partitions | ||
partialFilterExpression: { | ||
kind: 'plan' | ||
} | ||
}, | ||
{ | ||
name: 'task', | ||
partialFilterExpression: { | ||
kind: 'task' | ||
} | ||
}, | ||
{ | ||
name: 'project', | ||
partialFilterExpression: { | ||
kind: 'project' | ||
} | ||
} | ||
] | ||
}, | ||
fields: { | ||
_id: { is: 'mongoid' }, | ||
organizationId: { is: 'mongoid' }, | ||
name: { is: 'string', openAPI: true, required: true }, | ||
kind: { is: 'string' }, | ||
planField: { | ||
is: 'string', | ||
openAPI: { | ||
// we might want some fields to only appear on | ||
// certain partitions (can also be an array of partitions) | ||
partition: 'plan' | ||
} | ||
}, | ||
taskField: { | ||
is: 'string', | ||
openAPI: { | ||
partition: 'task' | ||
} | ||
}, | ||
nestedPartitionField: { | ||
is: 'object', | ||
fields: { | ||
innerPlanOrProjectField: { | ||
is: 'string', | ||
openAPI: { | ||
partition: ['plan', 'project'], | ||
name: 'renamedPartitionField' | ||
} | ||
}, | ||
innerTaskField: { is: 'string', openAPI: { partition: 'task' } } | ||
} | ||
} | ||
} | ||
}); | ||
``` |
Sorry, the diff of this file is not supported yet
116188
1908
213