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
@@ -0,0 +1,39 @@
import { afterEach, describe, expect, test } from 'bun:test';
import pkg from '../../package.json' with { type: 'json' };
import { deploySourceHeaders } from '../credentials.ts';

describe('deploySourceHeaders', () => {
const originalEnv = process.env['GITHUB_ACTIONS'];

afterEach(() => {
if (originalEnv === undefined) {
delete process.env['GITHUB_ACTIONS'];
} else {
process.env['GITHUB_ACTIONS'] = originalEnv;
}
});

test('uses "github-action" as source when GITHUB_ACTIONS is "true"', () => {
process.env['GITHUB_ACTIONS'] = 'true';
expect(deploySourceHeaders()['x-prisma-deploy-source']).toBe('github-action');
});

test('uses "composer" as source outside GitHub Actions', () => {
delete process.env['GITHUB_ACTIONS'];
expect(deploySourceHeaders()['x-prisma-deploy-source']).toBe('composer');
});

test('uses "composer" as source when GITHUB_ACTIONS has another value', () => {
process.env['GITHUB_ACTIONS'] = 'false';
expect(deploySourceHeaders()['x-prisma-deploy-source']).toBe('composer');
});

test('always sets x-prisma-client-name to "composer"', () => {
expect(deploySourceHeaders()['x-prisma-client-name']).toBe('composer');
});

test('sets x-prisma-client-version to the package version', () => {
expect(pkg.version.length).toBeGreaterThan(0);
expect(deploySourceHeaders()['x-prisma-client-version']).toBe(pkg.version);
});
});
8 changes: 6 additions & 2 deletions packages/1-prisma-cloud/0-lowering/lowering/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import * as Context from 'effect/Context';
import * as Effect from 'effect/Effect';
import * as Layer from 'effect/Layer';
import * as Redacted from 'effect/Redacted';
import { managementApiBaseUrl, PrismaCredentials } from './credentials.ts';
import { deploySourceHeaders, managementApiBaseUrl, PrismaCredentials } from './credentials.ts';

export type ManagementApiClient = ReturnType<typeof createManagementApiClient>;

Expand All @@ -31,6 +31,10 @@ export const layer = (options?: {
Effect.gen(function* () {
const { token } = yield* PrismaCredentials;
const baseUrl = options?.apiOrigin ?? (yield* managementApiBaseUrl());
return createManagementApiClient({ token: Redacted.value(token), baseUrl });
return createManagementApiClient({
token: Redacted.value(token),
baseUrl,
headers: deploySourceHeaders(),
});
}),
);
12 changes: 12 additions & 0 deletions packages/1-prisma-cloud/0-lowering/lowering/src/credentials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as Context from 'effect/Context';
import * as Effect from 'effect/Effect';
import * as Layer from 'effect/Layer';
import type * as Redacted from 'effect/Redacted';
import pkg from '../package.json' with { type: 'json' };

/**
* The Prisma service token used to authenticate Management API calls. Kept
Expand Down Expand Up @@ -64,6 +65,17 @@ const normalizeBaseUrl = (value: string): Effect.Effect<string, Error> =>
: new Error(`Invalid Prisma Management API URL: ${String(cause)}`),
});

/**
* Headers the Management API records in its deploy analytics: which tool sent
* the request, and whether it ran in GitHub Actions. The API behaves the same
* without them.
*/
export const deploySourceHeaders = (): Record<string, string> => ({
'x-prisma-deploy-source': process.env['GITHUB_ACTIONS'] === 'true' ? 'github-action' : 'composer',
'x-prisma-client-name': 'composer',
'x-prisma-client-version': pkg.version,
});

Comment thread
gregory-boch-prisma marked this conversation as resolved.
/**
* The Management API origin every Prisma-Cloud client in this package uses —
* Composer's own SDK client AND upstream alchemy's postgres providers resolve
Expand Down
26 changes: 23 additions & 3 deletions packages/1-prisma-cloud/0-lowering/lowering/src/providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,15 @@ import * as Prisma from 'alchemy/Prisma';
import * as Provider from 'alchemy/Provider';
import * as Effect from 'effect/Effect';
import * as Layer from 'effect/Layer';
import * as HttpClient from 'effect/unstable/http/HttpClient';
import * as HttpClientRequest from 'effect/unstable/http/HttpClientRequest';
import * as client from './client.ts';
import { fromEnv, managementApiBaseUrl, PrismaCredentials } from './credentials.ts';
import {
deploySourceHeaders,
fromEnv,
managementApiBaseUrl,
PrismaCredentials,
} from './credentials.ts';

/** The collection of Prisma resource providers. */
export class Providers extends Provider.ProviderCollection<Providers>()('PrismaComposer') {}
Expand Down Expand Up @@ -32,6 +39,19 @@ const prismaEnvironment = () =>
}),
);

/**
* A node:http transport that adds deploy-source headers to every request.
* Used privately by upstreamPrismaProviders — not exposed as the ambient
* HttpClient, so the artifact-upload path is unaffected.
*/
const prismaManagementHttpLayer = Layer.effect(
HttpClient.HttpClient,
Effect.gen(function* () {
const base = yield* HttpClient.HttpClient;
return HttpClient.mapRequest(base, HttpClientRequest.setHeaders(deploySourceHeaders()));
}),
).pipe(Layer.provide(NodeHttpClient.layerNodeHttp));

/**
* Upstream alchemy's live providers for the postgres family (Project,
* Database, Connection), the compute family (App, Deployment,
Expand All @@ -56,10 +76,10 @@ const upstreamPrismaProviders = () =>
Prisma.BucketAccessKeyProvider(),
).pipe(
Layer.provideMerge(Prisma.PrismaClientLive),
// Provide (NOT provideMerge) the node transport privately — mirrors
// Provide (NOT provideMerge) the transport privately — mirrors
// upstream's Providers.ts: it must serve only the Prisma management
// client, never override the ambient HttpClient of other providers.
Layer.provide(NodeHttpClient.layerNodeHttp),
Layer.provide(prismaManagementHttpLayer),
Layer.provideMerge(prismaEnvironment()),
);

Expand Down
Loading