Skip to content

Commit

Permalink
feat: added Capacitor driver (#7695)
Browse files Browse the repository at this point in the history
* added new driver for Capacitor

* updated CHANGELOG.md
  • Loading branch information
chriswep committed May 29, 2021
1 parent 620aac9 commit 0f7a778
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 4 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 0.2.33 (UNRELEASED)

### Bug fixes

### Features

* added capacitor driver ([#7695](https://github.com/typeorm/typeorm/pull/7695))

## [0.2.32](https://github.com/typeorm/typeorm/compare/0.2.31...0.2.32) (2021-03-30)

### Bug Fixes
Expand Down
11 changes: 9 additions & 2 deletions docs/connection-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* [`postgres` / `cockroachdb` connection options](#postgres--cockroachdb-connection-options)
* [`sqlite` connection options](#sqlite-connection-options)
* [`better-sqlite3` connection options](#better-sqlite3-connection-options)
* [`capacitor` connection options](#capacitor-connection-options)
* [`cordova` connection options](#cordova-connection-options)
* [`react-native` connection options](#react-native-connection-options)
* [`nativescript` connection options](#nativescript-connection-options)
Expand All @@ -23,8 +24,8 @@ Connection options is a connection configuration you pass to `createConnection`
## Common connection options

* `type` - Database type. You must specify what database engine you use.
Possible values are "mysql", "postgres", "cockroachdb", "mariadb", "sqlite", "better-sqlite3", "cordova", "nativescript",
"oracle", "mssql", "mongodb", "sqljs", "react-native".
Possible values are "mysql", "postgres", "cockroachdb", "mariadb", "sqlite", "better-sqlite3", "capacitor", "cordova",
"nativescript", "oracle", "mssql", "mongodb", "sqljs", "react-native".
This option is **required**.

* `name` - Connection name. You'll use it to get connection you need using `getConnection(name: string)`
Expand Down Expand Up @@ -196,6 +197,12 @@ See [SSL options](https://github.com/mysqljs/mysql#ssl-options).

* `prepareDatabase` - Function to run before a database is used in typeorm. You can access original better-sqlite3 Database object here.

## `capacitor` connection options

* `database` - Database name (capacitor-sqlite will add the suffix `SQLite.db`)

* `driver` - The capacitor-sqlite instance. For example, `new SQLiteConnection(CapacitorSQLite)`.

## `cordova` connection options

* `database` - Database name
Expand Down
4 changes: 3 additions & 1 deletion src/connection/ConnectionOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {AuroraDataApiConnectionOptions} from "../driver/aurora-data-api/AuroraDa
import {SapConnectionOptions} from "../driver/sap/SapConnectionOptions";
import {AuroraDataApiPostgresConnectionOptions} from "../driver/aurora-data-api-pg/AuroraDataApiPostgresConnectionOptions";
import {BetterSqlite3ConnectionOptions} from "../driver/better-sqlite3/BetterSqlite3ConnectionOptions";
import {CapacitorConnectionOptions} from "../driver/capacitor/CapacitorConnectionOptions";


/**
Expand All @@ -37,4 +38,5 @@ export type ConnectionOptions =
AuroraDataApiConnectionOptions|
AuroraDataApiPostgresConnectionOptions|
ExpoConnectionOptions|
BetterSqlite3ConnectionOptions;
BetterSqlite3ConnectionOptions |
CapacitorConnectionOptions;
3 changes: 3 additions & 0 deletions src/driver/DriverFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {Driver} from "./Driver";
import {Connection} from "../connection/Connection";
import {SapDriver} from "./sap/SapDriver";
import {BetterSqlite3Driver} from "./better-sqlite3/BetterSqlite3Driver";
import {CapacitorDriver} from "./capacitor/CapacitorDriver";

/**
* Helps to create drivers.
Expand Down Expand Up @@ -63,6 +64,8 @@ export class DriverFactory {
return new AuroraDataApiDriver(connection);
case "aurora-data-api-pg":
return new AuroraDataApiPostgresDriver(connection);
case "capacitor":
return new CapacitorDriver(connection);
default:
throw new MissingDriverError(type);
}
Expand Down
21 changes: 21 additions & 0 deletions src/driver/capacitor/CapacitorConnectionOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { BaseConnectionOptions } from "../../connection/BaseConnectionOptions";

/**
* Sqlite-specific connection options.
*/
export interface CapacitorConnectionOptions extends BaseConnectionOptions {
/**
* Database type.
*/
readonly type: "capacitor";

/**
* Database name (capacitor-sqlite will add the suffix `SQLite.db`)
*/
readonly database: string;

/**
* The capacitor-sqlite instance. For example, `new SQLiteConnection(CapacitorSQLite)`.
*/
readonly driver: any;
}
99 changes: 99 additions & 0 deletions src/driver/capacitor/CapacitorDriver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { AbstractSqliteDriver } from "../sqlite-abstract/AbstractSqliteDriver";
import { CapacitorConnectionOptions } from "./CapacitorConnectionOptions";
import { CapacitorQueryRunner } from "./CapacitorQueryRunner";
import { QueryRunner } from "../../query-runner/QueryRunner";
import { Connection } from "../../connection/Connection";
import {
DriverOptionNotSetError,
DriverPackageNotInstalledError,
} from "../../error";
import { ReplicationMode } from "../types/ReplicationMode";

export class CapacitorDriver extends AbstractSqliteDriver {
driver: any;
options: CapacitorConnectionOptions;

// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------

constructor(connection: Connection) {
super(connection);

this.database = this.options.database;
this.driver = this.options.driver;

// validate options to make sure everything is set
if (!this.options.database)
throw new DriverOptionNotSetError("database");

if (!this.options.driver) throw new DriverOptionNotSetError("driver");

// load sqlite package
this.sqlite = this.options.driver;
}

// -------------------------------------------------------------------------
// Public Methods
// -------------------------------------------------------------------------

/**
* Performs connection to the database.
*/
async connect(): Promise<void> {
this.databaseConnection = this.createDatabaseConnection();
await this.databaseConnection;
}

/**
* Closes connection with database.
*/
async disconnect(): Promise<void> {
this.queryRunner = undefined;
const databaseConnection = await this.databaseConnection;
return databaseConnection.close().then(() => {
this.databaseConnection = undefined;
});
}

/**
* Creates a query runner used to execute database queries.
*/
createQueryRunner(mode: ReplicationMode): QueryRunner {
if (!this.queryRunner)
this.queryRunner = new CapacitorQueryRunner(this);

return this.queryRunner;
}

// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------

/**
* Creates connection with the database.
*/
protected async createDatabaseConnection() {
const connection = await this.sqlite.createConnection(
this.options.database,
false,
"no-encryption",
1
);
await connection.open();
// we need to enable foreign keys in sqlite to make sure all foreign key related features
// working properly. this also makes onDelete to work with sqlite.
await connection.execute(`PRAGMA foreign_keys = ON;`);
return connection;
}

protected loadDependencies(): void {
this.sqlite = this.driver;
if (!this.driver) {
throw new DriverPackageNotInstalledError(
"Capacitor",
"@capacitor-community/sqlite"
);
}
}
}
98 changes: 98 additions & 0 deletions src/driver/capacitor/CapacitorQueryRunner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { QueryRunnerAlreadyReleasedError } from "../../error/QueryRunnerAlreadyReleasedError";
import { QueryFailedError } from "../../error/QueryFailedError";
import { AbstractSqliteQueryRunner } from "../sqlite-abstract/AbstractSqliteQueryRunner";
import { CapacitorDriver } from "./CapacitorDriver";
import { Broadcaster } from "../../subscriber/Broadcaster";
import { ObjectLiteral } from "../..";

/**
* Runs queries on a single sqlite database connection.
*/
export class CapacitorQueryRunner extends AbstractSqliteQueryRunner {
/**
* Database driver used by connection.
*/
driver: CapacitorDriver;

// -------------------------------------------------------------------------
// Constructor
// -------------------------------------------------------------------------

constructor(driver: CapacitorDriver) {
super();
this.driver = driver;
this.connection = driver.connection;
this.broadcaster = new Broadcaster(this);
}

async executeSet(set: { statement: string; values?: any[] }[]) {
if (this.isReleased) throw new QueryRunnerAlreadyReleasedError();

const databaseConnection = await this.connect();

return databaseConnection.executeSet(set, false);
}

/**
* Executes a given SQL query.
*/
async query(query: string, parameters?: any[]): Promise<any> {
if (this.isReleased) throw new QueryRunnerAlreadyReleasedError();

const databaseConnection = await this.connect();

this.driver.connection.logger.logQuery(query, parameters, this);

let pResult: Promise<any>;
const command = query.substr(0, query.indexOf(" "));

if (
[
"PRAGMA",
"BEGIN",
"ROLLBACK",
"COMMIT",
"CREATE",
"ALTER",
"DROP",
].indexOf(command) !== -1
) {
pResult = databaseConnection.execute(query, false);
} else if (["INSERT", "UPDATE", "DELETE"].indexOf(command) !== -1) {
pResult = databaseConnection
.run(query, parameters, false)
.then(
({
changes,
}: {
changes: { changes?: number; lastId?: number };
}) => changes.lastId || changes.changes
);
} else {
pResult = databaseConnection
.query(query, parameters)
.then(({ values }: { values: any[] }) => values);
}

return pResult.catch((err: any) => {
this.driver.connection.logger.logQueryError(
err,
query,
parameters,
this
);
throw new QueryFailedError(query, parameters, err);
});
}

// -------------------------------------------------------------------------
// Protected Methods
// -------------------------------------------------------------------------

/**
* Parametrizes given object of values. Used to create column=value queries.
*/
protected parametrize(objectLiteral: ObjectLiteral): string[] {
return Object.keys(objectLiteral).map((key) => `"${key}"` + "=?");
}
}
3 changes: 2 additions & 1 deletion src/driver/types/DatabaseType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export type DatabaseType =
"aurora-data-api"|
"aurora-data-api-pg"|
"expo"|
"better-sqlite3";
"better-sqlite3" |
"capacitor";

0 comments on commit 0f7a778

Please sign in to comment.