Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(dataproxy): remove 3.4.1 fallback from DataProxy Client #12738

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from 12 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
7,291 changes: 7,291 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { getConfig, getDMMF, parseEnvValue } from '@prisma/sdk'
import crypto from 'crypto'
import fs from 'fs'
import path from 'path'

import type { GetPrismaClientConfig } from '../../runtime/getPrismaClient'
import { getPrismaClient } from '../../runtime/getPrismaClient'

let prismaClientOptions: GetPrismaClientConfig
let config

// Keep original process.env
const originalEnv = process.env

beforeAll(async () => {
// Change process.env for dataproxy tests
process.env = {
...originalEnv,
PRISMA_CLIENT_ENGINE_TYPE: 'dataproxy',
PRISMA_CLI_QUERY_ENGINE_TYPE: 'library',
}

const schema = fs.readFileSync(path.join(__dirname, 'schema.prisma'), { encoding: 'utf8' })
config = await getConfig({ datamodel: schema })
const prismaClientDmmf = await getDMMF({ datamodel: schema })
const schemaContentsBase64 = Buffer.from(schema).toString('base64')
const schemaContentsHashed = crypto.createHash('sha256').update(schemaContentsBase64).digest('hex')

prismaClientOptions = {
inlineSchema: schemaContentsBase64,
inlineSchemaHash: schemaContentsHashed,
document: prismaClientDmmf,
generator: {
name: 'client',
provider: {
value: 'prisma-client-js',
fromEnvVar: null,
},
config: { engineType: 'dataproxy' },
output: null,
binaryTargets: [],
previewFeatures: [],
},
// clientVersion: '',
dirname: path.dirname(__dirname),
activeProvider: config.datasources[0].provider,
datasourceNames: [config.datasources[0].name],
relativePath: '',
relativeEnvPaths: {
rootEnvPath: '',
schemaEnvPath: '',
},
}
})

afterAll(() => {
// Restore process.env
process.env = originalEnv
})

test('getPrismaClient: Data Proxy: Error: inlineDatasources is required', () => {
expect.assertions(1)

const PrismaClient = getPrismaClient({
...prismaClientOptions,
})

try {
const prisma = new PrismaClient()
} catch (e) {
expect(e).toMatchInlineSnapshot(`Could not parse URL of the datasource`)
}
})

test('getPrismaClient: Data Proxy: Error: Datasource URL must start with prisma://', () => {
expect.assertions(1)

const PrismaClient = getPrismaClient({
...prismaClientOptions,
inlineDatasources: {
[config.datasources[0].name]: {
url: {
fromEnvVar: null,
value: 'postgresql://localhost',
},
},
},
})

try {
const prisma = new PrismaClient()
} catch (e) {
expect(e).toMatchInlineSnapshot(
`Datasource URL should use prisma:// protocol. If you are not using the Data Proxy, remove the \`dataProxy\` from the \`previewFeatures\` in your schema and ensure that \`PRISMA_CLIENT_ENGINE_TYPE\` environment variable is not set to \`dataproxy\`.`,
)
}
})

test('getPrismaClient: Data Proxy: InvalidDatasourceError: No valid API key found in the datasource URL', () => {
expect.assertions(1)

const PrismaClient = getPrismaClient({
...prismaClientOptions,
inlineDatasources: {
[config.datasources[0].name]: {
url: {
fromEnvVar: null,
value: parseEnvValue(config.datasources[0].url).replace('postgresql://', 'prisma://'),
},
},
},
})

try {
const prisma = new PrismaClient()
} catch (e) {
expect(e).toMatchInlineSnapshot(`No valid API key found in the datasource URL`)
}
})

test('getPrismaClient: Data Proxy: Error: client version is required', () => {
expect.assertions(1)

const PrismaClient = getPrismaClient({
...prismaClientOptions,
inlineDatasources: {
[config.datasources[0].name]: {
url: {
fromEnvVar: null,
value: `${parseEnvValue(config.datasources[0].url).replace('postgresql://', 'prisma://')}?api_key=something`,
},
},
},
})

try {
const prisma = new PrismaClient()
} catch (e) {
expect(e).toMatchInlineSnapshot(
`clientVersion or \`PRISMA_CLIENT_DATA_PROXY_CLIENT_VERSION\` env var needs to be set with a \`major.minor.patch\` version for Prisma Data Proxy.`,
)
}
})

test('getPrismaClient: Data Proxy: Error: client version must be major.minor.patch', () => {
expect.assertions(1)

const PrismaClient = getPrismaClient({
...prismaClientOptions,
clientVersion: 'unsupported',
inlineDatasources: {
[config.datasources[0].name]: {
url: {
fromEnvVar: null,
value: `${parseEnvValue(config.datasources[0].url).replace('postgresql://', 'prisma://')}?api_key=something`,
},
},
},
})

try {
const prisma = new PrismaClient()
} catch (e) {
expect(e).toMatchInlineSnapshot(`Only \`major.minor.patch\` versions are supported by Prisma Data Proxy.`)
}
})
126 changes: 126 additions & 0 deletions packages/client/src/__tests__/getPrismaClient/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// Chinook
datasource db {
provider = "postgresql"
url = "postgresql://localhost:5432/db"
}

generator client {
provider = "prisma-client-js"
engineType = "dataproxy"
}

model Album {
id Int @id @map("AlbumId")
Title String
ArtistId Int
Artist Artist @relation(fields: [ArtistId], references: [id])
Tracks Track[]
}

model Track {
id Int @id @map("TrackId")
Name String
Album Album? @relation(fields: [AlbumId], references: [id])
AlbumId Int?
Mediamodel Mediamodel @relation(fields: [MediamodelId], references: [id])
MediamodelId Int
Genre Genre? @relation(fields: [GenreId], references: [id])
GenreId Int?
Composer String?
Milliseconds Int
UnitPrice Float
Playlists PlaylistTrack[]
InvoiceLines InvoiceLine[]
}

model Mediamodel {
id Int @id @map("MediamodelId")
Name String?
Track Track[]
}

model Genre {
id Int @id @map("GenreId")
Name String?
Tracks Track[]
}

model Artist {
id Int @id @map("ArtistId")
Name String?
Albums Album[]
}

model Customer {
id Int @id @map("CustomerId")
FirstName String
LastName String
Company String?
Address String?
City String?
State String?
Country String?
PostalCode String?
Phone String?
Fax String?
Email String
SupportRep Employee? @relation(fields: [SupportRepId], references: [id])
SupportRepId Int?
Invoices Invoice[]
}

model Employee {
id Int @id @map("EmployeeId")
FirstName String
LastName String
Title String?
BirthDate DateTime?
HireDate DateTime?
Address String?
City String?
State String?
Country String?
PostalCode String?
Phone String?
Fax String?
Email String?
Customers Customer[]
}

model Invoice {
id Int @id @map("InvoiceId")
Customer Customer @relation(fields: [CustomerId], references: [id])
CustomerId Int
InvoiceDate DateTime
BillingAddress String?
BillingCity String?
BillingState String?
BillingCountry String?
BillingPostalCode String?
Total Float
Lines InvoiceLine[]
}

model InvoiceLine {
id Int @id @map("InvoiceLineId")
Invoice Invoice @relation(fields: [InvoiceId], references: [id])
InvoiceId Int
Track Track @relation(fields: [TrackId], references: [id])
TrackId Int
UnitPrice Float
Quantity Int
}

model Playlist {
id Int @id @map("PlaylistId")
Name String?
Tracks PlaylistTrack[]
}

model PlaylistTrack {
id Int @id
Playlist Playlist @relation(fields: [PlaylistId], references: [id])
PlaylistId Int
Track Track @relation(fields: [TrackId], references: [id])
TrackId Int
}
2 changes: 1 addition & 1 deletion packages/client/src/fixtures/blog.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const blog = /* GraphQL */ `
export const blog = /* Prisma */ `
datasource db {
provider = "postgresql"
url = "postgresql://localhost:5432/db"
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/fixtures/chinook.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const chinook = /* GraphQL */ `
export const chinook = /* Prisma */ `
datasource db {
provider = "postgresql"
url = "postgresql://localhost:5432/db"
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/fixtures/enums.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const enums = /* GraphQL */ `
export const enums = /* Prisma */ `
datasource db {
provider = "postgresql"
url = "postgresql://localhost:5432/db"
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/fixtures/recommender.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const recommender = /* GraphQL */ `
export const recommender = /* Prisma */ `
datasource db {
provider = "sqlite"
url = "file:./dev.db"
Expand Down
2 changes: 1 addition & 1 deletion packages/client/src/fixtures/saleBuyers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const saleBuyers = /* GraphQL */ `
export const saleBuyers = /* Prisma */ `
datasource db {
provider = "postgresql"
url = "postgresql://localhost:5432/db"
Expand Down
2 changes: 2 additions & 0 deletions packages/engine-core/src/data-proxy/DataProxyEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ export class DataProxyEngine extends Engine {
this.logEmitter.on('error', () => {})

const [host, apiKey] = this.extractHostAndApiKey()

this.remoteClientVersion = getClientVersion(this.config)

this.headers = { Authorization: `Bearer ${apiKey}` }
this.host = host

Expand Down
35 changes: 29 additions & 6 deletions packages/engine-core/src/data-proxy/utils/getClientVersion.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,42 @@
import Debug from '@prisma/debug'

import type { EngineConfig } from '../../common/Engine'
import { NotImplementedYetError } from '../errors/NotImplementedYetError'

const debug = Debug('prisma:engine-core:getClientVersion')

/**
* Determine the client version to be sent to the DataProxy
* @param config
* @returns
*/
export function getClientVersion(config: EngineConfig) {
const [version, suffix] = config.clientVersion?.split('-') ?? []
export function getClientVersion(config: EngineConfig): string {
const dataproxyClientVersionFromEnvVar = process.env.PRISMA_CLIENT_DATA_PROXY_CLIENT_VERSION

if (dataproxyClientVersionFromEnvVar) {
debug(`Client version: "${dataproxyClientVersionFromEnvVar}" from PRISMA_CLIENT_DATA_PROXY_CLIENT_VERSION env var.`)
return dataproxyClientVersionFromEnvVar
} else if (config.clientVersion) {
const [version, suffix] = config.clientVersion.split('-') ?? []
jkomyno marked this conversation as resolved.
Show resolved Hide resolved
debug(`Client version: "${version}" from config.clientVersion.`)

// we expect the version to match the pattern major.minor.patch
if (!suffix && /^[1-9][0-9]*\.[0-9]+\.[0-9]+$/.test(version)) {
return version
// we expect the version to match the pattern major.minor.patch
if (!suffix && /^[1-9][0-9]*\.[0-9]+\.[0-9]+$/.test(version)) {
return version
} else {
throw new NotImplementedYetError('Only `major.minor.patch` versions are supported by Prisma Data Proxy.', {
clientVersion: config.clientVersion,
})
}
} else {
throw new NotImplementedYetError(
'clientVersion or `PRISMA_CLIENT_DATA_PROXY_CLIENT_VERSION` env var needs to be set with a `major.minor.patch` version for Prisma Data Proxy.',
Copy link
Member Author

@janpio janpio May 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where would I set the clientVersion myself?
Is that the PDP use case?

{
clientVersion: '',
},
)
}

// TODO: we should have a Data Proxy deployment which accepts any
// arbitrary version for testing purposes.
return '3.4.1' // and we default it to the latest stable
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import chalk from 'chalk'

import { highlightDatamodel } from '../../../highlight/highlight'
import { link } from '../../../utils/link'

export const proxyFeatureFlagMissingMessage = `\nIn order to use the ${chalk.bold('dataproxy')} engine,
you need to set the ${chalk.green('dataProxy')} feature flag.
Expand Down