Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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",
},
}
71 changes: 71 additions & 0 deletions controlplane/eslint-local-rules.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use strict';

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

let container = node.parent;
let classNode = node;
// export default class wraps the ClassDeclaration in ExportDefaultDeclaration
if (container && container.type === 'ExportDefaultDeclaration') {
classNode = container;
container = container.parent;
}
if (!container || !container.body) {
return false;
}
const siblings = container.body;
const idx = siblings.indexOf(classNode);
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
Loading
Loading