Skip to content
This repository has been archived by the owner on Feb 13, 2023. It is now read-only.

Commit

Permalink
fix: repair crash on sqlite/mysql/sqlserver connection strings
Browse files Browse the repository at this point in the history
the previous version was hardcoded to only support postgresql strings.
  • Loading branch information
iiian committed Feb 15, 2022
1 parent 26e69a7 commit 77cb2b9
Show file tree
Hide file tree
Showing 9 changed files with 188 additions and 18 deletions.
8 changes: 7 additions & 1 deletion .gitignore
Expand Up @@ -4,4 +4,10 @@ yarn-error.log
build
pnpm-debug.log
.env*
packages/generator/README.md
packages/generator/README.md
**/obj
packages/usage/csharp-integration-tests/**/bin
**/test.db*
packages/usage/csharp-integration-tests/**/model
packages/usage/csharp/integration/tests/.vs
packages/usage/prisma/migrations
4 changes: 3 additions & 1 deletion README.md
Expand Up @@ -90,13 +90,15 @@ Right now, the primary target is .NET core, version 5.0 and later. If ~~enough~~

| Prisma connector | Supported | .NET core provider mapping |
|-------------------|-----------|--------------------------------------------------------------------------------------------------------------------|
| sqlserver | ✔️ | [Microsoft.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.SqlServer/) |
| postgres | ✔️ | [Npgsql.EntityFrameworkCore.PostgreSQL](https://www.nuget.org/packages/Npgsql.EntityFrameworkCore.PostgreSQL/) |
| mysql | ✔️ | [Pomelo.EntityFrameworkCore.MySql](https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MySql/) |
| sqlite | ✔️ | [Microsoft.EntityFrameworkCore.Sqlite](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Sqlite/) |
| sqlserver | ✔️ * | [Microsoft.EntityFrameworkCore.SqlServer](https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.SqlServer/) |
| cockroachdb || - |
| mongodb || - |

> _* The prisma sql server connection string is [quite configurable](https://www.prisma.io/docs/concepts/database-connectors/sql-server#connection-details). For the time being, I'm just focused on supporting the basic SQL-based authentication w/ TLS support._
For more information on EntityFramework database provider support, visit the [DbContext configuration guide](https://docs.microsoft.com/en-us/ef/core/dbcontext-configuration/).

For more information on Prisma-supported database connectors, visit the [Prisma database connector documentation](https://www.prisma.io/docs/concepts/database-connectors).
Expand Down
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`getConnectionString given a mysql database connector should transform the connection string 1`] = `"Host=host;Database=initial_db;Username=user;Password=password"`;

exports[`getConnectionString given a postgresql database connector should transform the connection string 1`] = `"Host=host;Database=initial_db;Username=user;Password=password"`;

exports[`getConnectionString given a sqlite3 database connector should transform the connection string 1`] = `"Data Source=./test.db;"`;

exports[`getConnectionString given a sqlserver database connector should transform the connection string 1`] = `"Host=host:5432;Database=initial_db;Username=user;Password=password;Encrypt=true"`;
3 changes: 2 additions & 1 deletion packages/generator/src/__tests__/generateDbContext.ts
Expand Up @@ -13,4 +13,5 @@ describe('generateDbContext', () => {
};
expect(generateDbContext(args)).toMatchSnapshot();
});
});
});

64 changes: 64 additions & 0 deletions packages/generator/src/__tests__/getConnectionString.ts
@@ -0,0 +1,64 @@
import { getConnectionString } from '../helpers/getConnectionString';

describe('getConnectionString', () => {
describe('given a sqlite3 database connector', () => {
it('should transform the connection string', () => {
const connection_string = getConnectionString({
activeProvider: 'sqlite',
config: {},
name: '',
provider: 'sqlite',
url: {
fromEnvVar: null,
value: 'file:./test.db'
}
});
expect(connection_string).toMatchSnapshot();
});
});
describe('given a postgresql database connector', () => {
it('should transform the connection string', () => {
const connection_string = getConnectionString({
activeProvider: 'postgresql',
config: {},
name: '',
provider: 'postgresql',
url: {
fromEnvVar: null,
value: 'postgresql://user:password@host:5432/initial_db'
}
});
expect(connection_string).toMatchSnapshot();
});
});
describe('given a mysql database connector', () => {
it('should transform the connection string', () => {
const connection_string = getConnectionString({
activeProvider: 'mysql',
config: {},
name: '',
provider: 'mysql',
url: {
fromEnvVar: null,
value: 'mysql://user:password@host:5432/initial_db'
}
});
expect(connection_string).toMatchSnapshot();
});
});
describe('given a sqlserver database connector', () => {
it('should transform the connection string', () => {
const connection_string = getConnectionString({
activeProvider: 'sqlserver',
config: {},
name: '',
provider: 'sqlserver',
url: {
fromEnvVar: null,
value: 'sqlserver://host:5432;Database=initial_db;user=user;PASSWORD=password;encrypt=true'
}
});
expect(connection_string).toMatchSnapshot();
});
});
});
17 changes: 2 additions & 15 deletions packages/generator/src/generator.ts
Expand Up @@ -2,14 +2,15 @@ import { DataSource, generatorHandler, GeneratorOptions } from '@prisma/generato
import { logger } from '@prisma/sdk';
import path from 'path';
import { GENERATOR_NAME } from './constants';
import { getConnectionString } from './helpers/getConnectionString';
import { generateAll } from './helpers/generateAll';
import { writeFileSafely } from './utils/writeFileSafely';

const { version } = require('../package.json');

generatorHandler({
onManifest() {
logger.info(`${GENERATOR_NAME}:Registered`);
logger.info(`${GENERATOR_NAME}@${version}:Registered`);
return {
version,
defaultOutput: '../generated',
Expand Down Expand Up @@ -60,20 +61,6 @@ generatorHandler({
}
},
});
export function getConnectionString(datasource: DataSource): string {
if (!datasource) {
throw new Error('No datasource specified. I need a datasource in order to construct the DbContext. The DbContext will need to setup the connection in the `OnConfiguring` life cycle hook.');
}
const conn_str_fmt: RegExp = /\w+:\/\/((?<user>.*)\:(?<password>.*)@)?(?<host>.*)\:(?<port>\d{4,5})(\/(?<initial_db>.*))?/;
const raw_conn_str = (datasource.url.fromEnvVar) ? process.env[datasource.url.fromEnvVar]! : datasource.url.value;
const matches = conn_str_fmt.exec(raw_conn_str);
logger.info(matches);
if (!matches) {
throw new Error(`Connection string could not be parsed: ${raw_conn_str}`);
}
return `Host=${matches!.groups!.host!};Database=${matches!.groups!.initial_db!};Username=${matches!.groups!.user!};Password=${matches!.groups!.password!}`;
}

export function getDbHost(datasource: DataSource): string {
if (!datasource) {
throw new Error('No datasource specified. I need a datasource in order to construct the DbContext. The DbContext will need to setup the connection in the `OnConfiguring` life cycle hook.');
Expand Down
58 changes: 58 additions & 0 deletions packages/generator/src/helpers/getConnectionString.ts
@@ -0,0 +1,58 @@
import { DataSource } from '@prisma/generator-helper';
import { logger } from '@prisma/sdk';
import { join } from 'path';

export function getConnectionString(datasource: DataSource): string {
if (!datasource) {
throw new Error('No datasource specified. I need a datasource in order to construct the DbContext. The DbContext will need to setup the connection in the `OnConfiguring` life cycle hook.');
}
switch (datasource.provider) {
case 'mysql':
case 'postgres' as any:
case 'postgresql':
return getMysqlOrPostgresqlConnectionString(datasource);
case 'sqlite':
return getSqliteConnectionString(datasource);
case 'sqlserver':
return getSqlServerConnectionString(datasource);
default:
throw new Error(`Unrecognized or unsupported datasource provider ${datasource.provider}`);
}
}

function getMysqlOrPostgresqlConnectionString(datasource: DataSource): string {
const conn_str_fmt: RegExp = /\w+:\/\/((?<user>.*)\:(?<password>.*)@)?(?<host>.*)\:(?<port>\d{4,5})(\/(?<initial_db>.*))?/;
const raw_conn_str = getUrl(datasource);
const matches = conn_str_fmt.exec(raw_conn_str);
if (!matches) {
throw new Error(`Connection string could not be parsed: ${raw_conn_str}`);
}
return `Host=${matches!.groups!.host!};Database=${matches!.groups!.initial_db!};Username=${matches!.groups!.user!};Password=${matches!.groups!.password!}`;
}

function getSqliteConnectionString(datasource: DataSource): string {
const conn_str_fmt: RegExp = /file\:(?<db_path>.*)/;
const raw_conn_str = getUrl(datasource);
const matches = conn_str_fmt.exec(raw_conn_str);
if (!matches) {
throw new Error(`Connection string could not be parsed: ${raw_conn_str}`);
}
return `Data Source=${matches!.groups!.db_path!};`;
}

function getSqlServerConnectionString(datasource: DataSource): string {
const host_fmt: RegExp = /^sqlserver:\/\/(?<host>.*)(\:(?<port>\d{4,5}))?\;(database|Database|DATABASE)\=(?<initial_db>.*)\;(user|User|USER)\=(?<user>.*)\;(password|Password|PASSWORD)\=(?<password>.*)\;(encrypt|Encrypt|ENCRYPT)\=(?<encrypted>(true|false))(\;)?$/;
const raw_conn_str = getUrl(datasource);
const matches = host_fmt.exec(raw_conn_str);
if (!matches) {
throw new Error(`Connection string could not be parsed: ${raw_conn_str}`);
}
return `Host=${matches!.groups!.host!};Database=${matches!.groups!.initial_db!};Username=${matches!.groups!.user!};Password=${matches!.groups!.password!};Encrypt=${matches!.groups!.encrypted!}`;
}

function getUrl(datasource: DataSource): string {
return (datasource.url.fromEnvVar)
? process.env[datasource.url.fromEnvVar]!
: datasource.url.value
;
}
Empty file.
43 changes: 43 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 77cb2b9

Please sign in to comment.