Repository: typedorm/typedorm (ts)

Issue:
Auto remove connection when failed to build metadatas for it
Creating connection with entities that are likely to fail to be build will put connection instance in an undesirable state, where connection exists in connection manger's context but but entities are not built. 

If failed to build entity metadata for connection, connection should be removed from connection manger.

Pull request description:
fix(core): connection manager not cleaning up broken connections
closes #36 

Patch:
diff --git a/packages/core/public-api.ts b/packages/core/public-api.ts
index 31d44ca..6884e68 100644
--- a/packages/core/public-api.ts
+++ b/packages/core/public-api.ts
@@ -14,7 +14,16 @@ export * from './src/classes/manager/transaction-manager';
 
 export function createConnection(options: ConnectionOptions) {
   const connection = connectionManger().create(options);
-  return connection.connect();
+
+  const connected = connection.connect();
+
+  if (!connected) {
+    throw new Error(
+      `Failed to create connection with options: ${JSON.stringify(options)}`
+    );
+  }
+
+  return connected;
 }
 
 export function createConnections(optionsList: ConnectionOptions[]) {
diff --git a/packages/core/src/classes/connection/connection-manager.ts b/packages/core/src/classes/connection/connection-manager.ts
index 95718be..3f6daf4 100644
--- a/packages/core/src/classes/connection/connection-manager.ts
+++ b/packages/core/src/classes/connection/connection-manager.ts
@@ -23,7 +23,13 @@ export class ConnectionManager {
         `There is already an existing connection with name "${name}".`
       );
     }
-    this.connections.set(name, new Connection(options));
+    this.connections.set(
+      name,
+      new Connection(
+        options,
+        this.clearByName.bind(this) // bind Connection manager ctx
+      )
+    );
 
     const createdConnection = this.connections.get(name);
     if (!createdConnection) {
@@ -37,4 +43,8 @@ export class ConnectionManager {
   clear() {
     this.connections = new Map<string, Connection>();
   }
+
+  clearByName(name: string) {
+    this.connections.delete(name);
+  }
 }
diff --git a/packages/core/src/classes/connection/connection.ts b/packages/core/src/classes/connection/connection.ts
index 80e6c32..3f6f3d6 100644
--- a/packages/core/src/classes/connection/connection.ts
+++ b/packages/core/src/classes/connection/connection.ts
@@ -26,7 +26,10 @@ export class Connection {
   private _entityMetadatas: Map<string, EntityMetadata>;
   private isConnected: boolean;
 
-  constructor(private options: ConnectionOptions) {
+  constructor(
+    private options: ConnectionOptions,
+    private destroySelf: (name: string) => void
+  ) {
     const {table, name = 'default'} = options;
     if (table) {
       this.table = table;
@@ -58,15 +61,21 @@ export class Connection {
       );
     }
 
-    this._entityMetadatas = new Map(
-      this.buildMetadatas().map(entityMeta => [
-        entityMeta.target.name,
-        entityMeta,
-      ])
-    );
+    try {
+      this._entityMetadatas = new Map(
+        this.buildMetadatas().map(entityMeta => [
+          entityMeta.target.name,
+          entityMeta,
+        ])
+      );
 
-    this.isConnected = true;
-    return this;
+      this.isConnected = true;
+      return this;
+    } catch (err) {
+      // Failed to connect to connection, clear self from connection manager
+      this.destroySelf(this.name);
+      return;
+    }
   }
 
   get entityMetadatas() {
@@ -141,7 +150,7 @@ export class Connection {
     });
   }
 
-  private buildMetadatas() {
+  buildMetadatas() {
     return new ConnectionMetadataBuilder(this).buildEntityMetadatas(
       this.options.entities
     );


Test patch:
diff --git a/packages/core/src/classes/connection/__test__/connection-manager.spec.ts b/packages/core/src/classes/connection/__test__/connection-manager.spec.ts
index d09b6e6..3104275 100644
--- a/packages/core/src/classes/connection/__test__/connection-manager.spec.ts
+++ b/packages/core/src/classes/connection/__test__/connection-manager.spec.ts
@@ -3,6 +3,7 @@ import {getConnection} from '@typedorm/core';
 import {User} from '../../../../__mocks__/user';
 import {createTestConnection, resetTestConnection} from '@typedorm/testing';
 import path from 'path';
+import {ConnectionManager} from '../connection-manager';
 jest.useFakeTimers('modern').setSystemTime(new Date('2020-01-01'));
 
 beforeEach(() => {
@@ -97,3 +98,23 @@ test('Connection manager should properly configure global table instance for sin
 
   expect(userMetadata.table).toEqual(globalTable);
 });
+
+/**
+ * Issue: 38
+ */
+test('Auto removes connection when failed to connect', () => {
+  const connectionManager = new ConnectionManager();
+  const createdConnection = connectionManager.create({
+    entities: [User],
+  });
+
+  createdConnection.buildMetadatas = jest.fn().mockImplementation(() => {
+    throw new Error('Failed to build metadata');
+  });
+
+  createdConnection.connect();
+
+  expect(() => connectionManager.get()).toThrow(
+    'No such connection with name "default" exists'
+  );
+});