Skip to content

Commit dd26205

Browse files
chore: migrate Pezzo client logic to REST (#73)
* chore: move pezzo client logic to REST * fix(ci): deps * fix: formatting
1 parent cdd5dd5 commit dd26205

25 files changed

+631
-353
lines changed

apps/server/project.json

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,7 @@
55
"projectType": "application",
66
"targets": {
77
"build": {
8-
"dependsOn": [
9-
"prisma:generate",
10-
"graphql:codegen:offline",
11-
"^prebuild",
12-
"prebuild"
13-
],
8+
"dependsOn": ["prisma:generate", "^prebuild", "prebuild"],
149
"executor": "@nrwl/webpack:webpack",
1510
"outputs": ["{options.outputPath}"],
1611
"defaultConfiguration": "production",
@@ -102,7 +97,7 @@
10297
}
10398
},
10499
"graphql:codegen:offline": {
105-
"dependsOn": ["graphql:schema-generate"],
100+
"dependsOn": ["prsima:generate", "graphql:schema-generate"],
106101
"executor": "nx:run-commands",
107102
"options": {
108103
"command": "OFFLINE=true npx graphql-codegen"
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import {
2+
CanActivate,
3+
ExecutionContext,
4+
Injectable,
5+
Scope,
6+
UnauthorizedException,
7+
} from "@nestjs/common";
8+
import { ApiKeysService } from "../identity/api-keys.service";
9+
import { PinoLogger } from "../logger/pino-logger";
10+
11+
export enum AuthMethod {
12+
ApiKey = "ApiKey",
13+
BearerToken = "BearerToken",
14+
}
15+
16+
@Injectable({ scope: Scope.REQUEST })
17+
export class ApiKeyAuthGuard implements CanActivate {
18+
constructor(
19+
private readonly apiKeysService: ApiKeysService,
20+
private readonly logger: PinoLogger
21+
) {}
22+
23+
async canActivate(context: ExecutionContext): Promise<boolean> {
24+
const req = context.switchToHttp().getRequest();
25+
26+
if (!req.headers["x-api-key"]) {
27+
throw new UnauthorizedException("Invalid Pezzo API Key");
28+
}
29+
30+
return this.authorizeApiKey(req);
31+
}
32+
33+
private async authorizeApiKey(req) {
34+
this.logger.assign({ method: AuthMethod.ApiKey });
35+
const keyValue = req.headers["x-api-key"];
36+
const apiKey = await this.apiKeysService.getApiKey(keyValue);
37+
38+
if (!apiKey) {
39+
throw new UnauthorizedException("Invalid Pezzo API Key");
40+
}
41+
42+
const environment = apiKey.environment;
43+
44+
req.projectId = environment.projectId;
45+
req.environmentId = environment.id;
46+
req.authMethod = AuthMethod.ApiKey;
47+
this.logger.assign({
48+
environmentId: environment.id,
49+
projectId: environment.projectId,
50+
});
51+
52+
return true;
53+
}
54+
}

apps/server/src/app/auth/auth.guard.ts

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import { GqlExecutionContext } from "@nestjs/graphql";
1010
import ThirdPartyEmailPassword from "supertokens-node/recipe/thirdpartyemailpassword";
1111
import { UsersService } from "../identity/users.service";
1212
import { RequestUser } from "../identity/users.types";
13-
import { ApiKeysService } from "../identity/api-keys.service";
1413
import Session, { SessionContainer } from "supertokens-node/recipe/session";
1514
import { ProjectsService } from "../identity/projects.service";
1615
import { PinoLogger } from "../logger/pino-logger";
@@ -24,46 +23,14 @@ export enum AuthMethod {
2423
export class AuthGuard implements CanActivate {
2524
constructor(
2625
private readonly usersService: UsersService,
27-
private readonly apiKeysService: ApiKeysService,
2826
private readonly projectsService: ProjectsService,
2927
private readonly logger: PinoLogger
3028
) {}
3129

3230
async canActivate(context: ExecutionContext): Promise<boolean> {
33-
const gqlCtx = GqlExecutionContext.create(context);
34-
const ctx = gqlCtx.getContext();
35-
const req = ctx.req;
36-
const res = ctx.res;
37-
38-
if (req.headers["x-api-key"]) {
39-
return this.authorizeApiKey(req, res);
40-
}
41-
4231
return this.authorizeBearerToken(context);
4332
}
4433

45-
private async authorizeApiKey(req, _res) {
46-
this.logger.assign({ method: AuthMethod.ApiKey });
47-
const keyValue = req.headers["x-api-key"];
48-
const apiKey = await this.apiKeysService.getApiKey(keyValue);
49-
50-
if (!apiKey) {
51-
throw new UnauthorizedException("Invalid Pezzo API Key");
52-
}
53-
54-
const environment = apiKey.environment;
55-
56-
req.projectId = environment.projectId;
57-
req.environmentId = environment.id;
58-
req.authMethod = AuthMethod.ApiKey;
59-
this.logger.assign({
60-
environmentId: environment.id,
61-
projectId: environment.projectId,
62-
});
63-
64-
return true;
65-
}
66-
6734
private async authorizeBearerToken(context: ExecutionContext) {
6835
const gqlCtx = GqlExecutionContext.create(context);
6936
const ctx = gqlCtx.getContext();

apps/server/src/app/auth/auth.module.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import {
33
Module,
44
NestModule,
55
DynamicModule,
6-
Global,
76
} from "@nestjs/common";
87

98
import { AuthMiddleware } from "./auth.middleware";
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { PromptExecutionStatus } from "@prisma/client";
2+
import {
3+
IsDateString,
4+
IsEnum,
5+
IsNumber,
6+
IsObject,
7+
IsOptional,
8+
IsString,
9+
} from "class-validator";
10+
11+
export class CreatePromptExecutionDto {
12+
@IsDateString()
13+
timestamp: string;
14+
15+
@IsString()
16+
environmentId: string;
17+
18+
@IsString()
19+
promptVersionSha: string;
20+
21+
@IsEnum(PromptExecutionStatus)
22+
status: PromptExecutionStatus;
23+
24+
@IsObject()
25+
settings: Record<string, unknown>;
26+
27+
@IsObject()
28+
variables: Record<string, unknown>;
29+
30+
@IsString()
31+
content: string;
32+
33+
@IsString()
34+
interpolatedContent: string;
35+
36+
@IsOptional()
37+
@IsString()
38+
result?: string;
39+
40+
@IsOptional()
41+
@IsString()
42+
error?: string;
43+
44+
@IsNumber()
45+
duration: number;
46+
47+
@IsNumber()
48+
completionCost: number;
49+
50+
@IsNumber()
51+
completionTokens: number;
52+
53+
@IsNumber()
54+
promptCost: number;
55+
56+
@IsNumber()
57+
promptTokens: number;
58+
59+
@IsNumber()
60+
totalTokens: number;
61+
62+
@IsNumber()
63+
totalCost: number;
64+
}

apps/server/src/app/prompts/prompt-executions.resolver.ts

Lines changed: 0 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -99,88 +99,6 @@ export class PromptExecutionsResolver {
9999
}
100100
}
101101

102-
@Mutation(() => PromptExecution)
103-
async reportPromptExecutionWithApiKey(
104-
@Args("data") data: PromptExecutionCreateInput,
105-
@ApiKeyProjectId() projectId: string,
106-
@ApiKeyEnvironmentId() environmentId: string
107-
) {
108-
console.log("environmentId", environmentId);
109-
110-
this.logger.assign({ ...data }).info("Reporting prompt execution");
111-
const promptId = data.prompt.connect.id;
112-
const prompt = await this.promptsService.getPrompt(promptId);
113-
114-
if (!prompt) {
115-
throw new NotFoundException();
116-
}
117-
118-
if (prompt.projectId !== projectId) {
119-
throw new ForbiddenException();
120-
}
121-
122-
let execution: PromptExecution;
123-
124-
try {
125-
execution = await this.prisma.promptExecution.create({
126-
data: {
127-
environmentId,
128-
prompt: data.prompt,
129-
promptVersionSha: data.promptVersionSha,
130-
timestamp: data.timestamp,
131-
status: data.status,
132-
content: data.content,
133-
interpolatedContent: data.interpolatedContent,
134-
settings: data.settings,
135-
result: data.result,
136-
duration: data.duration,
137-
promptTokens: data.promptTokens,
138-
completionTokens: data.completionTokens,
139-
totalTokens: data.totalTokens,
140-
promptCost: data.promptCost,
141-
completionCost: data.completionCost,
142-
totalCost: data.totalCost,
143-
error: data.error,
144-
variables: data.variables,
145-
},
146-
});
147-
} catch (error) {
148-
this.logger.error({ error }, "Error reporting prompt execution");
149-
throw new InternalServerErrorException();
150-
}
151-
152-
this.analytics.track("PROMPT_EXECUTION:REPORTED", "api", {
153-
projectId,
154-
promptId,
155-
executionId: execution.id,
156-
integrationId: prompt.integrationId,
157-
data: {
158-
status: execution.status,
159-
duration: execution.duration / 1000,
160-
},
161-
});
162-
163-
const writeClient = this.influxService.getWriteApi("primary", "primary");
164-
const point = new Point("prompt_execution")
165-
.tag("prompt_id", execution.promptId)
166-
.tag("prompt_version_sha", execution.promptVersionSha)
167-
.tag("project_id", prompt.projectId)
168-
.tag("prompt_name", prompt.name)
169-
.tag("prompt_integration_id", prompt.integrationId)
170-
.stringField("status", execution.status)
171-
.floatField("duration", execution.duration / 1000)
172-
.floatField("prompt_cost", execution.promptCost)
173-
.floatField("completion_cost", execution.completionCost)
174-
.floatField("total_cost", execution.totalCost)
175-
.intField("prompt_tokens", execution.promptTokens)
176-
.intField("completion_tokens", execution.completionTokens)
177-
.intField("total_tokens", execution.totalTokens);
178-
179-
writeClient.writePoint(point);
180-
writeClient.flush();
181-
return execution;
182-
}
183-
184102
@UseGuards(AuthGuard)
185103
@Mutation(() => PromptExecution)
186104
async testPrompt(

0 commit comments

Comments
 (0)