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
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
Query,
UseInterceptors,
} from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
import { RequirePermissions } from '@novu/application-generic';
import { ApiRateLimitCategoryEnum, PermissionsEnum, UserSessionData } from '@novu/shared';
import { RequireAuthentication } from '../auth/framework/auth.decorator';
Expand Down Expand Up @@ -88,10 +88,16 @@ export class EnvironmentVariablesController {
);
}

@Get('/:variableId/usage')
@Get('/:variableKey/usage')
@ExternalApiAccessible()
@RequirePermissions(PermissionsEnum.WORKFLOW_READ)
@SdkMethodName('usage')
@ApiParam({
name: 'variableKey',
description: 'The unique key of the environment variable (e.g. BASE_URL)',
type: String,
example: 'BASE_URL',
})
@ApiResponse(GetEnvironmentVariableUsageResponseDto)
@ApiOperation({
summary: 'Retrieve a variable usage',
Expand All @@ -101,36 +107,42 @@ export class EnvironmentVariablesController {
@ApiNotFoundResponse({ description: 'Environment variable not found.' })
async getEnvironmentVariableUsage(
@UserSession() user: UserSessionData,
@Param('variableId') variableId: string
@Param('variableKey') variableKey: string
): Promise<GetEnvironmentVariableUsageResponseDto> {
return this.getEnvironmentVariableUsageUsecase.execute(
GetEnvironmentVariableUsageCommand.create({
organizationId: user.organizationId,
userId: user._id,
variableId,
variableKey,
})
);
}

@Get('/:variableId')
@Get('/:variableKey')
@ExternalApiAccessible()
@RequirePermissions(PermissionsEnum.WORKFLOW_READ)
@SdkMethodName('retrieve')
@ApiParam({
name: 'variableKey',
description: 'The unique key of the environment variable (e.g. BASE_URL)',
type: String,
example: 'BASE_URL',
})
@ApiResponse(EnvironmentVariableResponseDto)
@ApiOperation({
summary: 'Retrieve a variable',
description: 'Returns a single environment variable by id. Secret values are masked.',
summary: 'Get environment variable',
description: 'Returns a single environment variable by key. Secret values are masked.',
})
@ApiNotFoundResponse({ description: 'Environment variable not found.' })
async getEnvironmentVariable(
@UserSession() user: UserSessionData,
@Param('variableId') variableId: string
@Param('variableKey') variableKey: string
): Promise<EnvironmentVariableResponseDto> {
return this.getEnvironmentVariableUsecase.execute(
GetEnvironmentVariableCommand.create({
organizationId: user.organizationId,
userId: user._id,
variableId,
variableKey,
})
);
}
Expand Down Expand Up @@ -162,9 +174,15 @@ export class EnvironmentVariablesController {
);
}

@Patch('/:variableId')
@Patch('/:variableKey')
@ExternalApiAccessible()
@RequirePermissions(PermissionsEnum.WORKFLOW_WRITE)
@ApiParam({
name: 'variableKey',
description: 'The unique key of the environment variable (e.g. BASE_URL)',
type: String,
example: 'BASE_URL',
})
@ApiResponse(EnvironmentVariableResponseDto)
@ApiOperation({
summary: 'Update a variable',
Expand All @@ -174,14 +192,14 @@ export class EnvironmentVariablesController {
@ApiNotFoundResponse({ description: 'Environment variable not found.' })
async updateEnvironmentVariable(
@UserSession() user: UserSessionData,
@Param('variableId') variableId: string,
@Param('variableKey') variableKey: string,
@Body() body: UpdateEnvironmentVariableRequestDto
): Promise<EnvironmentVariableResponseDto> {
return this.updateEnvironmentVariableUsecase.execute(
UpdateEnvironmentVariableCommand.create({
organizationId: user.organizationId,
userId: user._id,
variableId,
variableKey,
key: body.key,
type: body.type,
isSecret: body.isSecret,
Expand All @@ -190,25 +208,31 @@ export class EnvironmentVariablesController {
);
}

@Delete('/:variableId')
@Delete('/:variableKey')
@ExternalApiAccessible()
@RequirePermissions(PermissionsEnum.WORKFLOW_WRITE)
@ApiParam({
name: 'variableKey',
description: 'The unique key of the environment variable (e.g. BASE_URL)',
type: String,
example: 'BASE_URL',
})
@ApiOperation({
summary: 'Delete a variable',
description: 'Deletes an environment variable by id.',
summary: 'Delete environment variable',
description: 'Deletes an environment variable by key.',
})
@ApiNoContentResponse({ description: 'The environment variable has been deleted.' })
@ApiNotFoundResponse({ description: 'Environment variable not found.' })
@HttpCode(HttpStatus.NO_CONTENT)
async deleteEnvironmentVariable(
@UserSession() user: UserSessionData,
@Param('variableId') variableId: string
@Param('variableKey') variableKey: string
): Promise<void> {
return this.deleteEnvironmentVariableUsecase.execute(
DeleteEnvironmentVariableCommand.create({
organizationId: user.organizationId,
userId: user._id,
variableId,
variableKey,
})
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import { IsNotEmpty, IsString } from 'class-validator';
export class DeleteEnvironmentVariableCommand extends OrganizationLevelWithUserCommand {
@IsString()
@IsNotEmpty()
variableId: string;
variableKey: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ export class DeleteEnvironmentVariable {
constructor(private environmentVariableRepository: EnvironmentVariableRepository) {}

async execute(command: DeleteEnvironmentVariableCommand): Promise<void> {
const existing = await this.environmentVariableRepository.findById(
{ _id: command.variableId, _organizationId: command.organizationId },
const existing = await this.environmentVariableRepository.findOne(
{ key: command.variableKey, _organizationId: command.organizationId },
['_id']
);

if (!existing) {
throw new NotFoundException(`Environment variable with id ${command.variableId} not found`);
throw new NotFoundException(`Environment variable with key "${command.variableKey}" not found`);
}

await this.environmentVariableRepository.delete({
_id: command.variableId,
_id: existing._id,
_organizationId: command.organizationId,
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import { IsNotEmpty, IsString } from 'class-validator';
export class GetEnvironmentVariableUsageCommand extends OrganizationLevelWithUserCommand {
@IsString()
@IsNotEmpty()
variableId: string;
variableKey: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ export class GetEnvironmentVariableUsage {

@InstrumentUsecase()
async execute(command: GetEnvironmentVariableUsageCommand): Promise<GetEnvironmentVariableUsageResponseDto> {
const variable = await this.environmentVariableRepository.findById(
{ _id: command.variableId, _organizationId: command.organizationId },
const variable = await this.environmentVariableRepository.findOne(
{ key: command.variableKey, _organizationId: command.organizationId },
['key']
);

if (!variable) {
throw new NotFoundException(`Environment variable with id ${command.variableId} not found`);
throw new NotFoundException(`Environment variable with key "${command.variableKey}" not found`);
}

const envVarPattern = `env.${variable.key}`;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ import { IsNotEmpty, IsString } from 'class-validator';
export class GetEnvironmentVariableCommand extends OrganizationLevelWithUserCommand {
@IsString()
@IsNotEmpty()
variableId: string;
variableKey: string;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ export class GetEnvironmentVariable {
constructor(private environmentVariableRepository: EnvironmentVariableRepository) {}

async execute(command: GetEnvironmentVariableCommand): Promise<EnvironmentVariableResponseDto> {
const variable = await this.environmentVariableRepository.findById(
{ _id: command.variableId, _organizationId: command.organizationId },
const variable = await this.environmentVariableRepository.findOne(
{ key: command.variableKey, _organizationId: command.organizationId },
'*'
);

if (!variable) {
throw new NotFoundException(`Environment variable with id ${command.variableId} not found`);
throw new NotFoundException(`Environment variable with key "${command.variableKey}" not found`);
}

return toEnvironmentVariableResponseDto(variable);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { EnvironmentVariableValueCommand } from '../create-environment-variable/
export class UpdateEnvironmentVariableCommand extends OrganizationLevelWithUserCommand {
@IsString()
@IsNotEmpty()
variableId: string;
variableKey: string;

@IsString()
@Matches(/^[A-Za-z][A-Za-z0-9_]*$/)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ export class UpdateEnvironmentVariable {
constructor(private environmentVariableRepository: EnvironmentVariableRepository) {}

async execute(command: UpdateEnvironmentVariableCommand): Promise<EnvironmentVariableResponseDto> {
const existing = await this.environmentVariableRepository.findById(
{ _id: command.variableId, _organizationId: command.organizationId },
const existing = await this.environmentVariableRepository.findOne(
{ key: command.variableKey, _organizationId: command.organizationId },
['_id']
);

if (!existing) {
throw new NotFoundException(`Environment variable with id ${command.variableId} not found`);
throw new NotFoundException(`Environment variable with key "${command.variableKey}" not found`);
}

const updateBody: Record<string, unknown> = {};
Expand Down Expand Up @@ -45,17 +45,18 @@ export class UpdateEnvironmentVariable {
updateBody._updatedBy = command.userId;

await this.environmentVariableRepository.update(
{ _id: command.variableId, _organizationId: command.organizationId },
{ _id: existing._id, _organizationId: command.organizationId },
{ $set: updateBody }
);

const updated = await this.environmentVariableRepository.findById(
{ _id: command.variableId, _organizationId: command.organizationId },
const updatedKey = command.key ?? command.variableKey;
const updated = await this.environmentVariableRepository.findOne(
{ key: updatedKey, _organizationId: command.organizationId },
'*'
);

if (!updated) {
throw new NotFoundException(`Environment variable with id ${command.variableId} not found`);
throw new NotFoundException(`Environment variable with key "${updatedKey}" not found`);
}

return toEnvironmentVariableResponseDto(updated);
Expand Down
20 changes: 14 additions & 6 deletions apps/dashboard/src/api/environment-variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ export const getEnvironmentVariables = async ({
return data;
};

export const getEnvironmentVariable = async (variableKey: string): Promise<EnvironmentVariableResponseDto> => {
const { data } = await get<{ data: EnvironmentVariableResponseDto }>(`/environment-variables/${variableKey}`);

return data;
};

export const createEnvironmentVariable = async (
body: CreateEnvironmentVariableDto
): Promise<EnvironmentVariableResponseDto> => {
Expand All @@ -61,27 +67,29 @@ export const createEnvironmentVariable = async (
};

export const updateEnvironmentVariable = async (
variableId: string,
variableKey: string,
body: UpdateEnvironmentVariableDto
): Promise<EnvironmentVariableResponseDto> => {
const { data } = await patch<{ data: EnvironmentVariableResponseDto }>(`/environment-variables/${variableId}`, {
const { data } = await patch<{ data: EnvironmentVariableResponseDto }>(`/environment-variables/${variableKey}`, {
body,
});

return data;
};

export const deleteEnvironmentVariable = async (variableId: string): Promise<void> => {
await del(`/environment-variables/${variableId}`);
export const deleteEnvironmentVariable = async (variableKey: string): Promise<void> => {
await del(`/environment-variables/${variableKey}`);
};

export type GetEnvironmentVariableUsageResponse = {
workflows: { name: string; workflowId: string }[];
};

export const getEnvironmentVariableUsage = async (variableId: string): Promise<GetEnvironmentVariableUsageResponse> => {
export const getEnvironmentVariableUsage = async (
variableKey: string
): Promise<GetEnvironmentVariableUsageResponse> => {
const { data } = await get<{ data: GetEnvironmentVariableUsageResponse }>(
`/environment-variables/${variableId}/usage`
`/environment-variables/${variableKey}/usage`
);

return data;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const DeleteVariableDialog = ({
isLoading,
}: DeleteVariableDialogProps) => {
const { usage, isPending: isUsagePending } = useFetchEnvironmentVariableUsage({
variableId: variable._id,
variableKey: variable.key,
enabled: open,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export const UpsertVariableForm = ({
try {
if (isEditing) {
await updateEnvironmentVariable({
variableId: variable._id,
variableKey: variable.key,
key: data.key.trim(),
values,
});
Expand Down
2 changes: 1 addition & 1 deletion apps/dashboard/src/components/variables/variable-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ export const VariableRow = ({
const totalCount = environments.length;

const handleDelete = async () => {
await deleteEnvironmentVariable({ variableId: variable._id });
await deleteEnvironmentVariable({ variableKey: variable.key });
setIsDeleteModalOpen(false);
};

Expand Down
6 changes: 3 additions & 3 deletions apps/dashboard/src/hooks/use-delete-environment-variable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { deleteEnvironmentVariable } from '@/api/environment-variables';
import { QueryKeys } from '@/utils/query-keys';

type DeleteEnvironmentVariableArgs = {
variableId: string;
variableKey: string;
};

export const useDeleteEnvironmentVariable = (
Expand All @@ -12,10 +12,10 @@ export const useDeleteEnvironmentVariable = (
const queryClient = useQueryClient();

const { mutateAsync, ...rest } = useMutation({
mutationFn: ({ variableId }: DeleteEnvironmentVariableArgs) => deleteEnvironmentVariable(variableId),
mutationFn: ({ variableKey }: DeleteEnvironmentVariableArgs) => deleteEnvironmentVariable(variableKey),
...options,
onSuccess: async (_, variables, ctx) => {
queryClient.removeQueries({ queryKey: [QueryKeys.fetchEnvironmentVariable, variables.variableId], exact: true });
queryClient.removeQueries({ queryKey: [QueryKeys.fetchEnvironmentVariable, variables.variableKey], exact: true });
queryClient.invalidateQueries({
queryKey: [QueryKeys.fetchEnvironmentVariables],
exact: false,
Expand Down
10 changes: 5 additions & 5 deletions apps/dashboard/src/hooks/use-fetch-environment-variable-usage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ import { GetEnvironmentVariableUsageResponse, getEnvironmentVariableUsage } from
import { QueryKeys } from '@/utils/query-keys';

export const useFetchEnvironmentVariableUsage = ({
variableId,
variableKey,
enabled = true,
}: {
variableId: string;
variableKey: string;
enabled?: boolean;
}) => {
const {
data: usage,
isPending,
error,
} = useQuery<GetEnvironmentVariableUsageResponse>({
queryKey: [QueryKeys.fetchEnvironmentVariableUsage, variableId],
queryFn: () => getEnvironmentVariableUsage(variableId),
enabled: !!variableId && enabled,
queryKey: [QueryKeys.fetchEnvironmentVariableUsage, variableKey],
queryFn: () => getEnvironmentVariableUsage(variableKey),
enabled: !!variableKey && enabled,
});

return {
Expand Down
Loading
Loading