Skip to content

Commit

Permalink
updated query parameters for /search
Browse files Browse the repository at this point in the history
  • Loading branch information
theorm committed Nov 13, 2024
1 parent 6e724c3 commit db58b3b
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 58 deletions.
2 changes: 1 addition & 1 deletion src/hooks/params.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ const _validate = (params, rules) => {

Object.keys(rules).forEach(key => {
if (typeof params[key] === 'undefined') {
if (rules[key] && rules[key].required) {
if (rules[key] && rules[key].required && rules[key].defaultValue == null) {
// required!
_errors[key] = {
code: 'NotFound',
Expand Down
28 changes: 26 additions & 2 deletions src/hooks/transformation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const transformResponse = <S, I, O>(
condition?: (context: HookContext<ImpressoApplication>) => boolean
): HookFunction<ImpressoApplication, S> => {
return context => {
if (context.type != 'after') throw new Error('The redactResponseDataItem hook should be used as an after hook only')
if (context.type != 'after') throw new Error('The transformResponse hook should be used as an after hook only')
if (condition != null && !condition(context)) return context

if (context.result != null) {
Expand Down Expand Up @@ -40,7 +40,7 @@ export const renameTopLevelField = <S>(
condition?: (context: HookContext<ImpressoApplication>) => boolean
): HookFunction<ImpressoApplication, S> => {
return context => {
if (context.type != 'after') throw new Error('The redactResponseDataItem hook should be used as an after hook only')
if (context.type != 'after') throw new Error('The renameTopLevelField hook should be used as an after hook only')
if (condition != null && !condition(context)) return context

const [from, to] = policy
Expand All @@ -53,3 +53,27 @@ export const renameTopLevelField = <S>(
return context
}
}

export const renameQueryParameters = <S>(
policy: Record<string, string>,
condition?: (context: HookContext<ImpressoApplication>) => boolean
): HookFunction<ImpressoApplication, S> => {
return context => {
if (context.type != 'before') throw new Error('The renameQueryParameters hook should be used as an after hook only')
if (condition != null && !condition(context)) return context

const params = context.params as any

const query: Record<string, any> = params?.query ?? {}

for (const [from, to] of Object.entries(policy)) {
if (query[from] != null) {
query[to] = query[from]
delete query[from]
}
}
params.query = query

return context
}
}
21 changes: 11 additions & 10 deletions src/services/articles/articles.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,18 @@ const findParameters: MethodParameter[] = [
*/
export const docs: ServiceSwaggerOptions = {
description: 'Content items',
securities: ['find', 'get'],
securities: ['get'],
operations: {
find: {
operationId: 'findContentItem',
description: 'Find content items that match the given query',
parameters: findParameters,
responses: getStandardResponses({
method: 'find',
schema: 'ContentItem',
}),
},
// Duplicate of /search
// find: {
// operationId: 'findContentItem',
// description: 'Find content items that match the given query',
// parameters: findParameters,
// responses: getStandardResponses({
// method: 'find',
// schema: 'ContentItem',
// }),
// },
get: {
operationId: 'getContentItem',
description: 'Get a content item by its UID',
Expand Down
1 change: 1 addition & 0 deletions src/services/articles/articles.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export default function (app: ImpressoApplication) {
// Initialize our service with any options it requires
app.use('/content-items', svc, {
events: [],
methods: ['get'],
docs: createSwaggerServiceOptions({ schemas: {}, docs }),
} as ServiceOptions)

Expand Down
7 changes: 6 additions & 1 deletion src/services/search/search.hooks.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { authenticateAround as authenticate } from '../../hooks/authenticate'
import { rateLimit } from '../../hooks/rateLimiter'
import { redactResponseDataItem, defaultCondition, inPublicApi } from '../../hooks/redaction'
import { transformResponseDataItem } from '../../hooks/transformation'
import { transformResponseDataItem, renameQueryParameters } from '../../hooks/transformation'
import { transformContentItem } from '../../transformers/contentItem'
import { loadYamlFile } from '../../util/yaml'

Expand All @@ -22,6 +22,10 @@ const { SolrNamespaces } = require('../../solr')

const contentItemRedactionPolicy = loadYamlFile(`${__dirname}/../articles/resources/contentItemRedactionPolicy.yml`)

const findQueryParamsRenamePolicy = {
term: 'q',
}

module.exports = {
around: {
find: [authenticate({ allowUnauthenticated: true }), rateLimit()],
Expand All @@ -30,6 +34,7 @@ module.exports = {
before: {
all: [],
find: [
renameQueryParameters(findQueryParamsRenamePolicy, inPublicApi),
validate({
...paramsValidator,
facets: utils.facets({
Expand Down
96 changes: 54 additions & 42 deletions src/services/search/search.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,68 +4,80 @@ import type { MethodParameter } from '../../util/openapi'
import { filtersQueryParameter, getSchemaRef, getStandardParameters, getStandardResponses } from '../../util/openapi'
import { paramsValidator } from './search.validators'

const findParameters: MethodParameter[] = [
{
in: 'query',
name: 'q',
required: paramsValidator.q.required,
schema: {
type: 'string',
minLength: 2,
maxLength: 1000,
},
description: 'Search query term',
const parameterTerm: MethodParameter = {
in: 'query',
name: 'term',
required: paramsValidator.q.required,
schema: {
type: 'string',
minLength: 2,
maxLength: 1000,
},
{
in: 'query',
name: 'group_by',
required: paramsValidator.group_by.required,
schema: {
type: 'string',
enum: paramsValidator.group_by.choices,
default: paramsValidator.group_by.choices[0],
},
description: 'Group by term',
description: 'Search query term.',
}
const parameterGroupBy: MethodParameter = {
in: 'query',
name: 'group_by',
required: paramsValidator.group_by.required,
schema: {
type: 'string',
enum: paramsValidator.group_by.choices,
default: paramsValidator.group_by.choices[0],
},
{
in: 'query',
name: 'order_by',
required: false,
schema: {
type: 'string',
enum: paramsValidator.order_by.choices,
},
description: 'Order by term',
description: 'Group by term',
}
const parameterOrderBy: MethodParameter = {
in: 'query',
name: 'order_by',
required: false,
schema: {
type: 'string',
enum: paramsValidator.order_by.choices,
},
{
in: 'query',
name: 'facets',
required: false,
schema: {
type: 'string',
enum: Object.keys(SolrMappings.search.facets),
},
description: 'Facet to return',
description: 'Order by term',
}
const parameterFacets: MethodParameter = {
in: 'query',
name: 'facets',
required: false,
schema: {
type: 'string',
enum: Object.keys(SolrMappings.search.facets),
},
description: 'Facet to return',
}

const findParameters: MethodParameter[] = [
parameterTerm,
parameterGroupBy,
parameterOrderBy,
parameterFacets,
filtersQueryParameter,
...getStandardParameters({ method: 'find' }),
]

const findParametersPublicApi: MethodParameter[] = [
parameterTerm,
parameterOrderBy,
filtersQueryParameter,
...getStandardParameters({ method: 'find' }),
]

/**
* NOTE: Keep this in sync with validators in search.hooks.ts
*/
export const docs: ServiceSwaggerOptions = {
export const getDocs = (isPublicApi: boolean): ServiceSwaggerOptions => ({
description: 'Search content items',
securities: ['find'],
operations: {
find: {
operationId: 'search',
description: 'Find content items that match the given query',
parameters: findParameters,
parameters: isPublicApi ? findParametersPublicApi : findParameters,
responses: getStandardResponses({
method: 'find',
schema: 'ContentItem',
}),
},
},
}
})
4 changes: 2 additions & 2 deletions src/services/search/search.service.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createSwaggerServiceOptions } from 'feathers-swagger'
import { docs } from './search.schema'
import { getDocs } from './search.schema'

// Initializes the `search` service on path `/search`
const createService = require('./search.class.js')
Expand All @@ -18,7 +18,7 @@ module.exports = function (app) {
app.use('/search', createService(options), {
methods: isPublicApi ? ['find'] : undefined,
events: [],
docs: createSwaggerServiceOptions({ schemas: {}, docs }),
docs: createSwaggerServiceOptions({ schemas: {}, docs: getDocs(isPublicApi) }),
})

app.service('search').hooks(hooks)
Expand Down
1 change: 1 addition & 0 deletions src/services/search/search.validators.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ const paramsValidator = {
group_by: {
required: true,
choices: ['articles', 'raw'],
defaultValue: 'articles',
transform: d => utils.translate(d, SolrMappings.search.groupBy),
},
order_by: {
Expand Down

0 comments on commit db58b3b

Please sign in to comment.