Skip to content

Commit

Permalink
Implement named instance support
Browse files Browse the repository at this point in the history
This adds positional argument to `connect()` and `createPool()`
functions which can be either DSN or an instance name.
  • Loading branch information
tailhook committed Aug 21, 2020
1 parent a9b9d7c commit d9cb1a6
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 77 deletions.
37 changes: 25 additions & 12 deletions docs/api/connection.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,21 @@ API
Connection
==========

.. js:function:: connect(options)
.. js:function:: connect(dsn, options)

Establish a connection to an EdgeDB server.

:param options: Connection parameters object.
:param string dsn:
If this parameter does not start with ``edgedb://`` then this is
a :ref:`name of an instance <edgedb-instances>`.

:param string options.dsn:
Connection arguments specified using as a single string in the
connection URI format:
Otherwise it specifies a single string in the connection URI format:
``edgedb://user:password@host:port/database?option=value``.
The following options are recognized: host, port,
user, database, password.

:param options: Connection parameters object.

:param string|string[] options.host:
Database host address as one of the following:

Expand Down Expand Up @@ -224,10 +226,24 @@ Connection
Pool
====

.. js:function:: createPool(options)
.. js:function:: createPool(dsn, options)

Create a connection pool to an EdgeDB server.

If this parameter does not start with ``edgedb://`` then this is
a :ref:`name of an instance <edgedb-instances>`.

Otherwise it specifies a single string in the connection URI format:

:param string dsn:
If this parameter does not start with ``edgedb://`` then this is
a :ref:`name of an instance <edgedb-instances>`.

Otherwise it specifies a single string in the connection URI format:
``edgedb://user:password@host:port/database?option=value``.
The following options are recognized: host, port,
user, database, password.

:param options: Connection pool parameters object.

:param ConnectConfig options.connectOptions:
Expand Down Expand Up @@ -278,12 +294,9 @@ Pool
const edgedb = require("edgedb");
async function main() {
const pool = await edgedb.createPool({
connectOptions: {
user: "edgedb",
host: "127.0.0.1",
},
});
const pool = await edgedb.createPool(
"edgedb://edgedb@localhost/test"
);
try {
let data = await pool.queryOne("SELECT [1, 2, 3]");
Expand Down
11 changes: 2 additions & 9 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@ run queries.
async function main() {
// Establish a connection to an existing database
// named "test" as an "edgedb" user.
const conn = await edgedb.connect({
dsn: "edgedb://edgedb@localhost/test"
});
const conn = await edgedb.connect("edgedb://edgedb@localhost/test");
try {
// Create a User object type.
Expand Down Expand Up @@ -113,12 +111,7 @@ a certain number of open connections and borrow them when needed.
async function main() {
// Create a connection pool to an existing database
const pool = await edgedb.createPool({
connectOptions: {
user: "edgedb",
host: "127.0.0.1"
},
});
const pool = await edgedb.createPool("edgedb://edgedb@localhost/test");
try {
// Create a User object type.
Expand Down
3 changes: 2 additions & 1 deletion src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,10 @@ enum TransactionStatus {
export const proxyMap = new WeakMap<Connection, IConnectionProxied>();

export default function connect(
dsn?: string,
options?: ConnectConfig | null
): Promise<Connection> {
return ConnectionImpl.connect(options);
return ConnectionImpl.connect({...options, dsn});
}

class ConnectionImpl implements Connection {
Expand Down
16 changes: 10 additions & 6 deletions src/con_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export interface NormalizedConnectConfig {

export interface ConnectConfig {
dsn?: string;
credentialsFile?: string;
host?: string | string[];
port?: number | number[];
user?: string;
Expand Down Expand Up @@ -253,12 +252,17 @@ function parseConnectDsnAndArgs({
}
}
} else if (dsn) {
if (!/[A-Za-z_][A-Za-z_0-9]*/.test(dsn)) {
throw Error(`dsn "${dsn}" is neither a edgedb:// URI \
nor valid instance name`)
if (!/^[A-Za-z_][A-Za-z_0-9]*$/.test(dsn)) {
throw Error(
`dsn "${dsn}" is neither a edgedb:// URI nor valid instance name`
);
}
const credentials_file = path.join(os.homedir(),
".edgedb", "credentials", dsn + ".json");
const credentials_file = path.join(
os.homedir(),
".edgedb",
"credentials",
dsn + ".json"
);
const credentials = readCredentialsFile(credentials_file);
port = credentials.port;
user = credentials.user;
Expand Down
31 changes: 22 additions & 9 deletions src/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,10 @@ export interface PoolOptions {
onAcquire?: (proxy: Connection) => Promise<void>;
onRelease?: (proxy: Connection) => Promise<void>;
onConnect?: (connection: Connection) => Promise<void>;
connectionFactory?: (options?: ConnectConfig | null) => Promise<Connection>;
connectionFactory?: (
dsn: string | undefined,
options?: ConnectConfig | null
) => Promise<Connection>;
}

export class PoolStats implements IPoolStats {
Expand Down Expand Up @@ -430,12 +433,13 @@ class PoolImpl implements Pool {
private _onRelease?: (proxy: Connection) => Promise<void>;
private _onConnect?: (connection: Connection) => Promise<void>;
private _connectionFactory: (
dsn: string | undefined,
options?: ConnectConfig | null
) => Promise<Connection>;
private _generation: number;
private _connectOptions?: ConnectConfig | null;
private _connectOptions: ConnectConfig;

protected constructor(options: PoolOptions) {
protected constructor(dsn?: string, options: PoolOptions = {}) {
const {onAcquire, onRelease, onConnect, connectOptions} = options;
const minSize =
options.minSize === undefined ? DefaultMinPoolSize : options.minSize;
Expand All @@ -456,7 +460,7 @@ class PoolImpl implements Pool {
this._closing = false;
this._closed = false;
this._generation = 0;
this._connectOptions = connectOptions;
this._connectOptions = {...connectOptions, dsn};
this._connectionFactory = options.connectionFactory ?? connect;
}

Expand Down Expand Up @@ -484,8 +488,11 @@ class PoolImpl implements Pool {
}

/** @internal */
static async create(options?: PoolOptions | null): Promise<PoolImpl> {
const pool = new PoolImpl(options || {});
static async create(
dsn?: string,
options?: PoolOptions | null
): Promise<PoolImpl> {
const pool = new PoolImpl(dsn, options || {});
await pool.initialize();
return pool;
}
Expand Down Expand Up @@ -580,7 +587,10 @@ class PoolImpl implements Pool {

/** @internal */
async getNewConnection(): Promise<Connection> {
const connection = await this._connectionFactory(this._connectOptions);
const connection = await this._connectionFactory(
this._connectOptions.dsn,
this._connectOptions
);

if (this._onConnect) {
try {
Expand Down Expand Up @@ -791,6 +801,9 @@ class PoolImpl implements Pool {
}
}

export function createPool(options?: PoolOptions | null): Promise<Pool> {
return PoolImpl.create(options);
export function createPool(
dsn?: string,
options?: PoolOptions | null
): Promise<Pool> {
return PoolImpl.create(dsn, options);
}
16 changes: 3 additions & 13 deletions test/connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,9 @@ test("parseConnectArguments", () => {

{
opts: {dsn: "pq:///dbname?host=/unix_sock/test&user=spam"},
error: "invalid DSN",
error:
'dsn "pq:///dbname?host=/unix_sock/test&user=spam" ' +
"is neither a edgedb:// URI nor valid instance name",
},

{
Expand Down Expand Up @@ -391,18 +393,6 @@ test("parseConnectArguments", () => {
password: "lZTBy1RVCfOpBAOwSCwIyBIR",
},
},
{
opts: {
dsn: "edgedb://user3@localhost:5555/abcdef",
// We don't even read credentials file if dns is present
credentialsFile: "non-existent.json",
},
result: {
addrs: [["localhost", 5555]],
user: "user3",
database: "abcdef",
},
},
];

for (const testCase of TESTS) {
Expand Down
33 changes: 33 additions & 0 deletions test/credentials.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import {readCredentialsFile, validateCredentials} from "../src/credentials";

test("readCredentialsFile", () => {
let data = readCredentialsFile("test/credentials1.json");
expect(data).toEqual({
database: "test3n",
password: "lZTBy1RVCfOpBAOwSCwIyBIR",
port: 10702,
user: "test3n",
});
});

test("emptyCredentials", () => {
expect(() => validateCredentials({})).toThrow("`user` key is required");
});

test("port", () => {
expect(() => validateCredentials({user: "u1", port: "abc"})).toThrow(
"invalid `port` value"
);
expect(() => validateCredentials({user: "u1", port: 0})).toThrow(
"invalid `port` value"
);
expect(() => validateCredentials({user: "u1", port: 0.5})).toThrow(
"invalid `port` value"
);
expect(() => validateCredentials({user: "u1", port: -1})).toThrow(
"invalid `port` value"
);
expect(() => validateCredentials({user: "u1", port: 65536})).toThrow(
"invalid `port` value"
);
});
2 changes: 1 addition & 1 deletion test/globalSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export default async () => {
global.edgedbProc = proc;

const [host, port] = await done;
const con = await connect({
const con = await connect(undefined, {
host,
port,
user: "edgedb",
Expand Down
Loading

0 comments on commit d9cb1a6

Please sign in to comment.