Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion controlplane/.eslintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"extends": ["eslint-config-unjs", "plugin:require-extensions/recommended"],
"plugins": ["require-extensions"],
"plugins": ["require-extensions", "local-rules"],
"rules": {
"space-before-function-paren": 0,
"arrow-parens": 0,
Expand All @@ -25,5 +25,6 @@
"no-useless-constructor": 0,
"unicorn/prefer-ternary": 0,
"unicorn/no-nested-ternary": 0,
"local-rules/no-arrow-in-traced": "error",
},
}
65 changes: 65 additions & 0 deletions controlplane/eslint-local-rules.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
'use strict';

function isTraced(node) {
if (node.decorators && node.decorators.some((d) => d.expression && d.expression.name === 'traced')) {
return true;
}

const parent = node.parent;
if (!parent || !parent.body) {
return false;
}
const siblings = parent.body;
const idx = siblings.indexOf(node);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
for (let i = idx + 1; i < siblings.length; i++) {
const sibling = siblings[i];
if (
sibling.type === 'ExpressionStatement' &&
sibling.expression &&
sibling.expression.type === 'CallExpression' &&
sibling.expression.callee &&
sibling.expression.callee.name === 'traced' &&
sibling.expression.arguments.length === 1 &&
sibling.expression.arguments[0].name === node.id?.name
) {
return true;
}
if (sibling.type === 'ClassDeclaration' || sibling.type === 'FunctionDeclaration') {
break;
}
}
return false;
}

module.exports = {
'no-arrow-in-traced': {
meta: {
type: 'problem',
docs: {
description: 'Disallow arrow function class fields in @traced classes',
},
messages: {
noArrowInTraced:
'Arrow function class fields are not traced by the @traced decorator. Convert to a regular method.',
},
},
create(context) {
return {
ClassDeclaration(node) {
if (!isTraced(node)) {
return;
}
for (const member of node.body.body) {
if (
member.type === 'PropertyDefinition' &&
member.value &&
member.value.type === 'ArrowFunctionExpression'
) {
context.report({ node: member, messageId: 'noArrowInTraced' });
}
}
},
};
},
},
};
3 changes: 3 additions & 0 deletions controlplane/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"lint": "eslint --cache --ext .ts,.mjs,.cjs . && prettier -c src",
"lint:fix": "eslint --cache --fix --ext .ts,.mjs,.cjs . && pnpm format",
"format": "prettier -w .",
"sentry:spotlight": "spotlight",
"migrate": "pnpm db:migrate && pnpm ch:migrate",
"drizzle:up": "drizzle-kit up",
"ch:down": "dbmate -d \"./clickhouse/migrations\" down",
Expand Down Expand Up @@ -106,6 +107,7 @@
"@bufbuild/protobuf": "^1.9.0",
"@bufbuild/protoc-gen-es": "^1.9.0",
"@connectrpc/protoc-gen-connect-es": "^1.4.0",
"@spotlightjs/spotlight": "^4.10.0",
"@types/cookie": "^0.6.0",
"@types/ejs": "^3.1.5",
"@types/eslint": "^9.6.1",
Expand All @@ -118,6 +120,7 @@
"del-cli": "^5.1.0",
"drizzle-kit": "^0.26.2",
"eslint-config-unjs": "^0.2.1",
"eslint-plugin-local-rules": "^3.0.2",
"eslint-plugin-require-extensions": "^0.1.3",
"msw": "^2.2.11",
"pino-pretty": "^10.3.1",
Expand Down
4 changes: 3 additions & 1 deletion controlplane/src/core/auth-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import { OrganizationGroupRepository } from './repositories/OrganizationGroupRep
import { DefaultNamespace, NamespaceRepository } from './repositories/NamespaceRepository.js';
import Keycloak from './services/Keycloak.js';
import { IPlatformWebhookService } from './webhooks/PlatformWebhookService.js';

import { traced } from './tracing.js';
export type AuthUtilsOptions = {
webBaseUrl: string;
webErrorPath: string;
Expand Down Expand Up @@ -711,3 +711,5 @@ export default class AuthUtils {
return 0;
}
}

traced(AuthUtils);
2 changes: 2 additions & 0 deletions controlplane/src/core/blobstorage/dual.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { traced } from '../tracing.js';
import type { BlobObject, BlobStorage } from './index.js';

/**
Expand All @@ -6,6 +7,7 @@ import type { BlobObject, BlobStorage } from './index.js';
* - Writes and deletes go to both stores concurrently; both must succeed.
* - Reads try the primary first, falling back to the secondary on failure.
*/
@traced
export class DualBlobStorage implements BlobStorage {
constructor(
private primary: BlobStorage,
Expand Down
2 changes: 2 additions & 0 deletions controlplane/src/core/blobstorage/s3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
PutObjectCommand,
S3Client,
} from '@aws-sdk/client-s3';
import { traced } from '../tracing.js';
import { BlobNotFoundError, BlobObject, type BlobStorage } from './index.js';

const maxConcurrency = 10; // Maximum number of concurrent operations
Expand All @@ -26,6 +27,7 @@ export interface S3BlobStorageConfig {
/**
* Stores objects in S3 given an S3Client and a bucket name
*/
@traced
export class S3BlobStorage implements BlobStorage {
private readonly useIndividualDeletes: boolean;

Expand Down
42 changes: 40 additions & 2 deletions controlplane/src/core/build-server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Fastify, { FastifyBaseLogger } from 'fastify';
import { S3Client } from '@aws-sdk/client-s3';
import { fastifyConnectPlugin } from '@connectrpc/connect-fastify';
import * as Sentry from '@sentry/node';
import { cors, createContextValues } from '@connectrpc/connect';
import fastifyCors from '@fastify/cors';
import { pino, stdTimeFunctions, LoggerOptions } from 'pino';
Expand Down Expand Up @@ -37,7 +38,13 @@ import { BillingRepository } from './repositories/BillingRepository.js';
import { BillingService } from './services/BillingService.js';
import { UserRepository } from './repositories/UserRepository.js';
import { AIGraphReadmeQueue, createAIGraphReadmeWorker } from './workers/AIGraphReadmeWorker.js';
import { fastifyLoggerId, createS3ClientConfig, extractS3BucketName, isGoogleCloudStorageUrl } from './util.js';
import {
fastifyLoggerId,
sentrySpanId,
createS3ClientConfig,
extractS3BucketName,
isGoogleCloudStorageUrl,
} from './util.js';
import { ApiKeyRepository } from './repositories/ApiKeyRepository.js';
import { createDeleteOrganizationWorker, DeleteOrganizationQueue } from './workers/DeleteOrganizationWorker.js';
import {
Expand Down Expand Up @@ -531,6 +538,16 @@ export default async function build(opts: BuildConfig) {
keycloakRealm: opts.keycloak.realm,
});

// Capture the active Sentry span in preHandler (where OTEL context is still available)
// and store it on the request so Connect interceptors can use it as parentSpan.
fastify.addHook('preHandler', (req, _reply, done) => {
const span = Sentry.getActiveSpan();
if (span) {
(req.raw as any).__sentrySpan = span;
}
done();
});

// Must be registered after custom fastify routes
// Because it registers an all-catch route for connect handlers

Expand Down Expand Up @@ -567,7 +584,16 @@ export default async function build(opts: BuildConfig) {
cdnBaseUrl: opts.cdnBaseUrl,
}),
contextValues(req) {
return createContextValues().set<FastifyBaseLogger>({ id: fastifyLoggerId, defaultValue: req.log }, req.log);
const values = createContextValues().set<FastifyBaseLogger>(
{ id: fastifyLoggerId, defaultValue: req.log },
req.log,
);
// Read the parent span captured during the preHandler hook
const parentSpan = (req.raw as any).__sentrySpan;
if (parentSpan) {
values.set({ id: sentrySpanId, defaultValue: undefined }, parentSpan);
}
return values;
},
logLevel: opts.logger.level as pino.LevelWithSilent,
// Avoid compression for small requests
Expand All @@ -578,6 +604,18 @@ export default async function build(opts: BuildConfig) {
// We go with 32MiB to avoid allocating too much memory for large requests
writeMaxBytes: 32 * 1024 * 1024,
acceptCompression: [compressionBrotli, compressionGzip],
interceptors: [
(next) => (req) => {
const parentSpan = req.contextValues?.get({
id: sentrySpanId,
defaultValue: undefined,
});
if (parentSpan) {
return Sentry.withActiveSpan(parentSpan, () => next(req));
}
return next(req);
},
],
});

await fastify.register(fastifyGracefulShutdown, {
Expand Down
2 changes: 2 additions & 0 deletions controlplane/src/core/clickhouse/client/ClickHouseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import pkg from 'stream-json';

import { Observable, Subscriber } from 'rxjs';

import { traced } from '../../tracing.js';
import { ClickHouseCompressionMethod, ClickHouseDataFormat } from './enums/index.js';

import { ClickHouseClientOptions } from './interfaces/index.js';
Expand All @@ -16,6 +17,7 @@ const { Parser } = pkg;
* ClickHouse Client
* Most of the code is taken from https://github.com/depyronick/clickhouse-client
*/
@traced
export class ClickHouseClient {
/**
* ClickHouse Endpoint without path and query
Expand Down
2 changes: 2 additions & 0 deletions controlplane/src/core/composition/composer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { CacheWarmerRepository } from '../repositories/CacheWarmerRepository.js'
import { NamespaceRepository } from '../repositories/NamespaceRepository.js';
import { InspectorSchemaChange } from '../services/SchemaUsageTrafficInspector.js';
import { SchemaCheckChangeAction } from '../../db/models.js';
import { traced } from '../tracing.js';
import {
composeGraphsInWorker,
DeserializedComposedGraph,
Expand Down Expand Up @@ -136,6 +137,7 @@ export type CheckSubgraph = {
// will be used only for new subgraphs
labels?: Label[];
};
@traced
export class Composer {
constructor(
private logger: FastifyBaseLogger,
Expand Down
2 changes: 2 additions & 0 deletions controlplane/src/core/repositories/ApiKeyRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
import * as schema from '../../db/schema.js';
import { apiKeyPermissions, apiKeyResources, apiKeys, users } from '../../db/schema.js';
import { APIKeyDTO } from '../../types/index.js';
import { traced } from '../tracing.js';

/**
* Repository for organization related operations.
*/
@traced
export class ApiKeyRepository {
constructor(private db: PostgresJsDatabase<typeof schema>) {}

Expand Down
2 changes: 2 additions & 0 deletions controlplane/src/core/repositories/AuditLogRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
import { desc, eq, gt, lt, and, sql, count } from 'drizzle-orm';
import * as schema from '../../db/schema.js';
import { AuditableType, AuditActorType, AuditLogAction, AuditLogFullAction, AuditTargetType } from '../../db/models.js';
import { traced } from '../tracing.js';

export type AddAuditLogInput = {
organizationId: string;
Expand All @@ -25,6 +26,7 @@ export type AddAuditLogInput = {
/**
* Repository for audit log related operations.
*/
@traced
export class AuditLogRepository {
constructor(private db: PostgresJsDatabase<typeof schema>) {}

Expand Down
2 changes: 2 additions & 0 deletions controlplane/src/core/repositories/BillingRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { DB } from '../../db/index.js';
import { billingPlans, billingSubscriptions, organizationBilling } from '../../db/schema.js';
import { BillingPlanDTO } from '../../types/index.js';
import { BillingService } from '../services/BillingService.js';
import { traced } from '../tracing.js';

export const billingSchema = z.object({
plans: z.record(
Expand All @@ -26,6 +27,7 @@ export const billingSchema = z.object({
/**
* BillingRepository for billing related operations.
*/
@traced
export class BillingRepository {
constructor(private db: DB) {}

Expand Down
2 changes: 2 additions & 0 deletions controlplane/src/core/repositories/CacheWarmerRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { BlobStorage } from '../blobstorage/index.js';
import { ClickHouseClient } from '../clickhouse/index.js';
import { S3RouterConfigMetadata } from '../composition/composer.js';
import { CacheWarmupOperation } from '../../db/models.js';
import { traced } from '../tracing.js';
import { getDateRange, isoDateRangeToTimestamps } from './analytics/util.js';
import { OperationsRepository } from './OperationsRepository.js';

Expand All @@ -28,6 +29,7 @@ interface ComputeCacheWarmerOperationsProps {
maxOperationsCount: number;
}

@traced
export class CacheWarmerRepository {
constructor(
private client: ClickHouseClient,
Expand Down
2 changes: 2 additions & 0 deletions controlplane/src/core/repositories/ContractRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
import { FastifyBaseLogger } from 'fastify';
import * as schema from '../../db/schema.js';
import { FederatedGraphDTO } from '../../types/index.js';
import { traced } from '../tracing.js';
import { FederatedGraphRepository } from './FederatedGraphRepository.js';

@traced
export class ContractRepository {
constructor(
private logger: FastifyBaseLogger,
Expand Down
2 changes: 2 additions & 0 deletions controlplane/src/core/repositories/FeatureFlagRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
} from '../../types/index.js';
import { normalizeLabels } from '../util.js';
import { RBACEvaluator } from '../services/RBACEvaluator.js';
import { traced } from '../tracing.js';
import { FederatedGraphRepository } from './FederatedGraphRepository.js';
import { SubgraphRepository } from './SubgraphRepository.js';
import { UserRepository } from './UserRepository.js';
Expand Down Expand Up @@ -62,6 +63,7 @@ export type CheckConstituentFeatureSubgraphsResult = {
featureSubgraphIds: Array<string>;
};

@traced
export class FeatureFlagRepository {
constructor(
private logger: FastifyBaseLogger,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import { checkIfLabelMatchersChanged, normalizeLabelMatchers, normalizeLabels }
import { unsuccessfulBaseCompositionError } from '../errors/errors.js';
import { ClickHouseClient } from '../clickhouse/index.js';
import { RBACEvaluator } from '../services/RBACEvaluator.js';
import { traced } from '../tracing.js';
Comment thread
coderabbitai[bot] marked this conversation as resolved.
import { ContractRepository } from './ContractRepository.js';
import { FeatureFlagRepository, SubgraphsToCompose } from './FeatureFlagRepository.js';
import { GraphCompositionRepository } from './GraphCompositionRepository.js';
Expand All @@ -85,6 +86,7 @@ export interface FederatedGraphConfig {
trafficCheckDays: number;
}

@traced
export class FederatedGraphRepository {
constructor(
private logger: FastifyBaseLogger,
Expand Down Expand Up @@ -1501,7 +1503,7 @@ export class FederatedGraphRepository {
/**
* This method recomposes and deploys federated graphs and their respective contract graphs.
*/
public composeAndDeployGraphs = ({
public composeAndDeployGraphs({
actorId,
admissionConfig,
compositionOptions,
Expand All @@ -1520,7 +1522,7 @@ export class FederatedGraphRepository {
federatedGraphs: FederatedGraphDTO[];
compositionOptions?: CompositionOptions;
webhookProxyUrl?: string;
}) => {
}) {
return this.db.transaction(async (tx) => {
const subgraphRepo = new SubgraphRepository(this.logger, tx, this.organizationId);
const fedGraphRepo = new FederatedGraphRepository(this.logger, tx, this.organizationId);
Expand Down Expand Up @@ -1860,7 +1862,7 @@ export class FederatedGraphRepository {
compositionWarnings: allCompositionWarnings,
};
});
};
}

public updateRouterCompatibilityVersion(id: string, version: string) {
return this.db
Expand Down
2 changes: 2 additions & 0 deletions controlplane/src/core/repositories/GitHubRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import { eq } from 'drizzle-orm';
import { CompositionError, GitInfo } from '@wundergraph/cosmo-connect/dist/platform/v1/platform_pb';
import { PlainMessage } from '@bufbuild/protobuf';
import * as schema from '../../db/schema.js';
import { traced } from '../tracing.js';

@traced
export class GitHubRepository {
constructor(
private db: PostgresJsDatabase<typeof schema>,
Expand Down
Loading
Loading