mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-29 01:41:42 +00:00
feat(group): add thrift contract type
This commit is contained in:
parent
c1e890d2a9
commit
dff02c19e9
6 changed files with 104 additions and 7 deletions
|
|
@ -4,12 +4,13 @@ import type { GroupConfig, GroupManifestLink, ContractType, ContractRole } from
|
|||
const _require = createRequire(import.meta.url);
|
||||
const yaml = _require('js-yaml') as typeof import('js-yaml');
|
||||
|
||||
const VALID_CONTRACT_TYPES: ContractType[] = ['http', 'grpc', 'topic', 'lib', 'custom'];
|
||||
const VALID_CONTRACT_TYPES: ContractType[] = ['http', 'grpc', 'thrift', 'topic', 'lib', 'custom'];
|
||||
const VALID_ROLES: ContractRole[] = ['provider', 'consumer'];
|
||||
|
||||
const DEFAULT_DETECT = {
|
||||
http: true,
|
||||
grpc: true,
|
||||
thrift: true,
|
||||
topics: true,
|
||||
shared_libs: true,
|
||||
embedding_fallback: true,
|
||||
|
|
|
|||
|
|
@ -177,7 +177,7 @@ export class ManifestExtractor {
|
|||
|
||||
// NOTE: All lookups use EXACT equality on the relevant name field and
|
||||
// deterministic ORDER BY before LIMIT 1. Previous versions used CONTAINS
|
||||
// for fuzzy matching (plus an unconditional ".proto" fallback for gRPC)
|
||||
// for fuzzy matching (plus an unconditional IDL file fallback for gRPC)
|
||||
// which produced silent false positives: e.g. manifest "/orders" would
|
||||
// match "/suborders", and a gRPC manifest entry in a repo with any
|
||||
// .proto file would attach to a random proto symbol.
|
||||
|
|
@ -225,16 +225,16 @@ export class ManifestExtractor {
|
|||
LIMIT 1`,
|
||||
{ contract: link.contract },
|
||||
);
|
||||
} else if (link.type === 'grpc') {
|
||||
} else if (link.type === 'grpc' || link.type === 'thrift') {
|
||||
// Contract is "Service/Method" or just "Service" (or package.Service
|
||||
// variants). Prefer matching by method name when present, otherwise
|
||||
// by service name. NO .proto path fallback — that's guaranteed to
|
||||
// return a wrong symbol in any repo with more than one proto file.
|
||||
// by service name. NO IDL path fallback — that's guaranteed to
|
||||
// return a wrong symbol in any repo with more than one IDL file.
|
||||
// Label filters scope lookups: methods → Function|Method, services
|
||||
// → Class|Interface (no label match = no silent wrong hits on
|
||||
// File/Variable nodes that happen to share the name).
|
||||
const parts = link.contract.split('/');
|
||||
const serviceName = parts[0]?.trim() ?? '';
|
||||
const serviceName = parts[0]?.trim().split('.').pop() ?? '';
|
||||
const methodName = parts[1]?.trim() ?? '';
|
||||
if (methodName) {
|
||||
rows = await executor(
|
||||
|
|
@ -329,6 +329,8 @@ export class ManifestExtractor {
|
|||
}
|
||||
case 'grpc':
|
||||
return `grpc::${contract}`;
|
||||
case 'thrift':
|
||||
return `thrift::${contract}`;
|
||||
case 'topic':
|
||||
return `topic::${contract}`;
|
||||
case 'lib':
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export type ContractType = 'http' | 'grpc' | 'topic' | 'lib' | 'custom';
|
||||
export type ContractType = 'http' | 'grpc' | 'thrift' | 'topic' | 'lib' | 'custom';
|
||||
export type MatchType = 'exact' | 'manifest' | 'wildcard' | 'bm25' | 'embedding';
|
||||
export type ContractRole = 'provider' | 'consumer';
|
||||
|
||||
|
|
@ -24,6 +24,7 @@ export interface GroupManifestLink {
|
|||
export interface DetectConfig {
|
||||
http: boolean;
|
||||
grpc: boolean;
|
||||
thrift: boolean;
|
||||
topics: boolean;
|
||||
shared_libs: boolean;
|
||||
embedding_fallback: boolean;
|
||||
|
|
|
|||
|
|
@ -62,6 +62,36 @@ repos:
|
|||
expect(config.matching.bm25_threshold).toBe(0.7);
|
||||
});
|
||||
|
||||
it('defaults thrift detection to true', () => {
|
||||
const minimal = `
|
||||
version: 1
|
||||
name: test
|
||||
repos:
|
||||
app: my-app
|
||||
`;
|
||||
const config = parseGroupConfig(minimal);
|
||||
expect(config.detect.thrift).toBe(true);
|
||||
});
|
||||
|
||||
it('parses thrift manifest links', () => {
|
||||
const yaml = `
|
||||
version: 1
|
||||
name: test
|
||||
repos:
|
||||
gateway: gateway-repo
|
||||
orders: orders-repo
|
||||
links:
|
||||
- from: gateway
|
||||
to: orders
|
||||
type: thrift
|
||||
contract: billing.v1.OrderService/PlaceOrder
|
||||
role: consumer
|
||||
`;
|
||||
const config = parseGroupConfig(yaml);
|
||||
expect(config.links[0].type).toBe('thrift');
|
||||
expect(config.links[0].contract).toBe('billing.v1.OrderService/PlaceOrder');
|
||||
});
|
||||
|
||||
it('throws on missing required fields', () => {
|
||||
expect(() => parseGroupConfig('version: 1')).toThrow(/name.*required/i);
|
||||
expect(() => parseGroupConfig('name: test')).toThrow(/version.*required/i);
|
||||
|
|
|
|||
|
|
@ -578,6 +578,33 @@ describe('ManifestExtractor', () => {
|
|||
expect(lowerContractId).toBe(upperContractId);
|
||||
});
|
||||
|
||||
it('builds thrift manifest contracts with synthetic uids when unresolved', async () => {
|
||||
const extractor = new ManifestExtractor();
|
||||
const result = await extractor.extractFromManifest([
|
||||
{
|
||||
from: 'gateway',
|
||||
to: 'orders',
|
||||
type: 'thrift',
|
||||
contract: 'billing.v1.OrderService/PlaceOrder',
|
||||
role: 'consumer',
|
||||
},
|
||||
]);
|
||||
|
||||
expect(result.contracts).toHaveLength(2);
|
||||
expect(result.contracts.map((c) => c.contractId)).toEqual([
|
||||
'thrift::billing.v1.OrderService/PlaceOrder',
|
||||
'thrift::billing.v1.OrderService/PlaceOrder',
|
||||
]);
|
||||
expect(result.crossLinks).toHaveLength(1);
|
||||
expect(result.crossLinks[0].type).toBe('thrift');
|
||||
expect(result.crossLinks[0].from.symbolUid).toBe(
|
||||
'manifest::gateway::thrift::billing.v1.OrderService/PlaceOrder',
|
||||
);
|
||||
expect(result.crossLinks[0].to.symbolUid).toBe(
|
||||
'manifest::orders::thrift::billing.v1.OrderService/PlaceOrder',
|
||||
);
|
||||
});
|
||||
|
||||
it('returns empty for no links', async () => {
|
||||
const result = await extractor.extractFromManifest([]);
|
||||
expect(result.contracts).toHaveLength(0);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ describe('Group types', () => {
|
|||
detect: {
|
||||
http: true,
|
||||
grpc: true,
|
||||
thrift: true,
|
||||
topics: true,
|
||||
shared_libs: true,
|
||||
embedding_fallback: true,
|
||||
|
|
@ -63,6 +64,41 @@ describe('Group types', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('ExtractedContract accepts thrift contract type', () => {
|
||||
const contract: ExtractedContract = {
|
||||
contractId: 'thrift::billing.v1.OrderService/PlaceOrder',
|
||||
type: 'thrift',
|
||||
role: 'provider',
|
||||
symbolUid: 'uid-thrift',
|
||||
symbolRef: { filePath: 'idl/order.thrift', name: 'OrderService.PlaceOrder' },
|
||||
symbolName: 'OrderService.PlaceOrder',
|
||||
confidence: 0.9,
|
||||
meta: {},
|
||||
};
|
||||
expect(contract.type).toBe('thrift');
|
||||
});
|
||||
|
||||
it('DetectConfig includes thrift toggle', () => {
|
||||
const config: GroupConfig = {
|
||||
version: 1,
|
||||
name: 'company',
|
||||
description: 'All company microservices',
|
||||
repos: { orders: 'orders-repo' },
|
||||
links: [],
|
||||
packages: {},
|
||||
detect: {
|
||||
http: true,
|
||||
grpc: true,
|
||||
thrift: true,
|
||||
topics: true,
|
||||
shared_libs: true,
|
||||
embedding_fallback: true,
|
||||
},
|
||||
matching: { bm25_threshold: 0.7, embedding_threshold: 0.65, max_candidates_per_step: 3 },
|
||||
};
|
||||
expect(config.detect.thrift).toBe(true);
|
||||
});
|
||||
|
||||
it('CrossLink stores match metadata', () => {
|
||||
const link: CrossLink = {
|
||||
from: {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue