diff --git a/.changeset/shaggy-timers-warn.md b/.changeset/shaggy-timers-warn.md new file mode 100644 index 000000000000..66fb86d712a4 --- /dev/null +++ b/.changeset/shaggy-timers-warn.md @@ -0,0 +1,6 @@ +--- +'@rocket.chat/rest-typings': major +'@rocket.chat/meteor': major +--- + +Changes settings public listing endpoint by moving query params from the 'query' attribute to standard query parameters. diff --git a/apps/meteor/app/api/server/v1/settings.ts b/apps/meteor/app/api/server/v1/settings.ts index 8adeb14e96ba..0e3509fb1956 100644 --- a/apps/meteor/app/api/server/v1/settings.ts +++ b/apps/meteor/app/api/server/v1/settings.ts @@ -7,7 +7,12 @@ import type { } from '@rocket.chat/core-typings'; import { isSettingAction, isSettingColor } from '@rocket.chat/core-typings'; import { LoginServiceConfiguration as LoginServiceConfigurationModel, Settings } from '@rocket.chat/models'; -import { isSettingsUpdatePropDefault, isSettingsUpdatePropsActions, isSettingsUpdatePropsColor } from '@rocket.chat/rest-typings'; +import { + isSettingsUpdatePropDefault, + isSettingsUpdatePropsActions, + isSettingsUpdatePropsColor, + isSettingsPublicWithPaginationProps, +} from '@rocket.chat/rest-typings'; import { Meteor } from 'meteor/meteor'; import type { FindOptions } from 'mongodb'; import _ from 'underscore'; @@ -44,14 +49,18 @@ async function fetchSettings( // settings endpoints API.v1.addRoute( 'settings.public', - { authRequired: false }, + { authRequired: false, validateParams: isSettingsPublicWithPaginationProps }, { async get() { const { offset, count } = await getPaginationItems(this.queryParams); const { sort, fields, query } = await this.parseJsonQuery(); + const { _id } = this.queryParams; + + const parsedQueryId = typeof _id === 'string' && _id ? { _id: { $in: _id.split(',').map((id) => id.trim()) } } : {}; const ourQuery = { ...query, + ...parsedQueryId, hidden: { $ne: true }, public: true, }; diff --git a/apps/meteor/tests/end-to-end/api/settings.ts b/apps/meteor/tests/end-to-end/api/settings.ts index c596954ad065..973d21e84f94 100644 --- a/apps/meteor/tests/end-to-end/api/settings.ts +++ b/apps/meteor/tests/end-to-end/api/settings.ts @@ -31,9 +31,56 @@ describe('[Settings]', () => { .expect('Content-Type', 'application/json') .expect(200) .expect((res) => { - expect(res.body).to.have.property('success', true); - expect(res.body).to.have.property('settings'); - expect(res.body).to.have.property('count'); + expect(res.body).to.have.property('success').and.to.be.true; + expect(res.body).to.have.property('settings').and.to.be.an('array').and.to.have.lengthOf(5); + expect(res.body).to.have.property('count').and.to.be.a('number').and.to.equal(5); + }) + .end(done); + }); + it('should return public settings even requested with _id param', (done) => { + void request + .get(api('settings.public')) + .query({ + _id: 'Site_Url', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success').and.to.be.true; + expect(res.body).to.have.property('settings').and.to.be.an('array').and.to.have.lengthOf(1); + expect(res.body).to.have.property('count').and.to.be.a('number').and.to.equal(1); + }) + .end(done); + }); + it('should return public settings even requested with _id param as an array', (done) => { + void request + .get(api('settings.public')) + .query({ + _id: 'Site_Url,LDAP_Enable', + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success').and.to.be.true; + expect(res.body).to.have.property('settings').and.to.be.an('array').and.to.have.lengthOf(2); + expect(res.body).to.have.property('count').and.to.be.a('number').and.to.equal(2); + }) + .end(done); + }); + it('should return an empty response when requesting public settings with a broken _id param', (done) => { + void request + .get(api('settings.public')) + .query({ + _id: 10, + }) + .expect('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body).to.have.property('success').and.to.be.true; + expect(res.body).to.have.property('settings').and.to.be.an('array').and.to.be.empty; + expect(res.body).to.have.property('count').and.to.be.a('number').and.to.equal(0); + expect(res.body).to.have.property('offset').and.to.be.a('number').and.to.equal(0); + expect(res.body).to.have.property('total').and.to.be.a('number').and.to.equal(0); }) .end(done); }); diff --git a/packages/rest-typings/src/v1/settings.ts b/packages/rest-typings/src/v1/settings.ts index 6da53c04ae68..c512a554762d 100644 --- a/packages/rest-typings/src/v1/settings.ts +++ b/packages/rest-typings/src/v1/settings.ts @@ -1,6 +1,8 @@ import type { ISetting, ISettingColor, LoginServiceConfiguration } from '@rocket.chat/core-typings'; +import type { PaginatedRequest } from '../helpers/PaginatedRequest'; import type { PaginatedResult } from '../helpers/PaginatedResult'; +import { ajv } from './Ajv'; type SettingsUpdateProps = SettingsUpdatePropDefault | SettingsUpdatePropsActions | SettingsUpdatePropsColor; @@ -25,9 +27,39 @@ type SettingsUpdatePropDefault = { export const isSettingsUpdatePropDefault = (props: Partial): props is SettingsUpdatePropDefault => 'value' in props; +type SettingsPublicWithPaginationProps = PaginatedRequest<{ _id?: string; query?: string }>; + +const SettingsPublicWithPaginationSchema = { + type: 'object', + properties: { + count: { + type: 'number', + nullable: true, + }, + offset: { + type: 'number', + nullable: true, + }, + sort: { + type: 'string', + nullable: true, + }, + _id: { + type: 'string', + }, + query: { + type: 'string', + }, + }, + required: [], + additionalProperties: false, +}; + +export const isSettingsPublicWithPaginationProps = ajv.compile(SettingsPublicWithPaginationSchema); + export type SettingsEndpoints = { '/v1/settings.public': { - GET: () => PaginatedResult & { + GET: (params: SettingsPublicWithPaginationProps) => PaginatedResult & { settings: Array; }; };