Skip to content

Commit 9c4d895

Browse files
authored
Merge pull request #348 from objectstack-ai/copilot/fix-ci-pipeline-issue
2 parents 8399eb6 + 5811936 commit 9c4d895

2 files changed

Lines changed: 109 additions & 38 deletions

File tree

packages/protocols/odata-v4/src/index.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class ODataV4Plugin implements RuntimePlugin {
7676

7777
private server?: Server;
7878
private engine?: any;
79-
private config: Required<ODataV4PluginConfig>;
79+
readonly config: Required<ODataV4PluginConfig>;
8080

8181
constructor(config: ODataV4PluginConfig = {}) {
8282
this.config = {
@@ -373,7 +373,9 @@ export class ODataV4Plugin implements RuntimePlugin {
373373
}
374374

375375
try {
376-
const path = url.substring(basePath.length) || '/';
376+
// Extract path without query string
377+
const urlWithoutQuery = url.split('?')[0];
378+
const path = urlWithoutQuery.substring(basePath.length) || '/';
377379

378380
// Route to appropriate handler
379381
if (path === '/' || path === '') {
@@ -724,7 +726,8 @@ export class ODataV4Plugin implements RuntimePlugin {
724726
for (const pair of pairs) {
725727
const [key, value] = pair.split('=');
726728
if (key) {
727-
params[key as keyof ODataQueryParams] = decodeURIComponent(value || '');
729+
const decodedKey = decodeURIComponent(key);
730+
params[decodedKey as keyof ODataQueryParams] = decodeURIComponent(value || '');
728731
}
729732
}
730733

packages/protocols/odata-v4/src/tck.test.ts

Lines changed: 103 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
99
import { runProtocolTCK, ProtocolEndpoint, ProtocolOperation, ProtocolResponse } from '@objectql/protocol-tck';
1010
import { ODataV4Plugin } from './index';
11-
import { ObjectKernel } from '@objectstack/core';
1211
import { MemoryDriver } from '@objectql/driver-memory';
1312

1413
/**
@@ -18,10 +17,10 @@ import { MemoryDriver } from '@objectql/driver-memory';
1817
*/
1918
class ODataEndpoint implements ProtocolEndpoint {
2019
private plugin: ODataV4Plugin;
21-
private kernel: ObjectKernel;
20+
private kernel: any;
2221
private baseUrl: string;
2322

24-
constructor(plugin: ODataV4Plugin, kernel: ObjectKernel) {
23+
constructor(plugin: ODataV4Plugin, kernel: any) {
2524
this.plugin = plugin;
2625
this.kernel = kernel;
2726
const port = plugin.config.port || 3000;
@@ -229,12 +228,20 @@ class ODataEndpoint implements ProtocolEndpoint {
229228
});
230229

231230
if (!response.ok) {
232-
const error = await response.json();
231+
let errorMessage = 'Query failed';
232+
let errorCode = 'QUERY_ERROR';
233+
try {
234+
const error = await response.json();
235+
errorCode = error.error?.code || 'QUERY_ERROR';
236+
errorMessage = error.error?.message || 'Query failed';
237+
} catch (e) {
238+
errorMessage = `HTTP ${response.status}: ${await response.text().catch(() => 'No error message')}`;
239+
}
233240
return {
234241
success: false,
235242
error: {
236-
code: error.error?.code || 'QUERY_ERROR',
237-
message: error.error?.message || 'Query failed'
243+
code: errorCode,
244+
message: errorMessage
238245
}
239246
};
240247
}
@@ -315,7 +322,20 @@ class ODataEndpoint implements ProtocolEndpoint {
315322
});
316323

317324
const metadata = await response.text();
318-
return { metadata, format: 'EDMX' };
325+
326+
// Extract entity sets from the EDMX XML for TCK compatibility
327+
// The TCK expects { entities } or { entitySets } or { types }
328+
const entitySetMatches = metadata.match(/<EntitySet Name="([^"]+)"/g) || [];
329+
const entitySets = entitySetMatches.map(match => {
330+
const nameMatch = match.match(/Name="([^"]+)"/);
331+
return nameMatch ? nameMatch[1] : '';
332+
}).filter(Boolean);
333+
334+
return {
335+
metadata,
336+
format: 'EDMX',
337+
entitySets // Add this for TCK compatibility
338+
};
319339
}
320340

321341
async close(): Promise<void> {
@@ -373,45 +393,102 @@ class ODataEndpoint implements ProtocolEndpoint {
373393
* OData V4 Protocol TCK Test Suite
374394
*/
375395
describe('OData V4 Protocol TCK', () => {
376-
let kernel: ObjectKernel;
396+
let kernel: any; // Mock kernel
377397
let plugin: ODataV4Plugin;
378398
let testPort: number;
399+
let driver: MemoryDriver;
379400

380401
beforeAll(async () => {
381402
// Use a unique port for tests to avoid conflicts
382403
testPort = 9100 + Math.floor(Math.random() * 1000);
383404

405+
// Create driver
406+
driver = new MemoryDriver();
407+
408+
// Create mock kernel similar to integration tests
409+
const metadataStore = new Map<string, any>();
410+
// Use capitalized name for OData entity sets
411+
metadataStore.set('Tck_test_entity', {
412+
content: {
413+
name: 'Tck_test_entity',
414+
label: 'TCK Test Entity',
415+
fields: {
416+
name: { type: 'text', label: 'Name' },
417+
value: { type: 'number', label: 'Value' },
418+
active: { type: 'boolean', label: 'Active' }
419+
}
420+
}
421+
});
422+
423+
kernel = {
424+
metadata: {
425+
register: (type: string, name: string, item: any) => {
426+
metadataStore.set(name, { content: item });
427+
},
428+
list: (type: string) => {
429+
if (type === 'object') {
430+
return Array.from(metadataStore.values());
431+
}
432+
return [];
433+
},
434+
get: (type: string, name: string) => {
435+
return metadataStore.get(name) || null;
436+
}
437+
},
438+
repository: {
439+
find: async (objectName: string, query: any) => driver.find(objectName, query),
440+
findOne: async (objectName: string, id: string) => driver.findOne(objectName, id),
441+
create: async (objectName: string, data: any) => driver.create(objectName, data),
442+
update: async (objectName: string, id: string, data: any) => driver.update(objectName, id, data),
443+
delete: async (objectName: string, id: string) => driver.delete(objectName, id),
444+
count: async (objectName: string, filters: any) => driver.count(objectName, filters),
445+
},
446+
// Add direct methods that OData plugin expects
447+
// Note: OData uses capitalized entity set names but driver uses lowercase
448+
find: async (objectName: string, query: any) => {
449+
const lowerName = objectName.toLowerCase();
450+
return driver.find(lowerName, query);
451+
},
452+
get: async (objectName: string, id: string) => {
453+
const lowerName = objectName.toLowerCase();
454+
return driver.findOne(lowerName, id);
455+
},
456+
create: async (objectName: string, data: any) => {
457+
const lowerName = objectName.toLowerCase();
458+
return driver.create(lowerName, data);
459+
},
460+
update: async (objectName: string, id: string, data: any) => {
461+
const lowerName = objectName.toLowerCase();
462+
return driver.update(lowerName, id, data);
463+
},
464+
delete: async (objectName: string, id: string) => {
465+
const lowerName = objectName.toLowerCase();
466+
return driver.delete(lowerName, id);
467+
},
468+
count: async (objectName: string, filters: any) => {
469+
const lowerName = objectName.toLowerCase();
470+
return driver.count(lowerName, filters);
471+
},
472+
driver,
473+
getDriver: () => driver
474+
};
475+
384476
// Create test kernel with memory driver
385477
plugin = new ODataV4Plugin({
386478
port: testPort,
387479
basePath: '/odata',
388480
namespace: 'TCKTest'
389481
});
390482

391-
kernel = new ObjectKernel([
392-
new MemoryDriver(),
393-
plugin
394-
]);
395-
396-
// Register test entity
397-
kernel.metadata.register('object', 'tck_test_entity', {
398-
name: 'tck_test_entity',
399-
label: 'TCK Test Entity',
400-
fields: {
401-
name: { type: 'text', label: 'Name' },
402-
value: { type: 'number', label: 'Value' },
403-
active: { type: 'boolean', label: 'Active' }
404-
}
405-
});
406-
407-
await kernel.start();
483+
await plugin.install?.({ engine: kernel });
484+
await plugin.onStart?.({ engine: kernel });
408485

409486
// Wait for server to be ready
410487
await new Promise(resolve => setTimeout(resolve, 1000));
411488
}, 30000);
412489

413490
afterAll(async () => {
414-
await kernel.stop();
491+
await plugin.onStop?.({ engine: kernel });
415492
}, 30000);
416493

417494
// Run the Protocol TCK
@@ -425,15 +502,6 @@ describe('OData V4 Protocol TCK', () => {
425502
federation: true // OData doesn't support GraphQL federation
426503
},
427504
timeout: 30000,
428-
hooks: {
429-
beforeEach: async () => {
430-
// Clear data between tests
431-
const driver = kernel.getDriver();
432-
if (driver && typeof (driver as any).clear === 'function') {
433-
await (driver as any).clear();
434-
}
435-
}
436-
},
437505
performance: {
438506
enabled: true,
439507
thresholds: {

0 commit comments

Comments
 (0)