mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-27 22:15:33 +00:00
fix(timeline): expose date window filters (#2694)
This commit is contained in:
+15
-2
@@ -2176,14 +2176,27 @@ const add_timeline_entry: Operation = {
|
||||
|
||||
const get_timeline: Operation = {
|
||||
name: 'get_timeline',
|
||||
description: 'Get timeline entries for a page',
|
||||
description: 'Get timeline entries for a page, optionally filtered by date window',
|
||||
params: {
|
||||
slug: { type: 'string', required: true },
|
||||
after: { type: 'string', description: 'Return entries on or after this date (YYYY-MM-DD)' },
|
||||
before: { type: 'string', description: 'Return entries on or before this date (YYYY-MM-DD)' },
|
||||
since: { type: 'string', description: 'Alias for after; accepted for agent callers' },
|
||||
until: { type: 'string', description: 'Alias for before; accepted for agent callers' },
|
||||
limit: { type: 'number', description: 'Maximum number of timeline entries to return' },
|
||||
},
|
||||
handler: async (ctx, p) => {
|
||||
// #2200: route through sourceScopeOpts so a federated grant reaches the
|
||||
// engine via TimelineOpts.sourceIds; scalar/unset unchanged.
|
||||
return ctx.engine.getTimeline(p.slug as string, sourceScopeOpts(ctx));
|
||||
const after = typeof p.after === 'string' ? p.after : typeof p.since === 'string' ? p.since : undefined;
|
||||
const before = typeof p.before === 'string' ? p.before : typeof p.until === 'string' ? p.until : undefined;
|
||||
const limit = typeof p.limit === 'number' ? p.limit : undefined;
|
||||
return ctx.engine.getTimeline(p.slug as string, {
|
||||
...sourceScopeOpts(ctx),
|
||||
...(after ? { after } : {}),
|
||||
...(before ? { before } : {}),
|
||||
...(limit !== undefined ? { limit } : {}),
|
||||
});
|
||||
},
|
||||
scope: 'read',
|
||||
cliHints: { name: 'timeline', positional: ['slug'] },
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import { operationsByName, type OperationContext } from '../src/core/operations.ts';
|
||||
import type { TimelineOpts } from '../src/core/types.ts';
|
||||
|
||||
const getTimeline = operationsByName['get_timeline'];
|
||||
|
||||
function makeCtx(): OperationContext {
|
||||
const calls: Array<{ slug: string; opts?: TimelineOpts }> = [];
|
||||
const engine = {
|
||||
getTimeline: async (slug: string, opts?: TimelineOpts) => {
|
||||
calls.push({ slug, opts });
|
||||
return [];
|
||||
},
|
||||
};
|
||||
|
||||
return {
|
||||
engine,
|
||||
config: {},
|
||||
logger: { info() {}, warn() {}, error() {}, debug() {} },
|
||||
dryRun: false,
|
||||
remote: true,
|
||||
sourceId: 'default',
|
||||
auth: {
|
||||
token: 'test',
|
||||
clientId: 'client',
|
||||
scopes: ['read'],
|
||||
sourceId: 'default',
|
||||
allowedSources: ['alpha', 'beta'],
|
||||
},
|
||||
__calls: calls,
|
||||
} as unknown as OperationContext & { __calls: Array<{ slug: string; opts?: TimelineOpts }> };
|
||||
}
|
||||
|
||||
describe('get_timeline op', () => {
|
||||
test('declares date-window and limit params', () => {
|
||||
expect(getTimeline.params.after.type).toBe('string');
|
||||
expect(getTimeline.params.before.type).toBe('string');
|
||||
expect(getTimeline.params.since.type).toBe('string');
|
||||
expect(getTimeline.params.until.type).toBe('string');
|
||||
expect(getTimeline.params.limit.type).toBe('number');
|
||||
});
|
||||
|
||||
test('threads after/before/limit with federated source scope', async () => {
|
||||
const ctx = makeCtx();
|
||||
await getTimeline.handler(ctx, {
|
||||
slug: 'people/alice-example',
|
||||
after: '2026-01-01',
|
||||
before: '2026-03-31',
|
||||
limit: 7,
|
||||
});
|
||||
|
||||
expect((ctx as typeof ctx & { __calls: Array<{ slug: string; opts?: TimelineOpts }> }).__calls).toEqual([{
|
||||
slug: 'people/alice-example',
|
||||
opts: {
|
||||
sourceIds: ['alpha', 'beta'],
|
||||
after: '2026-01-01',
|
||||
before: '2026-03-31',
|
||||
limit: 7,
|
||||
},
|
||||
}]);
|
||||
});
|
||||
|
||||
test('accepts since/until as aliases for after/before', async () => {
|
||||
const ctx = makeCtx();
|
||||
await getTimeline.handler(ctx, {
|
||||
slug: 'people/alice-example',
|
||||
since: '2026-04-01',
|
||||
until: '2026-04-30',
|
||||
});
|
||||
|
||||
expect((ctx as typeof ctx & { __calls: Array<{ slug: string; opts?: TimelineOpts }> }).__calls[0]?.opts).toMatchObject({
|
||||
sourceIds: ['alpha', 'beta'],
|
||||
after: '2026-04-01',
|
||||
before: '2026-04-30',
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user