Skip to content

Commit

Permalink
feat(native-query): handle segment live queries (#1226)
Browse files Browse the repository at this point in the history
  • Loading branch information
DayTF authored Dec 16, 2024
1 parent 4f543ac commit 3f1bd1a
Show file tree
Hide file tree
Showing 24 changed files with 376 additions and 67 deletions.
24 changes: 23 additions & 1 deletion packages/agent/src/services/authorization/authorization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,36 @@ export default class AuthorizationService {
context: Context,
collectionName: string,
) {
const { id: userId } = context.state.user;
const { id: userId, renderingId } = context.state.user;

const canOnCollection = await this.forestAdminClient.permissionService.canOnCollection({
userId,
event,
collectionName,
});

if (
context.request?.body &&
CollectionActionEvent.Browse === event &&
(context.request.body as { segmentQuery?: string }).segmentQuery
) {
const { segmentQuery, connectionName } = context.request.body as {
segmentQuery?: string;
connectionName?: string;
};

const canExecuteSegmentQuery =
await this.forestAdminClient.permissionService.canExecuteSegmentQuery({
userId,
collectionName,
renderingId,
segmentQuery,
connectionName,
});

if (!canExecuteSegmentQuery) context.throw(HttpCode.Forbidden, 'Forbidden');
}

if (!canOnCollection) {
context.throw(HttpCode.Forbidden, 'Forbidden');
}
Expand Down
1 change: 1 addition & 0 deletions packages/agent/src/utils/context-filter-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default class ContextFilterFactory {
return new Filter({
search: QueryStringParser.parseSearch(collection, context),
segment: QueryStringParser.parseSegment(collection, context),
liveQuerySegment: QueryStringParser.parseLiveQuerySegment(context),
searchExtended: QueryStringParser.parseSearchExtended(context),
conditionTree: ConditionTreeFactory.intersect(
QueryStringParser.parseConditionTree(collection, context),
Expand Down
17 changes: 17 additions & 0 deletions packages/agent/src/utils/query-string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Sort,
SortFactory,
SortValidator,
UnprocessableError,
ValidationError,
} from '@forestadmin/datasource-toolkit';
import { Context } from 'koa';
Expand Down Expand Up @@ -117,6 +118,22 @@ export default class QueryStringParser {
return segment;
}

static parseLiveQuerySegment(context: Context) {
const { query } = context.request as any;
const segmentQuery = query.segmentQuery?.toString();
const connectionName = query.connectionName?.toString();

if (!segmentQuery) {
return null;
}

if (!connectionName) {
throw new UnprocessableError('Missing native query connection attribute');
}

return { query: segmentQuery, connectionName };
}

static parseCaller(context: Context): Caller {
const timezone = context.request.query.timezone?.toString();
const { ip } = context.request;
Expand Down
69 changes: 56 additions & 13 deletions packages/agent/test/routes/access/chart.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,13 @@ describe('ChartRoute', () => {
request: { ip: expect.any(String) },
timezone: 'Europe/Paris',
},
{ conditionTree: null, search: null, searchExtended: false, segment: null },
{
conditionTree: null,
search: null,
searchExtended: false,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ field: undefined, groups: undefined, operation: 'Count' },
);
expect(getChartWithContextInjectedMock).toHaveBeenCalledWith({
Expand Down Expand Up @@ -274,7 +280,8 @@ describe('ChartRoute', () => {
conditionTree: { field: 'name', operator: 'Present', value: null },
search: null,
searchExtended: false,
segment: null,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ field: undefined, groups: undefined, operation: 'Count' },
);
Expand Down Expand Up @@ -335,7 +342,8 @@ describe('ChartRoute', () => {
},
search: null,
searchExtended: false,
segment: null,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ field: undefined, groups: undefined, operation: 'Count' },
);
Expand Down Expand Up @@ -410,7 +418,13 @@ describe('ChartRoute', () => {
request: { ip: expect.any(String) },
timezone: 'Europe/Paris',
},
{ conditionTree: null, search: null, searchExtended: false, segment: null },
{
conditionTree: null,
search: null,
searchExtended: false,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ field: undefined, groups: undefined, operation: 'Count' },
);
expect(getChartWithContextInjectedMock).toHaveBeenCalledWith({
Expand Down Expand Up @@ -460,7 +474,8 @@ describe('ChartRoute', () => {
conditionTree: { field: 'title', operator: 'NotContains', value: '[test]' },
search: null,
searchExtended: false,
segment: null,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ field: undefined, groups: undefined, operation: 'Count' },
);
Expand Down Expand Up @@ -505,7 +520,13 @@ describe('ChartRoute', () => {
request: { ip: expect.any(String) },
timezone: 'Europe/Paris',
},
{ conditionTree: null, search: null, searchExtended: false, segment: null },
{
conditionTree: null,
search: null,
searchExtended: false,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ field: undefined, groups: [{ field: 'author:firstName' }], operation: 'Count' },
);
expect(getChartWithContextInjectedMock).toHaveBeenCalledWith({
Expand Down Expand Up @@ -566,7 +587,8 @@ describe('ChartRoute', () => {
conditionTree: { field: 'title', operator: 'NotContains', value: '[test]' },
search: null,
searchExtended: false,
segment: null,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ field: undefined, groups: [{ field: 'author:firstName' }], operation: 'Count' },
);
Expand Down Expand Up @@ -628,7 +650,8 @@ describe('ChartRoute', () => {
}),
search: null,
searchExtended: false,
segment: null,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{
field: undefined,
Expand Down Expand Up @@ -709,7 +732,8 @@ describe('ChartRoute', () => {
}),
search: null,
searchExtended: false,
segment: null,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{
field: undefined,
Expand Down Expand Up @@ -770,7 +794,13 @@ describe('ChartRoute', () => {
request: { ip: expect.any(String) },
timezone: 'Europe/Paris',
},
{ conditionTree: null, search: null, searchExtended: false, segment: null },
{
conditionTree: null,
search: null,
searchExtended: false,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ field: 'id', groups: [{ field: 'author:id' }], operation: 'Sum' },
2,
);
Expand Down Expand Up @@ -821,7 +851,13 @@ describe('ChartRoute', () => {
request: { ip: expect.any(String) },
timezone: 'Europe/Paris',
},
{ conditionTree: null, search: null, searchExtended: false, segment: null },
{
conditionTree: null,
search: null,
searchExtended: false,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ operation: 'Count', field: null, groups: [{ field: 'publisher:id' }] },
2,
);
Expand Down Expand Up @@ -887,7 +923,8 @@ describe('ChartRoute', () => {
},
search: null,
searchExtended: false,
segment: null,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ field: 'id', groups: [{ field: 'author:id' }], operation: 'Sum' },
2,
Expand Down Expand Up @@ -941,7 +978,13 @@ describe('ChartRoute', () => {
request: { ip: expect.any(String) },
timezone: 'Europe/Paris',
},
{ conditionTree: null, search: null, searchExtended: false, segment: null },
{
conditionTree: null,
search: null,
searchExtended: false,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ groups: [{ field: 'author:id' }], operation: 'Count' },
2,
);
Expand Down
4 changes: 3 additions & 1 deletion packages/agent/test/routes/access/count-related.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ describe('CountRelatedRoute', () => {
search: 'searched argument',
searchExtended: false,
segment: 'a-valid-segment',
liveQuerySegment: expect.toBeNil(),
conditionTree: new ConditionTreeLeaf(
'id',
'Equal',
Expand Down Expand Up @@ -198,7 +199,7 @@ describe('CountRelatedRoute', () => {
],
}),
};
const segmentParams = { segment: 'a-valid-segment' };
const segmentParams = { segment: 'a-valid-segment', liveQuerySegment: expect.toBeNil() };

const context = createMockContext({
customProperties: {
Expand Down Expand Up @@ -238,6 +239,7 @@ describe('CountRelatedRoute', () => {
search: 'searched argument',
searchExtended: false,
segment: 'a-valid-segment',
liveQuerySegment: expect.toBeNil(),
conditionTree: ConditionTreeFactory.fromPlainObject({
aggregator: 'And',
conditions: [
Expand Down
11 changes: 9 additions & 2 deletions packages/agent/test/routes/access/count.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@ describe('CountRoute', () => {
request: { ip: expect.any(String) },
timezone: 'Europe/Paris',
},
{ conditionTree: null, search: null, searchExtended: false, segment: null },
{
conditionTree: null,
search: null,
searchExtended: false,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ operation: 'Count' },
);
expect(context.response.body).toEqual({ count: 2 });
Expand Down Expand Up @@ -94,7 +100,8 @@ describe('CountRoute', () => {
},
search: null,
searchExtended: false,
segment: null,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
},
{ operation: 'Count' },
);
Expand Down
4 changes: 3 additions & 1 deletion packages/agent/test/routes/access/list-related.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ describe('ListRelatedRoute', () => {
page: new Page(0, 15),
sort: new Sort({ field: 'id', ascending: true }),
segment: 'a-valid-segment',
liveQuerySegment: expect.toBeNil(),
conditionTree: new ConditionTreeLeaf(
'id',
'Equal',
Expand Down Expand Up @@ -184,7 +185,7 @@ describe('ListRelatedRoute', () => {
],
}),
};
const segmentParams = { segment: 'a-valid-segment' };
const segmentParams = { segment: 'a-valid-segment', liveQuerySegment: expect.toBeNil() };
const projectionParams = { 'fields[persons]': 'id,name' };
const context = createMockContext({
state: { user: { email: 'john.doe@domain.com' } },
Expand Down Expand Up @@ -225,6 +226,7 @@ describe('ListRelatedRoute', () => {
page: new Page(0, 15),
sort: new Sort({ field: 'id', ascending: true }),
segment: 'a-valid-segment',
liveQuerySegment: expect.toBeNil(),
conditionTree: ConditionTreeFactory.fromPlainObject({
aggregator: 'And',
conditions: [
Expand Down
6 changes: 4 additions & 2 deletions packages/agent/test/routes/access/list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ describe('ListRoute', () => {
conditionTree: null,
search: '2',
searchExtended: false,
segment: null,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
page: { limit: 15, skip: 0 },
sort: [{ ascending: true, field: 'id' }],
},
Expand Down Expand Up @@ -132,7 +133,8 @@ describe('ListRoute', () => {
},
search: '2',
searchExtended: false,
segment: null,
segment: expect.toBeNil(),
liveQuerySegment: expect.toBeNil(),
page: { limit: 15, skip: 0 },
sort: [{ ascending: true, field: 'id' }],
},
Expand Down
Loading

0 comments on commit 3f1bd1a

Please sign in to comment.