Skip to content

Commit

Permalink
Merge branch 'main' into api/graphql-cancel-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesaucode authored May 9, 2022
2 parents 8b36664 + f72e3df commit a6f62b7
Show file tree
Hide file tree
Showing 17 changed files with 915 additions and 570 deletions.
4 changes: 2 additions & 2 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
/packages/aws-amplify-vue @aws-amplify/amplify-js
/packages/cache @aws-amplify/amplify-js
/packages/core @elorzafe @manueliglesias @iartemiev
/packages/datastore @svidgen @david-mcafee @manueliglesias @ArkamJ
/packages/datastore-storage-adapter @svidgen @david-mcafee @manueliglesias @ArkamJ
/packages/datastore @svidgen @david-mcafee @manueliglesias
/packages/datastore-storage-adapter @svidgen @david-mcafee @manueliglesias
/packages/geo @TreTuna @thaddmt
/packages/interactions @katiegoines
/packages/predictions @elorzafe @stocaaro
Expand Down
7 changes: 7 additions & 0 deletions packages/datastore-storage-adapter/ExpoSQLiteAdapter/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

if (process.env.NODE_ENV === 'production') {
module.exports = require('../dist/aws-amplify-datastore-sqlite-adapter-expo.min.js');
} else {
module.exports = require('../dist/aws-amplify-datastore-sqlite-adapter-expo.js');
}
7 changes: 7 additions & 0 deletions packages/datastore-storage-adapter/SQLiteAdapter/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

if (process.env.NODE_ENV === 'production') {
module.exports = require('../dist/aws-amplify-datastore-storage-adapter.min.js');
} else {
module.exports = require('../dist/aws-amplify-datastore-storage-adapter.js');
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import sqlite3 from 'sqlite3';
sqlite3.verbose();

import { SQLiteAdapter } from '../src';
import SQLiteAdapter from '../src/SQLiteAdapter/SQLiteAdapter';
import SQLiteDatabase from '../src/SQLiteAdapter/SQLiteDatabase';
import { ParameterizedStatement } from '../src/SQLiteAdapter/SQLiteUtils';
import { ParameterizedStatement } from '../src/common/types';
import {
DataStore as DataStoreType,
StorageAdapter,
Expand Down Expand Up @@ -177,7 +177,7 @@ describe('SQLiteAdapter', () => {

describe('sanity checks', () => {
it('is set as the adapter SQLite tests', async () => {
expect(adapter.constructor.name).toEqual('SQLiteAdapter');
expect(adapter.constructor.name).toEqual('CommonSQLiteAdapter');
});

it('is logging SQL statements during normal operation', async () => {
Expand Down Expand Up @@ -228,7 +228,7 @@ describe('SQLiteAdapter', () => {
[
'field1 value 0',
'2022-04-18T19:29:46.316Z',
[],
'abcd@abcd.com',
'a1d63606-bd3b-4641-870a-ac97694577a8',
null,
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
deleteByPredicateStatement,
modelCreateTableStatement,
implicitAuthFieldsForModel,
} from '../src/SQLiteAdapter/SQLiteUtils';
} from '../src/common/SQLiteUtils';
import {
InternalSchema,
PersistentModelConstructor,
Expand Down
5 changes: 4 additions & 1 deletion packages/datastore-storage-adapter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,11 @@
"devDependencies": {
"@aws-amplify/core": "4.5.3",
"@aws-amplify/datastore": "3.11.0",
"expo-file-system" : "13.1.4",
"expo-sqlite": "10.1.0",
"react-native-sqlite-storage": "5.0.0",
"sqlite3": "^5.0.2"
"sqlite3": "^5.0.2",
"@types/react-native-sqlite-storage" : "5.0.1"
},
"jest": {
"globals": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { CommonSQLiteAdapter } from '../common/CommonSQLiteAdapter';
import ExpoSQLiteDatabase from './ExpoSQLiteDatabase';

const ExpoSQLiteAdapter: CommonSQLiteAdapter = new CommonSQLiteAdapter(
new ExpoSQLiteDatabase()
);

export default ExpoSQLiteAdapter;
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
import { ConsoleLogger as Logger } from '@aws-amplify/core';
import { PersistentModel } from '@aws-amplify/datastore';
import { deleteAsync, documentDirectory } from 'expo-file-system';
import { openDatabase, WebSQLDatabase } from 'expo-sqlite';
import { DB_NAME } from '../common/constants';
import { CommonSQLiteDatabase, ParameterizedStatement } from '../common/types';

const logger = new Logger('ExpoSQLiteDatabase');

/*
Note:
ExpoSQLite transaction error callbacks require returning a boolean value to indicate whether the
error was handled or not. Returning a true value indicates the error was handled and does not
rollback the whole transaction.
*/

class ExpoSQLiteDatabase implements CommonSQLiteDatabase {
private db: WebSQLDatabase;

public async init(): Promise<void> {
// only open database once.

if (!this.db) {
// As per expo docs version, description and size arguments are ignored,
// but are accepted by the function for compatibility with the WebSQL specification.
// Hence, we do not need those arguments.
this.db = openDatabase(DB_NAME);
}
}

public createSchema(statements: string[]): Promise<void> {
return this.executeStatements(statements);
}

public async clear(): Promise<void> {
try {
logger.debug('Clearing database');
await this.closeDB();
// delete database is not supported by expo-sqlite.
// Database file needs to be deleted using deleteAsync from expo-file-system
await deleteAsync(`${documentDirectory}SQLite/${DB_NAME}`);
logger.debug('Database cleared');
} catch (error) {
logger.warn('Error clearing the database.', error);
// open database if it was closed earlier and this.db was set to undefined.
this.init();
}
}

public async get<T extends PersistentModel>(
statement: string,
params: (string | number)[]
): Promise<T> {
const results: T[] = await this.getAll(statement, params);
return results[0];
}

public getAll<T extends PersistentModel>(
statement: string,
params: (string | number)[]
): Promise<T[]> {
return new Promise((resolve, reject) => {
this.db.readTransaction(transaction => {
transaction.executeSql(
statement,
params,
(_, result) => {
resolve(result.rows._array || []);
},
(_, error) => {
reject(error);
logger.warn(error);
return true;
}
);
});
});
}

public save(statement: string, params: (string | number)[]): Promise<void> {
return new Promise((resolve, reject) => {
this.db.transaction(transaction => {
transaction.executeSql(
statement,
params,
() => {
resolve(null);
},
(_, error) => {
reject(error);
logger.warn(error);
return true;
}
);
});
});
}

public batchQuery<T = any>(
queryParameterizedStatements: Set<ParameterizedStatement> = new Set()
): Promise<T[]> {
return new Promise((resolveTransaction, rejectTransaction) => {
this.db.transaction(async transaction => {
try {
const results: any[] = await Promise.all(
[...queryParameterizedStatements].map(
([statement, params]) =>
new Promise((resolve, reject) => {
transaction.executeSql(
statement,
params,
(_, result) => {
resolve(result.rows._array[0]);
},
(_, error) => {
reject(error);
logger.warn(error);
return true;
}
);
})
)
);
resolveTransaction(results);
} catch (error) {
rejectTransaction(error);
logger.warn(error);
}
});
});
}

public batchSave(
saveParameterizedStatements: Set<ParameterizedStatement> = new Set(),
deleteParameterizedStatements?: Set<ParameterizedStatement>
): Promise<void> {
return new Promise((resolveTransaction, rejectTransaction) => {
this.db.transaction(async transaction => {
try {
// await for all sql statements promises to resolve
await Promise.all(
[...saveParameterizedStatements].map(
([statement, params]) =>
new Promise((resolve, reject) => {
transaction.executeSql(
statement,
params,
() => {
resolve(null);
},
(_, error) => {
reject(error);
logger.warn(error);
return true;
}
);
})
)
);
if (deleteParameterizedStatements) {
await Promise.all(
[...deleteParameterizedStatements].map(
([statement, params]) =>
new Promise((resolve, reject) =>
transaction.executeSql(
statement,
params,
() => {
resolve(null);
},
(_, error) => {
reject(error);
logger.warn(error);
return true;
}
)
)
)
);
}
resolveTransaction(null);
} catch (error) {
rejectTransaction(error);
logger.warn(error);
}
});
});
}

public selectAndDelete<T = any>(
queryParameterizedStatement: ParameterizedStatement,
deleteParameterizedStatement: ParameterizedStatement
): Promise<T[]> {
const [queryStatement, queryParams] = queryParameterizedStatement;
const [deleteStatement, deleteParams] = deleteParameterizedStatement;

return new Promise((resolveTransaction, rejectTransaction) => {
this.db.transaction(async transaction => {
try {
const result: T[] = await new Promise((resolve, reject) => {
transaction.executeSql(
queryStatement,
queryParams,
(_, result) => {
resolve(result.rows._array || []);
},
(_, error) => {
reject(error);
logger.warn(error);
return true;
}
);
});
await new Promise((resolve, reject) => {
transaction.executeSql(
deleteStatement,
deleteParams,
() => {
resolve(null);
},
(_, error) => {
reject(error);
logger.warn(error);
return true;
}
);
});
resolveTransaction(result);
} catch (error) {
rejectTransaction(error);
logger.warn(error);
}
});
});
}

private executeStatements(statements: string[]): Promise<void> {
return new Promise((resolveTransaction, rejectTransaction) => {
this.db.transaction(async transaction => {
try {
await Promise.all(
statements.map(
statement =>
new Promise((resolve, reject) => {
transaction.executeSql(
statement,
[],
() => {
resolve(null);
},
(_, error) => {
reject(error);
return true;
}
);
})
)
);
resolveTransaction(null);
} catch (error) {
rejectTransaction(error);
logger.warn(error);
}
});
});
}

private async closeDB() {
if (this.db) {
logger.debug('Closing Database');
// closing database is not supported by expo-sqlite.
// Workaround is to access the private db variable and call the close() method.
await (this.db as any)._db.close();
logger.debug('Database closed');
this.db = undefined;
}
}
}

export default ExpoSQLiteDatabase;
Loading

0 comments on commit a6f62b7

Please sign in to comment.