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

feat: add ignore http method support #752

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
19 changes: 18 additions & 1 deletion docs/reference/sql-openapi/ignore.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,24 @@ To ignore individual fields:
app.register(require('@platformatic/sql-openapi'), {
ignore: {
categories: {
name: true
fields: {
name: true
}
}
}
})
```

To ignore individual routes:

```javascript
app.register(require('@platformatic/sql-openapi'), {
ignore: {
categories: {
routes: {
GET: ['/categories/'],
POST: ['/categories/:id']
}
}
}
})
Expand Down
22 changes: 21 additions & 1 deletion packages/db/lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,27 @@ const core = {
type: 'object',
// TODO add support for column-level ignore
additionalProperties: {
type: 'boolean'
oneOf: [{
type: 'boolean'
}, {
type: 'object',
properties: {
routes: {
type: 'object',
additionalProperties: {
type: 'array',
items: { type: 'string' }
}
},
fields: {
type: 'object',
additionalProperties: {
type: 'boolean'
}
},
additionalProperties: false
}
}]
}
}
},
Expand Down
5 changes: 3 additions & 2 deletions packages/sql-openapi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ async function setupOpenAPI (app, opts) {
})

for (const entity of Object.values(app.platformatic.entities)) {
const entitySchema = mapSQLEntityToJSONSchema(entity, ignore[entity.pluralName])
const ignoredFields = ignore[entity.pluralName]?.fields
const entitySchema = mapSQLEntityToJSONSchema(entity, ignoredFields)
// TODO remove reverseRelationships from the entity
/* istanbul ignore next */
entity.reverseRelationships = entity.reverseRelationships || []
Expand Down Expand Up @@ -78,7 +79,7 @@ async function setupOpenAPI (app, opts) {
app.register(manyToMany, {
entity,
prefix: localPrefix,
ignore
ignore: ignore[entity.pluralName] || {}
})
}
}
Expand Down
150 changes: 82 additions & 68 deletions packages/sql-openapi/lib/entity-to-routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,61 +41,65 @@ const getEntityLinksForEntity = (app, entity) => {

async function entityPlugin (app, opts) {
const entity = opts.entity
const ignore = opts.ignore
const ignoredFields = opts.ignore.fields || []
const ignoredRoutes = opts.ignore.routes || {}

const entitySchema = {
$ref: entity.name + '#'
}
const primaryKey = entity.primaryKeys.values().next().value
const primaryKeyParams = getPrimaryKeyParams(entity, ignore)
const primaryKeyParams = getPrimaryKeyParams(entity, ignoredFields)
const primaryKeyCamelcase = camelcase(primaryKey)
const entityLinks = getEntityLinksForEntity(app, entity)

const { whereArgs, orderByArgs } = generateArgs(entity, ignore)
const { whereArgs, orderByArgs } = generateArgs(entity, ignoredFields)

app.addHook('preValidation', async (req) => {
if (typeof req.query.fields === 'string') {
req.query.fields = req.query.fields.split(',')
}
})

const fields = getFieldsForEntity(entity, ignore)
const fields = getFieldsForEntity(entity, ignoredFields)

rootEntityRoutes(app, entity, whereArgs, orderByArgs, entityLinks, entitySchema, fields)
rootEntityRoutes(app, entity, whereArgs, orderByArgs, entityLinks, entitySchema, fields, ignoredRoutes)

app.get(`/:${primaryKeyCamelcase}`, {
schema: {
operationId: `get${entity.name}By${capitalize(primaryKeyCamelcase)}`,
params: primaryKeyParams,
querystring: {
type: 'object',
properties: {
fields
const entityByPrimaryKeyUrl = `/:${primaryKeyCamelcase}`
if (!ignoredRoutes.GET?.includes(app.prefix + entityByPrimaryKeyUrl)) {
app.get(entityByPrimaryKeyUrl, {
schema: {
operationId: `get${entity.name}By${capitalize(primaryKeyCamelcase)}`,
params: primaryKeyParams,
querystring: {
type: 'object',
properties: {
fields
}
},
response: {
200: entitySchema
}
},
response: {
200: entitySchema
links: {
200: entityLinks
}
},
links: {
200: entityLinks
}
}, async function (request, reply) {
const ctx = { app: this, reply }
const res = await entity.find({
ctx,
where: {
[primaryKeyCamelcase]: {
eq: request.params[primaryKeyCamelcase]
}
},
fields: request.query.fields
}, async function (request, reply) {
const ctx = { app: this, reply }
const res = await entity.find({
ctx,
where: {
[primaryKeyCamelcase]: {
eq: request.params[primaryKeyCamelcase]
}
},
fields: request.query.fields
})
if (res.length === 0) {
return reply.callNotFound()
}
return res[0]
})
if (res.length === 0) {
return reply.callNotFound()
}
return res[0]
})
}

const mapRoutePathNamesReverseRelations = new Map()
let idxRoutePathNamesReverseRelations = 1
Expand All @@ -122,15 +126,18 @@ async function entityPlugin (app, opts) {
mapRoutePathNamesReverseRelations.set(routePathName, true)
}

const entityUrl = `/:${camelcase(primaryKey)}/${routePathName}`
if (ignoredRoutes.GET?.includes(app.prefix + entityUrl)) continue

try {
app.get(`/:${camelcase(primaryKey)}/${routePathName}`, {
schema: {
operationId,
params: getPrimaryKeyParams(entity, ignore),
params: getPrimaryKeyParams(entity, ignoredFields),
querystring: {
type: 'object',
properties: {
fields: getFieldsForEntity(targetEntity, ignore)
fields: getFieldsForEntity(targetEntity, ignoredFields)
}
},
response: {
Expand Down Expand Up @@ -204,6 +211,9 @@ async function entityPlugin (app, opts) {
mapRoutePathNamesRelations.set(targetRelation, true)
}

const entityUrl = `/:${camelcase(primaryKey)}/${targetRelation}`
if (ignoredRoutes.GET?.includes(app.prefix + entityUrl)) continue

const targetEntitySchema = {
$ref: targetEntity.name + '#'
}
Expand All @@ -212,14 +222,14 @@ async function entityPlugin (app, opts) {
const operationId = `get${capitalize(targetEntity.singularName)}For${capitalize(entity.singularName)}`
// We need to get the relation name from the PK column:
try {
app.get(`/:${camelcase(primaryKey)}/${targetRelation}`, {
app.get(entityUrl, {
schema: {
operationId,
params: getPrimaryKeyParams(entity, ignore),
params: getPrimaryKeyParams(entity, ignoredFields),
querystring: {
type: 'object',
properties: {
fields: getFieldsForEntity(targetEntity, ignore)
fields: getFieldsForEntity(targetEntity, ignoredFields)
}
},
response: {
Expand Down Expand Up @@ -269,8 +279,10 @@ async function entityPlugin (app, opts) {
}

for (const method of ['POST', 'PUT']) {
if (ignoredRoutes[method]?.includes(app.prefix + entityByPrimaryKeyUrl)) continue

app.route({
url: `/:${primaryKeyCamelcase}`,
url: entityByPrimaryKeyUrl,
method,
schema: {
body: entitySchema,
Expand Down Expand Up @@ -310,43 +322,45 @@ async function entityPlugin (app, opts) {
})
}

app.delete(`/:${primaryKeyCamelcase}`, {
schema: {
params: primaryKeyParams,
querystring: {
type: 'object',
properties: {
fields
if (!ignoredRoutes.DELETE?.includes(app.prefix + entityByPrimaryKeyUrl)) {
app.delete(entityByPrimaryKeyUrl, {
schema: {
params: primaryKeyParams,
querystring: {
type: 'object',
properties: {
fields
}
},
response: {
200: entitySchema
}
},
response: {
200: entitySchema
}
}
}, async function (request, reply) {
const ctx = { app: this, reply }
const res = await entity.delete({
ctx,
where: {
[primaryKeyCamelcase]: {
eq: request.params[primaryKeyCamelcase]
}
},
fields: request.query.fields
}, async function (request, reply) {
const ctx = { app: this, reply }
const res = await entity.delete({
ctx,
where: {
[primaryKeyCamelcase]: {
eq: request.params[primaryKeyCamelcase]
}
},
fields: request.query.fields
})
if (res.length === 0) {
return reply.callNotFound()
}
return res[0]
})
if (res.length === 0) {
return reply.callNotFound()
}
return res[0]
})
}
}

function getPrimaryKeyParams (entity, ignore) {
function getPrimaryKeyParams (entity, ignoredFields) {
const primaryKey = entity.primaryKeys.values().next().value
const fields = entity.fields
const field = fields[primaryKey]
const properties = {
[field.camelcase]: { type: mapSQLTypeToOpenAPIType(field.sqlType, ignore) }
[field.camelcase]: { type: mapSQLTypeToOpenAPIType(field.sqlType, ignoredFields) }
}
const required = [field.camelcase]

Expand Down
Loading