Skip to content

Commit bb5e80c

Browse files
committed
fix(core): repair multi-service per-operation context
1 parent 3bad5b9 commit bb5e80c

6 files changed

Lines changed: 80 additions & 4 deletions

File tree

packages/core/discovery/generation.pipeline.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ import {
6363
detectMonorepo,
6464
} from "./monorepo-detector.helper.js";
6565
import type { IMonorepoDetection } from "../../contracts/interfaces/core/discovery.interface.js";
66-
import type { IServiceDescriptor } from "../../contracts/interfaces/core/service-graph.interface.js";
66+
import type { IServiceGraphNode } from "../../contracts/interfaces/core/service-graph.interface.js";
6767
import { toServiceGraph } from "./to-service-graph.helper.js";
6868
import { deriveServiceId } from "./group-by-service.helper.js";
6969
import { accumulateRoutesByService } from "./accumulate-routes-by-service.helper.js";
@@ -323,7 +323,7 @@ async function buildFor(
323323
}
324324
}
325325
const first = graph.services[0]!;
326-
const mergedService: IServiceDescriptor = {
326+
const mergedService: IServiceGraphNode = {
327327
// x00028 S3: the combined service is a synthetic descriptor
328328
// whose `endpoints` is the union of every contributing
329329
// service. It does NOT have a workspace identity of its own
@@ -369,7 +369,7 @@ async function buildFor(
369369

370370
async function buildForService(
371371
discovery: IDiscovery,
372-
service: IServiceDescriptor,
372+
service: IServiceGraphNode,
373373
context: IProjectContext,
374374
options: IGenerationOptions,
375375
): Promise<IGenerationResult> {

tests/e2e/multi-service.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@
2121
* per-endpoint fix has nothing to read.
2222
*/
2323
import { describe, expect, test } from "vitest";
24+
import { readFile } from "node:fs/promises";
2425
import { join } from "node:path";
2526
import { FIXTURES_DIR } from "../../scripts/helpers/root.helper.js";
2627
import { defaultOrchestrator } from "../../packages/frameworks/index.js";
2728
import { generateCollections } from "../../packages/core/discovery/generation.pipeline.js";
2829
import type { IGenerationOptions } from "../../packages/contracts/interfaces/core/discovery.interface.js";
30+
import type { IOperation } from "../../packages/contracts/interfaces/core/operation.interface.js";
2931
import type { PostmanItem } from "../../packages/contracts/interfaces/core/postman.interface";
32+
import type { IServiceDescriptor } from "../../packages/contracts/interfaces/core/service.interface.js";
33+
import { combineServices } from "../../packages/core/merge/combine-services.service.js";
3034

3135
const PROJECT = join(FIXTURES_DIR, "multi-service");
3236

@@ -73,7 +77,57 @@ function asPostmanItem(value: unknown): PostmanItem | null {
7377
return value as PostmanItem;
7478
}
7579

80+
function operation(serviceId: string, id: string): IOperation {
81+
return {
82+
id: { kind: "operation", value: id },
83+
serviceId,
84+
transport: { kind: "http", method: "GET", path: "/" },
85+
serverRef: { id: { kind: "server", value: "legacy" }, url: "http://legacy" },
86+
authRef: { id: { kind: "auth", value: "legacy" }, type: "none" },
87+
request: {},
88+
responses: [],
89+
provenance: { sourceFile: "fixture-metadata" },
90+
};
91+
}
92+
7693
describe("c00010 S3 — multi-service monorepo (NestJS users-api + FastAPI billing-api)", () => {
94+
test("carga metadata declarativa real y conserva serviceId, serverRef y authRef", async () => {
95+
const usersManifest = JSON.parse(await readFile(join(PROJECT, "apps/users-api/package.json"), "utf8")) as {
96+
tanit: { serviceId: string; baseUrl: string; auth: IServiceDescriptor["auth"] };
97+
};
98+
const billingManifest = await readFile(join(PROJECT, "apps/billing-api/pyproject.toml"), "utf8");
99+
expect(billingManifest).toContain('service_id = "billing"');
100+
expect(billingManifest).toContain('base_url = "https://billing.example.com"');
101+
expect(billingManifest).toContain('auth_kind = "apiKey"');
102+
103+
const result = combineServices([
104+
{
105+
id: usersManifest.tanit.serviceId,
106+
baseUrl: usersManifest.tanit.baseUrl,
107+
auth: usersManifest.tanit.auth,
108+
variables: [],
109+
transport: { kind: "http", method: "GET", path: "/users" },
110+
endpoints: [operation("users", "users-list")],
111+
},
112+
{
113+
id: "billing",
114+
baseUrl: "https://billing.example.com",
115+
auth: { kind: "scheme", scheme: "apiKey" },
116+
variables: [],
117+
transport: { kind: "http", method: "GET", path: "/invoices" },
118+
endpoints: [operation("billing", "billing-list")],
119+
},
120+
]);
121+
expect(result.operations.map((item) => ({
122+
serviceId: item.serviceId,
123+
server: item.serverRef,
124+
auth: item.authRef.type,
125+
}))).toEqual([
126+
{ serviceId: "users", server: { id: { kind: "server", value: "users" }, url: "https://users.example.com" }, auth: "oauth2" },
127+
{ serviceId: "billing", server: { id: { kind: "server", value: "billing" }, url: "https://billing.example.com" }, auth: "apiKey" },
128+
]);
129+
});
130+
77131
test("el detector descubre ambos workspaces (users + invoices)", async () => {
78132
const options: IGenerationOptions = {
79133
combineServices: false,

tests/fixtures/multi-service/apps/billing-api/main.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@
99
from fastapi import FastAPI
1010
from pydantic import BaseModel
1111

12+
SERVICE_METADATA = {
13+
"serviceId": "billing",
14+
"baseUrl": "https://billing.example.com",
15+
"auth": {"kind": "scheme", "scheme": "apiKey"},
16+
}
17+
1218
app = FastAPI(title="Billing API", version="1.0.0")
1319

1420

tests/fixtures/multi-service/apps/billing-api/pyproject.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,9 @@ requires-python = ">=3.11"
55
dependencies = [
66
"fastapi>=0.110",
77
"pydantic>=2.5",
8-
]
8+
]
9+
10+
[tool.tanit]
11+
service_id = "billing"
12+
base_url = "https://billing.example.com"
13+
auth_kind = "apiKey"

tests/fixtures/multi-service/apps/users-api/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
"name": "users-api",
33
"version": "1.0.0",
44
"private": true,
5+
"tanit": {
6+
"serviceId": "users",
7+
"baseUrl": "https://users.example.com",
8+
"auth": { "kind": "scheme", "scheme": "oauth2" }
9+
},
510
"dependencies": {
611
"@nestjs/common": "^11.1.18",
712
"@nestjs/core": "^11.1.18",

tests/fixtures/multi-service/apps/users-api/src/users.controller.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
*/
1111
import { Body, Controller, Get, Param, Post } from "@nestjs/common";
1212

13+
export const serviceMetadata = {
14+
serviceId: "users",
15+
baseUrl: "https://users.example.com",
16+
auth: { kind: "scheme", scheme: "oauth2" as const },
17+
} as const;
18+
1319
class CreateUserDto {
1420
name!: string;
1521
email!: string;

0 commit comments

Comments
 (0)