1409 lines
40 KiB
TypeScript
1409 lines
40 KiB
TypeScript
import type { AuthenticatedEnvironment } from "@trigger.dev/core/v3/auth/environment";
|
|
import { Prisma, type PrismaClient, type RuntimeEnvironmentType } from "@trigger.dev/database";
|
|
import { z } from "zod";
|
|
import { environmentFullTitle } from "~/components/environments/EnvironmentLabel";
|
|
import { $replica, $transaction, prisma, type PrismaReplicaClient } from "~/db.server";
|
|
import { env } from "~/env.server";
|
|
import { getSecretStore } from "~/services/secrets/secretStore.server";
|
|
import { deduplicateVariableArray } from "../deduplicateVariableArray.server";
|
|
import { removeBlacklistedVariables } from "../environmentVariableRules.server";
|
|
import { generateFriendlyId } from "../friendlyIdentifiers";
|
|
import {
|
|
type CreateEnvironmentVariables,
|
|
type CreateResult,
|
|
type DeleteEnvironmentVariable,
|
|
type DeleteEnvironmentVariableValue,
|
|
type EditEnvironmentVariable,
|
|
type EditEnvironmentVariableValue,
|
|
type EnvironmentVariable,
|
|
type EnvironmentVariableWithSecret,
|
|
type ProjectEnvironmentVariable,
|
|
type Repository,
|
|
type Result,
|
|
} from "./repository";
|
|
|
|
function secretKeyProjectPrefix(projectId: string) {
|
|
return `environmentvariable:${projectId}:`;
|
|
}
|
|
|
|
function secretKeyEnvironmentPrefix(projectId: string, environmentId: string) {
|
|
return `${secretKeyProjectPrefix(projectId)}${environmentId}:`;
|
|
}
|
|
|
|
function secretKey(projectId: string, environmentId: string, key: string) {
|
|
return `${secretKeyEnvironmentPrefix(projectId, environmentId)}${key}`;
|
|
}
|
|
|
|
function parseSecretKey(key: string) {
|
|
const parts = key.split(":");
|
|
return {
|
|
projectId: parts[1],
|
|
environmentId: parts[2],
|
|
key: parts[3],
|
|
};
|
|
}
|
|
|
|
const SecretValue = z.object({ secret: z.string() });
|
|
|
|
export class EnvironmentVariablesRepository implements Repository {
|
|
constructor(
|
|
private prismaClient: PrismaClient = prisma,
|
|
private replicaClient: PrismaReplicaClient = $replica
|
|
) {}
|
|
|
|
async create(projectId: string, options: CreateEnvironmentVariables): Promise<CreateResult> {
|
|
const project = await this.prismaClient.project.findFirst({
|
|
where: {
|
|
id: projectId,
|
|
deletedAt: null,
|
|
},
|
|
select: {
|
|
environments: {
|
|
select: {
|
|
id: true,
|
|
},
|
|
},
|
|
environmentVariables: {
|
|
select: {
|
|
key: true,
|
|
values: {
|
|
select: {
|
|
environment: {
|
|
select: { id: true, type: true },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!project) {
|
|
return { success: false as const, error: "Project not found" };
|
|
}
|
|
|
|
if (options.environmentIds.every((v) => !project.environments.some((e) => e.id === v))) {
|
|
return { success: false as const, error: `Environment not found` };
|
|
}
|
|
|
|
// Remove `TRIGGER_SECRET_KEY` or `TRIGGER_API_URL`
|
|
let values = removeBlacklistedVariables(options.variables);
|
|
const removedBlacklisted = values.length !== options.variables.length;
|
|
|
|
//get rid of empty variables
|
|
values = values.filter((v) => v.key.trim() !== "" && v.value.trim() !== "");
|
|
if (values.length === 0) {
|
|
return {
|
|
success: false as const,
|
|
error: `You must set at least one valid variable.${
|
|
removedBlacklisted ? " All the variables submitted are not allowed." : ""
|
|
}`,
|
|
};
|
|
}
|
|
|
|
//check if any of them exist in an environment we're setting
|
|
if (!options.override) {
|
|
const existingVariableKeys: { key: string; environments: RuntimeEnvironmentType[] }[] = [];
|
|
for (const variable of values) {
|
|
const existingVariable = project.environmentVariables.find((v) => v.key === variable.key);
|
|
if (
|
|
existingVariable &&
|
|
existingVariable.values.some((v) => options.environmentIds.includes(v.environment.id))
|
|
) {
|
|
existingVariableKeys.push({
|
|
key: variable.key,
|
|
environments: existingVariable.values
|
|
.filter((v) => options.environmentIds.includes(v.environment.id))
|
|
.map((v) => v.environment.type),
|
|
});
|
|
}
|
|
}
|
|
|
|
if (existingVariableKeys.length > 0) {
|
|
return {
|
|
success: false as const,
|
|
error: `Some of the variables are already set for these environments. Set override to true to override them.`,
|
|
variableErrors: existingVariableKeys.map((val) => ({
|
|
key: val.key,
|
|
error: `Variable already set in ${val.environments
|
|
.map((e) => environmentFullTitle({ type: e }))
|
|
.join(", ")}.`,
|
|
})),
|
|
};
|
|
}
|
|
}
|
|
|
|
try {
|
|
for (const variable of values) {
|
|
const _result = await $transaction(this.prismaClient, "create env var", async (tx) => {
|
|
const environmentVariable = await tx.environmentVariable.upsert({
|
|
where: {
|
|
projectId_key: {
|
|
key: variable.key,
|
|
projectId,
|
|
},
|
|
},
|
|
create: {
|
|
key: variable.key,
|
|
friendlyId: generateFriendlyId("envvar"),
|
|
project: {
|
|
connect: {
|
|
id: projectId,
|
|
},
|
|
},
|
|
},
|
|
update: {},
|
|
});
|
|
|
|
const secretStore = getSecretStore("DATABASE", {
|
|
prismaClient: tx,
|
|
});
|
|
|
|
// If parentEnvironmentId is provided and isSecret is not explicitly set,
|
|
// look up if the parent has this variable marked as secret
|
|
let inheritedIsSecret: boolean | undefined = undefined;
|
|
if (options.isSecret === undefined && options.parentEnvironmentId) {
|
|
const parentVariableValue = await tx.environmentVariableValue.findFirst({
|
|
where: {
|
|
variableId: environmentVariable.id,
|
|
environmentId: options.parentEnvironmentId,
|
|
},
|
|
select: {
|
|
isSecret: true,
|
|
},
|
|
});
|
|
if (parentVariableValue?.isSecret) {
|
|
inheritedIsSecret = true;
|
|
}
|
|
}
|
|
|
|
const effectiveIsSecret = options.isSecret ?? inheritedIsSecret;
|
|
|
|
//set the secret values and references
|
|
for (const environmentId of options.environmentIds) {
|
|
const key = secretKey(projectId, environmentId, variable.key);
|
|
|
|
const existingValueRecord = await tx.environmentVariableValue.findFirst({
|
|
where: {
|
|
variableId: environmentVariable.id,
|
|
environmentId,
|
|
},
|
|
});
|
|
|
|
// Check if value already exists and is the same, and no metadata change (e.g. isSecret toggle)
|
|
const existingSecret = await secretStore.getSecret(SecretValue, key);
|
|
const canSkip =
|
|
existingSecret &&
|
|
existingSecret.secret === variable.value &&
|
|
existingValueRecord &&
|
|
(options.isSecret === undefined || existingValueRecord.isSecret === options.isSecret);
|
|
if (canSkip) {
|
|
continue;
|
|
}
|
|
|
|
//create the secret reference
|
|
const secretReference = await tx.secretReference.upsert({
|
|
where: {
|
|
key,
|
|
},
|
|
create: {
|
|
key,
|
|
provider: "DATABASE",
|
|
},
|
|
update: {},
|
|
});
|
|
|
|
if (existingValueRecord) {
|
|
await tx.environmentVariableValue.update({
|
|
where: {
|
|
id: existingValueRecord.id,
|
|
},
|
|
data: {
|
|
version: {
|
|
increment: 1,
|
|
},
|
|
...(options.lastUpdatedBy ? { lastUpdatedBy: options.lastUpdatedBy } : {}),
|
|
valueReferenceId: secretReference.id,
|
|
...(options.isSecret !== undefined
|
|
? {
|
|
isSecret: options.isSecret,
|
|
}
|
|
: {}),
|
|
},
|
|
});
|
|
} else {
|
|
await tx.environmentVariableValue.create({
|
|
data: {
|
|
variableId: environmentVariable.id,
|
|
environmentId: environmentId,
|
|
valueReferenceId: secretReference.id,
|
|
isSecret: effectiveIsSecret,
|
|
version: 1,
|
|
lastUpdatedBy: options.lastUpdatedBy ? options.lastUpdatedBy : Prisma.JsonNull,
|
|
},
|
|
});
|
|
}
|
|
|
|
await secretStore.setSecret<{ secret: string }>(key, {
|
|
secret: variable.value,
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
return {
|
|
success: true as const,
|
|
};
|
|
} catch (error) {
|
|
if (error instanceof Prisma.PrismaClientKnownRequestError) {
|
|
// The error code for unique constraint violation in Prisma is P2002
|
|
if (error.code === "P2002") {
|
|
return {
|
|
success: false as const,
|
|
error: `There was already an existing field`,
|
|
};
|
|
}
|
|
}
|
|
|
|
return {
|
|
success: false as const,
|
|
error: error instanceof Error ? error.message : "Something went wrong",
|
|
};
|
|
}
|
|
}
|
|
|
|
async edit(projectId: string, options: EditEnvironmentVariable): Promise<Result> {
|
|
const project = await this.prismaClient.project.findFirst({
|
|
where: {
|
|
id: projectId,
|
|
deletedAt: null,
|
|
},
|
|
select: {
|
|
environments: {
|
|
select: {
|
|
id: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!project) {
|
|
return { success: false as const, error: "Project not found" };
|
|
}
|
|
|
|
if (options.values.every((v) => !project.environments.some((e) => e.id === v.environmentId))) {
|
|
return { success: false as const, error: `Environment not found` };
|
|
}
|
|
|
|
//get rid of empty strings
|
|
let values = options.values.filter((v) => v.value.trim() !== "");
|
|
|
|
//add in empty values for environments that don't have a value
|
|
const environmentIds = project.environments.map((e) => e.id);
|
|
|
|
if (!options.keepEmptyValues) {
|
|
for (const environmentId of environmentIds) {
|
|
if (!values.some((v) => v.environmentId === environmentId)) {
|
|
values.push({
|
|
environmentId,
|
|
value: "",
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
const environmentVariable = await this.prismaClient.environmentVariable.findFirst({
|
|
select: {
|
|
id: true,
|
|
key: true,
|
|
},
|
|
where: {
|
|
id: options.id,
|
|
},
|
|
});
|
|
if (!environmentVariable) {
|
|
return { success: false as const, error: "Environment variable not found" };
|
|
}
|
|
|
|
try {
|
|
await $transaction(this.prismaClient, "edit env var", async (tx) => {
|
|
const secretStore = getSecretStore("DATABASE", {
|
|
prismaClient: tx,
|
|
});
|
|
|
|
//create the secret values and references
|
|
for (const value of values) {
|
|
const key = secretKey(projectId, value.environmentId, environmentVariable.key);
|
|
const existingValue = await tx.environmentVariableValue.findFirst({
|
|
where: {
|
|
variableId: environmentVariable.id,
|
|
environmentId: value.environmentId,
|
|
},
|
|
});
|
|
|
|
if (existingValue && existingValue.valueReferenceId) {
|
|
if (value.value === "") {
|
|
//delete the value
|
|
await secretStore.deleteSecret(key);
|
|
await tx.secretReference.delete({
|
|
where: {
|
|
id: existingValue.valueReferenceId,
|
|
},
|
|
});
|
|
await tx.environmentVariableValue.delete({
|
|
where: {
|
|
variableId_environmentId: {
|
|
variableId: environmentVariable.id,
|
|
environmentId: value.environmentId,
|
|
},
|
|
},
|
|
});
|
|
} else {
|
|
await secretStore.setSecret<{ secret: string }>(key, {
|
|
secret: value.value,
|
|
});
|
|
await tx.environmentVariableValue.update({
|
|
where: {
|
|
variableId_environmentId: {
|
|
variableId: environmentVariable.id,
|
|
environmentId: value.environmentId,
|
|
},
|
|
},
|
|
data: {
|
|
version: {
|
|
increment: 1,
|
|
},
|
|
lastUpdatedBy: options.lastUpdatedBy ? options.lastUpdatedBy : undefined,
|
|
},
|
|
});
|
|
}
|
|
continue;
|
|
}
|
|
|
|
//create the secret reference
|
|
const secretReference = await tx.secretReference.create({
|
|
data: {
|
|
key,
|
|
provider: "DATABASE",
|
|
},
|
|
});
|
|
|
|
const _variableValue = await tx.environmentVariableValue.create({
|
|
data: {
|
|
variableId: environmentVariable.id,
|
|
environmentId: value.environmentId,
|
|
valueReferenceId: secretReference.id,
|
|
version: 1,
|
|
lastUpdatedBy: options.lastUpdatedBy ? options.lastUpdatedBy : Prisma.JsonNull,
|
|
},
|
|
});
|
|
|
|
await secretStore.setSecret<{ secret: string }>(key, {
|
|
secret: value.value,
|
|
});
|
|
}
|
|
});
|
|
|
|
return {
|
|
success: true as const,
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false as const,
|
|
error: error instanceof Error ? error.message : "Something went wrong",
|
|
};
|
|
}
|
|
}
|
|
|
|
async editValue(projectId: string, options: EditEnvironmentVariableValue): Promise<Result> {
|
|
const project = await this.prismaClient.project.findFirst({
|
|
where: {
|
|
id: projectId,
|
|
deletedAt: null,
|
|
},
|
|
select: {
|
|
environments: {
|
|
select: {
|
|
id: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!project) {
|
|
return { success: false as const, error: "Project not found" };
|
|
}
|
|
|
|
if (!project.environments.some((e) => e.id === options.environmentId)) {
|
|
return { success: false as const, error: "Environment not found" };
|
|
}
|
|
|
|
const environmentVariable = await this.prismaClient.environmentVariable.findFirst({
|
|
select: {
|
|
id: true,
|
|
key: true,
|
|
values: {
|
|
where: {
|
|
environmentId: options.environmentId,
|
|
},
|
|
select: {
|
|
valueReferenceId: true,
|
|
},
|
|
},
|
|
},
|
|
where: {
|
|
id: options.id,
|
|
},
|
|
});
|
|
|
|
if (!environmentVariable) {
|
|
return { success: false as const, error: "Environment variable not found" };
|
|
}
|
|
|
|
if (environmentVariable.values.length === 0) {
|
|
return { success: false as const, error: "Environment variable value not found" };
|
|
}
|
|
|
|
try {
|
|
await $transaction(this.prismaClient, "edit env var value", async (tx) => {
|
|
const secretStore = getSecretStore("DATABASE", {
|
|
prismaClient: tx,
|
|
});
|
|
|
|
const key = secretKey(projectId, options.environmentId, environmentVariable.key);
|
|
await secretStore.setSecret<{ secret: string }>(key, {
|
|
secret: options.value,
|
|
});
|
|
|
|
await tx.environmentVariableValue.update({
|
|
where: {
|
|
variableId_environmentId: {
|
|
variableId: environmentVariable.id,
|
|
environmentId: options.environmentId,
|
|
},
|
|
},
|
|
data: {
|
|
version: {
|
|
increment: 1,
|
|
},
|
|
lastUpdatedBy: options.lastUpdatedBy ? options.lastUpdatedBy : undefined,
|
|
},
|
|
});
|
|
});
|
|
|
|
return {
|
|
success: true as const,
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false as const,
|
|
error: error instanceof Error ? error.message : "Something went wrong",
|
|
};
|
|
}
|
|
}
|
|
|
|
async getProject(projectId: string): Promise<ProjectEnvironmentVariable[]> {
|
|
const project = await this.prismaClient.project.findFirst({
|
|
where: {
|
|
id: projectId,
|
|
deletedAt: null,
|
|
},
|
|
select: {
|
|
environments: {
|
|
select: {
|
|
id: true,
|
|
type: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!project) {
|
|
return [];
|
|
}
|
|
|
|
const secretStore = getSecretStore("DATABASE", {
|
|
prismaClient: this.prismaClient,
|
|
});
|
|
|
|
const secrets = await secretStore.getSecrets(SecretValue, secretKeyProjectPrefix(projectId));
|
|
|
|
const values = secrets.map((secret) => {
|
|
const { projectId, environmentId, key } = parseSecretKey(secret.key);
|
|
return {
|
|
projectId,
|
|
environmentId,
|
|
key,
|
|
value: secret.value.secret,
|
|
};
|
|
});
|
|
|
|
//now group the values together by key and environment ID into ProjectEnvironmentVariable[]
|
|
//and add the type of environment to the result
|
|
const results: ProjectEnvironmentVariable[] = [];
|
|
for (const value of values) {
|
|
const environment = project.environments.find((e) => e.id === value.environmentId);
|
|
if (!environment) {
|
|
throw new Error("Environment not found");
|
|
}
|
|
|
|
const existing = results.find((r) => r.key === value.key);
|
|
if (existing) {
|
|
existing.values.push({
|
|
value: value.value,
|
|
environment: {
|
|
id: value.environmentId,
|
|
type: environment.type,
|
|
},
|
|
});
|
|
} else {
|
|
results.push({
|
|
key: value.key,
|
|
values: [
|
|
{
|
|
value: value.value,
|
|
environment: {
|
|
id: value.environmentId,
|
|
type: environment.type,
|
|
},
|
|
},
|
|
],
|
|
});
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
async getVariableValuesForKeys(
|
|
projectId: string,
|
|
items: Array<{ environmentId: string; key: string }>
|
|
): Promise<Map<string, string>> {
|
|
if (items.length === 0) {
|
|
return new Map();
|
|
}
|
|
|
|
const uniqueItems = new Map<string, { environmentId: string; key: string }>();
|
|
for (const item of items) {
|
|
uniqueItems.set(`${item.environmentId}:${item.key}`, item);
|
|
}
|
|
|
|
const secretStore = getSecretStore("DATABASE", {
|
|
prismaClient: this.replicaClient,
|
|
});
|
|
|
|
const storeKeys = Array.from(uniqueItems.values()).map((item) =>
|
|
secretKey(projectId, item.environmentId, item.key)
|
|
);
|
|
|
|
const secrets = await secretStore.getSecretsByKeys(SecretValue, storeKeys);
|
|
const secretsByStoreKey = new Map(secrets.map((secret) => [secret.key, secret.value.secret]));
|
|
|
|
const values = new Map<string, string>();
|
|
for (const item of uniqueItems.values()) {
|
|
const storeKey = secretKey(projectId, item.environmentId, item.key);
|
|
const value = secretsByStoreKey.get(storeKey);
|
|
if (value !== undefined) {
|
|
values.set(`${item.environmentId}:${item.key}`, value);
|
|
}
|
|
}
|
|
|
|
return values;
|
|
}
|
|
|
|
async getEnvironmentWithRedactedSecrets(
|
|
projectId: string,
|
|
environmentId: string,
|
|
parentEnvironmentId?: string
|
|
): Promise<EnvironmentVariableWithSecret[]> {
|
|
const variables = await this.getEnvironment(projectId, environmentId, parentEnvironmentId);
|
|
|
|
// Get the keys of all secret variables
|
|
const secretValues = await this.replicaClient.environmentVariableValue.findMany({
|
|
where: {
|
|
environmentId: parentEnvironmentId
|
|
? { in: [environmentId, parentEnvironmentId] }
|
|
: environmentId,
|
|
isSecret: true,
|
|
},
|
|
select: {
|
|
variable: {
|
|
select: {
|
|
key: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
const secretVarKeys = secretValues.map((r) => r.variable.key);
|
|
|
|
// Filter out secret variables
|
|
return variables.map((v) => {
|
|
if (secretVarKeys.includes(v.key)) {
|
|
return {
|
|
key: v.key,
|
|
value: "<redacted>",
|
|
isSecret: true,
|
|
};
|
|
}
|
|
|
|
return { key: v.key, value: v.value, isSecret: false };
|
|
});
|
|
}
|
|
|
|
async getEnvironment(
|
|
projectId: string,
|
|
environmentId: string,
|
|
parentEnvironmentId?: string
|
|
): Promise<EnvironmentVariable[]> {
|
|
const project = await this.prismaClient.project.findFirst({
|
|
where: {
|
|
id: projectId,
|
|
deletedAt: null,
|
|
},
|
|
select: {
|
|
environments: {
|
|
select: {
|
|
id: true,
|
|
},
|
|
where: {
|
|
id: environmentId,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!project || project.environments.length === 0) {
|
|
return [];
|
|
}
|
|
|
|
return this.getEnvironmentVariables(projectId, environmentId, parentEnvironmentId);
|
|
}
|
|
|
|
async #getSecretEnvironmentVariables(
|
|
projectId: string,
|
|
environmentId: string,
|
|
parentEnvironmentId?: string
|
|
): Promise<EnvironmentVariable[]> {
|
|
const secretStore = getSecretStore("DATABASE", {
|
|
prismaClient: this.prismaClient,
|
|
});
|
|
|
|
const parentSecrets = parentEnvironmentId
|
|
? await secretStore.getSecrets(
|
|
SecretValue,
|
|
secretKeyEnvironmentPrefix(projectId, parentEnvironmentId)
|
|
)
|
|
: [];
|
|
|
|
const childSecrets = await secretStore.getSecrets(
|
|
SecretValue,
|
|
secretKeyEnvironmentPrefix(projectId, environmentId)
|
|
);
|
|
|
|
// Merge the secrets, we want child ones to override parent ones
|
|
const mergedSecrets = new Map<string, string>();
|
|
for (const secret of parentSecrets) {
|
|
const { key: parsedKey } = parseSecretKey(secret.key);
|
|
mergedSecrets.set(parsedKey, secret.value.secret);
|
|
}
|
|
for (const secret of childSecrets) {
|
|
const { key: parsedKey } = parseSecretKey(secret.key);
|
|
mergedSecrets.set(parsedKey, secret.value.secret);
|
|
}
|
|
|
|
const merged = Array.from(mergedSecrets.entries()).map(([key, value]) => {
|
|
return {
|
|
key,
|
|
value,
|
|
};
|
|
});
|
|
|
|
return removeBlacklistedVariables(merged);
|
|
}
|
|
|
|
async getEnvironmentVariables(
|
|
projectId: string,
|
|
environmentId: string,
|
|
parentEnvironmentId?: string
|
|
): Promise<EnvironmentVariable[]> {
|
|
return this.#getSecretEnvironmentVariables(projectId, environmentId, parentEnvironmentId);
|
|
}
|
|
|
|
async delete(projectId: string, options: DeleteEnvironmentVariable): Promise<Result> {
|
|
const project = await this.prismaClient.project.findFirst({
|
|
where: {
|
|
id: projectId,
|
|
deletedAt: null,
|
|
},
|
|
select: {
|
|
environments: {
|
|
select: {
|
|
id: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!project) {
|
|
return { success: false as const, error: "Project not found" };
|
|
}
|
|
|
|
const environmentVariable = await this.prismaClient.environmentVariable.findFirst({
|
|
select: {
|
|
id: true,
|
|
key: true,
|
|
values: {
|
|
select: {
|
|
id: true,
|
|
environmentId: true,
|
|
valueReference: {
|
|
select: {
|
|
key: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
where: {
|
|
id: options.id,
|
|
},
|
|
});
|
|
if (!environmentVariable) {
|
|
return { success: false as const, error: "Environment variable not found" };
|
|
}
|
|
|
|
try {
|
|
await $transaction(this.prismaClient, "delete env var", async (tx) => {
|
|
await tx.environmentVariable.delete({
|
|
where: {
|
|
id: options.id,
|
|
},
|
|
});
|
|
|
|
const secretStore = getSecretStore("DATABASE", {
|
|
prismaClient: tx,
|
|
});
|
|
|
|
//delete the secret values and references
|
|
for (const value of environmentVariable.values) {
|
|
const key = secretKey(projectId, value.environmentId, environmentVariable.key);
|
|
await secretStore.deleteSecret(key);
|
|
|
|
if (value.valueReference) {
|
|
await tx.secretReference.delete({
|
|
where: {
|
|
key: value.valueReference.key,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
return {
|
|
success: true as const,
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false as const,
|
|
error: error instanceof Error ? error.message : "Something went wrong",
|
|
};
|
|
}
|
|
}
|
|
|
|
async deleteValue(projectId: string, options: DeleteEnvironmentVariableValue): Promise<Result> {
|
|
const project = await this.prismaClient.project.findFirst({
|
|
where: {
|
|
id: projectId,
|
|
deletedAt: null,
|
|
},
|
|
select: {
|
|
environments: {
|
|
select: {
|
|
id: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!project) {
|
|
return { success: false as const, error: "Project not found" };
|
|
}
|
|
|
|
const environmentVariable = await this.prismaClient.environmentVariable.findFirst({
|
|
select: {
|
|
id: true,
|
|
key: true,
|
|
values: {
|
|
select: {
|
|
id: true,
|
|
environmentId: true,
|
|
valueReference: {
|
|
select: {
|
|
key: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
where: {
|
|
id: options.id,
|
|
},
|
|
});
|
|
|
|
if (!environmentVariable) {
|
|
return { success: false as const, error: "Environment variable not found" };
|
|
}
|
|
|
|
const value = environmentVariable.values.find((v) => v.environmentId === options.environmentId);
|
|
|
|
if (!value) {
|
|
return { success: false as const, error: "Environment variable value not found" };
|
|
}
|
|
|
|
// If this is the last value, delete the whole variable
|
|
if (environmentVariable.values.length === 1) {
|
|
return this.delete(projectId, { id: options.id });
|
|
}
|
|
|
|
try {
|
|
await $transaction(this.prismaClient, "delete env var value", async (tx) => {
|
|
const secretStore = getSecretStore("DATABASE", {
|
|
prismaClient: tx,
|
|
});
|
|
|
|
const key = secretKey(projectId, options.environmentId, environmentVariable.key);
|
|
await secretStore.deleteSecret(key);
|
|
|
|
if (value.valueReference) {
|
|
await tx.secretReference.delete({
|
|
where: {
|
|
key: value.valueReference.key,
|
|
},
|
|
});
|
|
}
|
|
|
|
await tx.environmentVariableValue.delete({
|
|
where: {
|
|
id: value.id,
|
|
},
|
|
});
|
|
});
|
|
|
|
return {
|
|
success: true as const,
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
success: false as const,
|
|
error: error instanceof Error ? error.message : "Something went wrong",
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
export const RuntimeEnvironmentForEnvRepoPayload = {
|
|
select: {
|
|
id: true,
|
|
slug: true,
|
|
type: true,
|
|
projectId: true,
|
|
apiKey: true,
|
|
organizationId: true,
|
|
branchName: true,
|
|
builtInEnvironmentVariableOverrides: true,
|
|
},
|
|
} as const;
|
|
|
|
// Derived from the slim AuthenticatedEnvironment so a full AE satisfies
|
|
// this type — the legacy Prisma payload had `builtInEnvironmentVariableOverrides`
|
|
// as Prisma's JsonValue, which is a subtype of `unknown` in the slim
|
|
// shape, causing assignability errors in the JWT/queue paths that pass
|
|
// AE values straight through. Using Pick<AE, ...> aligns them.
|
|
export type RuntimeEnvironmentForEnvRepo = Pick<
|
|
AuthenticatedEnvironment,
|
|
| "id"
|
|
| "slug"
|
|
| "type"
|
|
| "projectId"
|
|
| "apiKey"
|
|
| "organizationId"
|
|
| "branchName"
|
|
| "builtInEnvironmentVariableOverrides"
|
|
>;
|
|
|
|
export const environmentVariablesRepository = new EnvironmentVariablesRepository();
|
|
|
|
export async function resolveVariablesForEnvironment(
|
|
runtimeEnvironment: RuntimeEnvironmentForEnvRepo,
|
|
parentEnvironment?: RuntimeEnvironmentForEnvRepo
|
|
) {
|
|
let projectSecrets = await environmentVariablesRepository.getEnvironmentVariables(
|
|
runtimeEnvironment.projectId,
|
|
runtimeEnvironment.id,
|
|
parentEnvironment?.id
|
|
);
|
|
|
|
projectSecrets = renameVariables(projectSecrets, {
|
|
OTEL_RESOURCE_ATTRIBUTES: "CUSTOM_OTEL_RESOURCE_ATTRIBUTES",
|
|
});
|
|
|
|
const overridableTriggerVariables = await resolveOverridableTriggerVariables(runtimeEnvironment);
|
|
|
|
const builtInVariables =
|
|
runtimeEnvironment.type === "DEVELOPMENT"
|
|
? await resolveBuiltInDevVariables(runtimeEnvironment)
|
|
: await resolveBuiltInProdVariables(runtimeEnvironment, parentEnvironment);
|
|
|
|
const overridableOtelVariables =
|
|
runtimeEnvironment.type === "DEVELOPMENT"
|
|
? await resolveOverridableOtelDevVariables(runtimeEnvironment)
|
|
: [];
|
|
|
|
const result = deduplicateVariableArray([
|
|
...overridableTriggerVariables,
|
|
...overridableOtelVariables,
|
|
...projectSecrets,
|
|
...builtInVariables,
|
|
]);
|
|
|
|
return result;
|
|
}
|
|
|
|
function renameVariables(variables: EnvironmentVariable[], renameMap: Record<string, string>) {
|
|
return variables.map((variable) => {
|
|
return {
|
|
...variable,
|
|
key: renameMap[variable.key] ?? variable.key,
|
|
};
|
|
});
|
|
}
|
|
|
|
async function resolveOverridableTriggerVariables(
|
|
runtimeEnvironment: RuntimeEnvironmentForEnvRepo
|
|
) {
|
|
let result: Array<EnvironmentVariable> = [
|
|
{
|
|
key: "TRIGGER_REALTIME_STREAM_VERSION",
|
|
value: env.REALTIME_STREAM_VERSION,
|
|
},
|
|
];
|
|
|
|
return result;
|
|
}
|
|
|
|
async function resolveBuiltInDevVariables(runtimeEnvironment: RuntimeEnvironmentForEnvRepo) {
|
|
let result: Array<EnvironmentVariable> = [
|
|
{
|
|
key: "TRIGGER_OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
value: env.DEV_OTEL_EXPORTER_OTLP_ENDPOINT ?? `${env.APP_ORIGIN.replace(/\/$/, "")}/otel`,
|
|
},
|
|
{
|
|
key: "TRIGGER_API_URL",
|
|
value: env.API_ORIGIN ?? env.APP_ORIGIN,
|
|
},
|
|
{
|
|
key: "TRIGGER_STREAM_URL",
|
|
value: env.STREAM_ORIGIN ?? env.API_ORIGIN ?? env.APP_ORIGIN,
|
|
},
|
|
];
|
|
|
|
if (env.DEV_OTEL_METRICS_ENDPOINT) {
|
|
result.push({
|
|
key: "TRIGGER_OTEL_METRICS_ENDPOINT",
|
|
value: env.DEV_OTEL_METRICS_ENDPOINT,
|
|
});
|
|
}
|
|
|
|
if (env.DEV_OTEL_METRICS_EXPORT_INTERVAL_MILLIS) {
|
|
result.push({
|
|
key: "TRIGGER_OTEL_METRICS_EXPORT_INTERVAL_MILLIS",
|
|
value: env.DEV_OTEL_METRICS_EXPORT_INTERVAL_MILLIS,
|
|
});
|
|
}
|
|
|
|
if (env.DEV_OTEL_METRICS_EXPORT_TIMEOUT_MILLIS) {
|
|
result.push({
|
|
key: "TRIGGER_OTEL_METRICS_EXPORT_TIMEOUT_MILLIS",
|
|
value: env.DEV_OTEL_METRICS_EXPORT_TIMEOUT_MILLIS,
|
|
});
|
|
}
|
|
|
|
if (env.DEV_OTEL_METRICS_COLLECTION_INTERVAL_MILLIS) {
|
|
result.push({
|
|
key: "TRIGGER_OTEL_METRICS_COLLECTION_INTERVAL_MILLIS",
|
|
value: env.DEV_OTEL_METRICS_COLLECTION_INTERVAL_MILLIS,
|
|
});
|
|
}
|
|
|
|
if (env.DEV_OTEL_BATCH_PROCESSING_ENABLED === "1") {
|
|
result = result.concat([
|
|
{
|
|
key: "TRIGGER_OTEL_BATCH_PROCESSING_ENABLED",
|
|
value: "1",
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_SPAN_MAX_EXPORT_BATCH_SIZE",
|
|
value: env.DEV_OTEL_SPAN_MAX_EXPORT_BATCH_SIZE,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_SPAN_SCHEDULED_DELAY_MILLIS",
|
|
value: env.DEV_OTEL_SPAN_SCHEDULED_DELAY_MILLIS,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_SPAN_EXPORT_TIMEOUT_MILLIS",
|
|
value: env.DEV_OTEL_SPAN_EXPORT_TIMEOUT_MILLIS,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_SPAN_MAX_QUEUE_SIZE",
|
|
value: env.DEV_OTEL_SPAN_MAX_QUEUE_SIZE,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_LOG_MAX_EXPORT_BATCH_SIZE",
|
|
value: env.DEV_OTEL_LOG_MAX_EXPORT_BATCH_SIZE,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_LOG_SCHEDULED_DELAY_MILLIS",
|
|
value: env.DEV_OTEL_LOG_SCHEDULED_DELAY_MILLIS,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_LOG_EXPORT_TIMEOUT_MILLIS",
|
|
value: env.DEV_OTEL_LOG_EXPORT_TIMEOUT_MILLIS,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_LOG_MAX_QUEUE_SIZE",
|
|
value: env.DEV_OTEL_LOG_MAX_QUEUE_SIZE,
|
|
},
|
|
{
|
|
key: "OTEL_BATCH_PROCESSING_ENABLED",
|
|
value: "1",
|
|
},
|
|
{
|
|
key: "OTEL_SPAN_MAX_EXPORT_BATCH_SIZE",
|
|
value: env.DEV_OTEL_SPAN_MAX_EXPORT_BATCH_SIZE,
|
|
},
|
|
{
|
|
key: "OTEL_SPAN_SCHEDULED_DELAY_MILLIS",
|
|
value: env.DEV_OTEL_SPAN_SCHEDULED_DELAY_MILLIS,
|
|
},
|
|
{
|
|
key: "OTEL_SPAN_EXPORT_TIMEOUT_MILLIS",
|
|
value: env.DEV_OTEL_SPAN_EXPORT_TIMEOUT_MILLIS,
|
|
},
|
|
{
|
|
key: "OTEL_SPAN_MAX_QUEUE_SIZE",
|
|
value: env.DEV_OTEL_SPAN_MAX_QUEUE_SIZE,
|
|
},
|
|
{
|
|
key: "OTEL_LOG_MAX_EXPORT_BATCH_SIZE",
|
|
value: env.DEV_OTEL_LOG_MAX_EXPORT_BATCH_SIZE,
|
|
},
|
|
{
|
|
key: "OTEL_LOG_SCHEDULED_DELAY_MILLIS",
|
|
value: env.DEV_OTEL_LOG_SCHEDULED_DELAY_MILLIS,
|
|
},
|
|
{
|
|
key: "OTEL_LOG_EXPORT_TIMEOUT_MILLIS",
|
|
value: env.DEV_OTEL_LOG_EXPORT_TIMEOUT_MILLIS,
|
|
},
|
|
{
|
|
key: "OTEL_LOG_MAX_QUEUE_SIZE",
|
|
value: env.DEV_OTEL_LOG_MAX_QUEUE_SIZE,
|
|
},
|
|
]);
|
|
}
|
|
|
|
// Dev branches set branchName too, so carry it to the task via the same
|
|
// TRIGGER_PREVIEW_BRANCH var the prod path uses.
|
|
if (runtimeEnvironment.branchName) {
|
|
result = result.concat([
|
|
{
|
|
key: "TRIGGER_PREVIEW_BRANCH",
|
|
value: runtimeEnvironment.branchName,
|
|
},
|
|
]);
|
|
}
|
|
|
|
const commonVariables = await resolveCommonBuiltInVariables(runtimeEnvironment);
|
|
|
|
return [...result, ...commonVariables];
|
|
}
|
|
|
|
async function resolveOverridableOtelDevVariables(
|
|
runtimeEnvironment: RuntimeEnvironmentForEnvRepo
|
|
) {
|
|
let result: Array<EnvironmentVariable> = [
|
|
{
|
|
key: "OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
value: env.DEV_OTEL_EXPORTER_OTLP_ENDPOINT ?? `${env.APP_ORIGIN.replace(/\/$/, "")}/otel`,
|
|
},
|
|
];
|
|
|
|
return result;
|
|
}
|
|
|
|
async function resolveBuiltInProdVariables(
|
|
runtimeEnvironment: RuntimeEnvironmentForEnvRepo,
|
|
parentEnvironment?: RuntimeEnvironmentForEnvRepo
|
|
) {
|
|
let result: Array<EnvironmentVariable> = [
|
|
{
|
|
key: "TRIGGER_SECRET_KEY",
|
|
value: parentEnvironment?.apiKey ?? runtimeEnvironment.apiKey,
|
|
},
|
|
{
|
|
key: "TRIGGER_API_URL",
|
|
value: env.API_ORIGIN ?? env.APP_ORIGIN,
|
|
},
|
|
{
|
|
key: "TRIGGER_STREAM_URL",
|
|
value: env.STREAM_ORIGIN ?? env.API_ORIGIN ?? env.APP_ORIGIN,
|
|
},
|
|
{
|
|
key: "TRIGGER_RUNTIME_WAIT_THRESHOLD_IN_MS",
|
|
value: String(env.CHECKPOINT_THRESHOLD_IN_MS),
|
|
},
|
|
{
|
|
key: "TRIGGER_ORG_ID",
|
|
value: runtimeEnvironment.organizationId,
|
|
},
|
|
];
|
|
|
|
if (runtimeEnvironment.branchName) {
|
|
result = result.concat([
|
|
{
|
|
key: "TRIGGER_PREVIEW_BRANCH",
|
|
value: runtimeEnvironment.branchName,
|
|
},
|
|
]);
|
|
}
|
|
|
|
if (env.PROD_OTEL_METRICS_EXPORT_INTERVAL_MILLIS) {
|
|
result.push({
|
|
key: "TRIGGER_OTEL_METRICS_EXPORT_INTERVAL_MILLIS",
|
|
value: env.PROD_OTEL_METRICS_EXPORT_INTERVAL_MILLIS,
|
|
});
|
|
}
|
|
|
|
if (env.PROD_OTEL_METRICS_EXPORT_TIMEOUT_MILLIS) {
|
|
result.push({
|
|
key: "TRIGGER_OTEL_METRICS_EXPORT_TIMEOUT_MILLIS",
|
|
value: env.PROD_OTEL_METRICS_EXPORT_TIMEOUT_MILLIS,
|
|
});
|
|
}
|
|
|
|
if (env.PROD_OTEL_METRICS_COLLECTION_INTERVAL_MILLIS) {
|
|
result.push({
|
|
key: "TRIGGER_OTEL_METRICS_COLLECTION_INTERVAL_MILLIS",
|
|
value: env.PROD_OTEL_METRICS_COLLECTION_INTERVAL_MILLIS,
|
|
});
|
|
}
|
|
|
|
if (env.PROD_OTEL_BATCH_PROCESSING_ENABLED === "1") {
|
|
result = result.concat([
|
|
{
|
|
key: "TRIGGER_OTEL_BATCH_PROCESSING_ENABLED",
|
|
value: "1",
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_SPAN_MAX_EXPORT_BATCH_SIZE",
|
|
value: env.PROD_OTEL_SPAN_MAX_EXPORT_BATCH_SIZE,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_SPAN_SCHEDULED_DELAY_MILLIS",
|
|
value: env.PROD_OTEL_SPAN_SCHEDULED_DELAY_MILLIS,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_SPAN_EXPORT_TIMEOUT_MILLIS",
|
|
value: env.PROD_OTEL_SPAN_EXPORT_TIMEOUT_MILLIS,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_SPAN_MAX_QUEUE_SIZE",
|
|
value: env.PROD_OTEL_SPAN_MAX_QUEUE_SIZE,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_LOG_MAX_EXPORT_BATCH_SIZE",
|
|
value: env.PROD_OTEL_LOG_MAX_EXPORT_BATCH_SIZE,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_LOG_SCHEDULED_DELAY_MILLIS",
|
|
value: env.PROD_OTEL_LOG_SCHEDULED_DELAY_MILLIS,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_LOG_EXPORT_TIMEOUT_MILLIS",
|
|
value: env.PROD_OTEL_LOG_EXPORT_TIMEOUT_MILLIS,
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_LOG_MAX_QUEUE_SIZE",
|
|
value: env.PROD_OTEL_LOG_MAX_QUEUE_SIZE,
|
|
},
|
|
{
|
|
key: "OTEL_BATCH_PROCESSING_ENABLED",
|
|
value: "1",
|
|
},
|
|
{
|
|
key: "OTEL_SPAN_MAX_EXPORT_BATCH_SIZE",
|
|
value: env.PROD_OTEL_SPAN_MAX_EXPORT_BATCH_SIZE,
|
|
},
|
|
{
|
|
key: "OTEL_SPAN_SCHEDULED_DELAY_MILLIS",
|
|
value: env.PROD_OTEL_SPAN_SCHEDULED_DELAY_MILLIS,
|
|
},
|
|
{
|
|
key: "OTEL_SPAN_EXPORT_TIMEOUT_MILLIS",
|
|
value: env.PROD_OTEL_SPAN_EXPORT_TIMEOUT_MILLIS,
|
|
},
|
|
{
|
|
key: "OTEL_SPAN_MAX_QUEUE_SIZE",
|
|
value: env.PROD_OTEL_SPAN_MAX_QUEUE_SIZE,
|
|
},
|
|
{
|
|
key: "OTEL_LOG_MAX_EXPORT_BATCH_SIZE",
|
|
value: env.PROD_OTEL_LOG_MAX_EXPORT_BATCH_SIZE,
|
|
},
|
|
{
|
|
key: "OTEL_LOG_SCHEDULED_DELAY_MILLIS",
|
|
value: env.PROD_OTEL_LOG_SCHEDULED_DELAY_MILLIS,
|
|
},
|
|
{
|
|
key: "OTEL_LOG_EXPORT_TIMEOUT_MILLIS",
|
|
value: env.PROD_OTEL_LOG_EXPORT_TIMEOUT_MILLIS,
|
|
},
|
|
{
|
|
key: "OTEL_LOG_MAX_QUEUE_SIZE",
|
|
value: env.PROD_OTEL_LOG_MAX_QUEUE_SIZE,
|
|
},
|
|
]);
|
|
}
|
|
|
|
if (env.PROD_USAGE_HEARTBEAT_INTERVAL_MS && env.USAGE_EVENT_URL) {
|
|
result = result.concat([
|
|
{
|
|
key: "USAGE_HEARTBEAT_INTERVAL_MS",
|
|
value: String(env.PROD_USAGE_HEARTBEAT_INTERVAL_MS),
|
|
},
|
|
{
|
|
key: "USAGE_EVENT_URL",
|
|
value: env.USAGE_EVENT_URL,
|
|
},
|
|
]);
|
|
}
|
|
|
|
if (env.PROD_TASK_HEARTBEAT_INTERVAL_MS) {
|
|
result = result.concat([
|
|
{
|
|
key: "HEARTBEAT_INTERVAL_MS",
|
|
value: String(env.PROD_TASK_HEARTBEAT_INTERVAL_MS),
|
|
},
|
|
]);
|
|
}
|
|
|
|
const commonVariables = await resolveCommonBuiltInVariables(runtimeEnvironment);
|
|
|
|
return [...result, ...commonVariables];
|
|
}
|
|
|
|
async function resolveCommonBuiltInVariables(
|
|
runtimeEnvironment: RuntimeEnvironmentForEnvRepo
|
|
): Promise<Array<EnvironmentVariable>> {
|
|
return [
|
|
{
|
|
key: "TRIGGER_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT",
|
|
value: resolveBuiltInEnvironmentVariableOverrides(
|
|
"TRIGGER_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT",
|
|
runtimeEnvironment,
|
|
String(env.TRIGGER_OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT)
|
|
),
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_LOG_ATTRIBUTE_COUNT_LIMIT",
|
|
value: resolveBuiltInEnvironmentVariableOverrides(
|
|
"TRIGGER_OTEL_LOG_ATTRIBUTE_COUNT_LIMIT",
|
|
runtimeEnvironment,
|
|
String(env.TRIGGER_OTEL_LOG_ATTRIBUTE_COUNT_LIMIT)
|
|
),
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT",
|
|
value: resolveBuiltInEnvironmentVariableOverrides(
|
|
"TRIGGER_OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT",
|
|
runtimeEnvironment,
|
|
String(env.TRIGGER_OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT)
|
|
),
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_LOG_ATTRIBUTE_VALUE_LENGTH_LIMIT",
|
|
value: resolveBuiltInEnvironmentVariableOverrides(
|
|
"TRIGGER_OTEL_LOG_ATTRIBUTE_VALUE_LENGTH_LIMIT",
|
|
runtimeEnvironment,
|
|
String(env.TRIGGER_OTEL_LOG_ATTRIBUTE_VALUE_LENGTH_LIMIT)
|
|
),
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_SPAN_EVENT_COUNT_LIMIT",
|
|
value: resolveBuiltInEnvironmentVariableOverrides(
|
|
"TRIGGER_OTEL_SPAN_EVENT_COUNT_LIMIT",
|
|
runtimeEnvironment,
|
|
String(env.TRIGGER_OTEL_SPAN_EVENT_COUNT_LIMIT)
|
|
),
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_LINK_COUNT_LIMIT",
|
|
value: resolveBuiltInEnvironmentVariableOverrides(
|
|
"TRIGGER_OTEL_LINK_COUNT_LIMIT",
|
|
runtimeEnvironment,
|
|
String(env.TRIGGER_OTEL_LINK_COUNT_LIMIT)
|
|
),
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_ATTRIBUTE_PER_LINK_COUNT_LIMIT",
|
|
value: resolveBuiltInEnvironmentVariableOverrides(
|
|
"TRIGGER_OTEL_ATTRIBUTE_PER_LINK_COUNT_LIMIT",
|
|
runtimeEnvironment,
|
|
String(env.TRIGGER_OTEL_ATTRIBUTE_PER_LINK_COUNT_LIMIT)
|
|
),
|
|
},
|
|
{
|
|
key: "TRIGGER_OTEL_ATTRIBUTE_PER_EVENT_COUNT_LIMIT",
|
|
value: resolveBuiltInEnvironmentVariableOverrides(
|
|
"TRIGGER_OTEL_ATTRIBUTE_PER_EVENT_COUNT_LIMIT",
|
|
runtimeEnvironment,
|
|
String(env.TRIGGER_OTEL_ATTRIBUTE_PER_EVENT_COUNT_LIMIT)
|
|
),
|
|
},
|
|
{
|
|
key: "TRIGGER_WAIT_UNTIL_TIMEOUT_MS",
|
|
value: resolveBuiltInEnvironmentVariableOverrides(
|
|
"TRIGGER_WAIT_UNTIL_TIMEOUT_MS",
|
|
runtimeEnvironment,
|
|
String(env.WAIT_UNTIL_TIMEOUT_MS)
|
|
),
|
|
},
|
|
];
|
|
}
|
|
|
|
function resolveBuiltInEnvironmentVariableOverrides(
|
|
key: string,
|
|
runtimeEnvironment: RuntimeEnvironmentForEnvRepo,
|
|
defaultValue: string
|
|
) {
|
|
const overrides = runtimeEnvironment.builtInEnvironmentVariableOverrides;
|
|
|
|
if (!overrides) {
|
|
return defaultValue;
|
|
}
|
|
|
|
if (
|
|
!Array.isArray(overrides) &&
|
|
typeof overrides === "object" &&
|
|
overrides !== null &&
|
|
key in overrides
|
|
) {
|
|
const value = (overrides as Record<string, unknown>)[key];
|
|
if (typeof value === "string") {
|
|
return value;
|
|
}
|
|
}
|
|
|
|
return defaultValue;
|
|
}
|