Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,14 @@ const documents2_3_4_5 = firestore.query("FirstCollection").Limit(4).Offset(2).E
const documents3_4_5_6 = firestore.query("FirstCollection").Range(3, 7).Execute();
```

#### Support for multiple databases in one GCP project
By default, GCP crates only one "(default)" database; however, it is possible to have multiple databases under different names in one GCP project.
You can access a non-default database using its name while initializing the library.

```javascript
const firestore = FirestoreApp.getFirestore(email, key, projected, "v1", yourDatabaseName);
```

See other library methods and details [in the wiki](../../../wiki).

### Frequently Asked Questions
Expand Down
2 changes: 1 addition & 1 deletion Auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class Auth {
* @returns {string} The generated access token string
*/
get accessToken(): string {
const request = new Request(this.authUrl, '', this.options_).post<TokenResponse>();
const request = Request.authRequest(this.authUrl, this.options_).post<TokenResponse>();
return request.access_token;
}

Expand Down
39 changes: 28 additions & 11 deletions Firestore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ class Firestore implements FirestoreRead, FirestoreWrite, FirestoreDelete {
auth: Auth;
basePath: string;
baseUrl: string;
projectId: string;
databaseName: string;

/**
* Constructor
Expand All @@ -15,12 +17,15 @@ class Firestore implements FirestoreRead, FirestoreWrite, FirestoreDelete {
* @param {string} key the user private key (for authentication)
* @param {string} projectId the Firestore project ID
* @param {string} apiVersion [Optional] The Firestore API Version ("v1beta1", "v1beta2", or "v1")
* @param {string} databaseName [Optional] database name
* @return {Firestore} an authenticated interface with a Firestore project (constructor)
*/
constructor(email: string, key: string, projectId: string, apiVersion: Version = 'v1') {
constructor(email: string, key: string, projectId: string, apiVersion: Version = 'v1', databaseName = '(default)') {
// The authentication token used for accessing Firestore
this.auth = new Auth(email, key);
this.basePath = `projects/${projectId}/databases/(default)/documents/`;
this.projectId = projectId;
this.databaseName = databaseName;
this.basePath = `projects/${projectId}/databases/${databaseName}/documents/`;
this.baseUrl = `https://firestore.googleapis.com/${apiVersion}/${this.basePath}`;
}

Expand All @@ -38,7 +43,7 @@ class Firestore implements FirestoreRead, FirestoreWrite, FirestoreDelete {
* @return {object} the document object
*/
getDocument(path: string): Document {
const request = new Request(this.baseUrl, this.authToken);
const request = Request.dbRequest(this.baseUrl, this.authToken, this.projectId, this.databaseName);
return this.getDocument_(path, request);
}
getDocument_ = FirestoreRead.prototype.getDocument_;
Expand All @@ -55,7 +60,12 @@ class Firestore implements FirestoreRead, FirestoreWrite, FirestoreDelete {
if (!ids) {
docs = this.query(path).Execute() as Document[];
} else {
const request = new Request(this.baseUrl.replace('/documents/', '/documents:batchGet/'), this.authToken);
const request = Request.dbRequest(
this.baseUrl.replace('/documents/', '/documents:batchGet/'),
this.authToken,
this.projectId,
this.databaseName
);
docs = this.getDocuments_(this.basePath + path, request, ids);
}
return docs;
Expand All @@ -69,7 +79,7 @@ class Firestore implements FirestoreRead, FirestoreWrite, FirestoreDelete {
* @return {object} an array of IDs of the documents in the collection
*/
getDocumentIds(path: string): string[] {
const request = new Request(this.baseUrl, this.authToken);
const request = Request.dbRequest(this.baseUrl, this.authToken, this.projectId, this.databaseName);
return this.getDocumentIds_(path, request);
}
getDocumentIds_ = FirestoreRead.prototype.getDocumentIds_;
Expand All @@ -82,7 +92,7 @@ class Firestore implements FirestoreRead, FirestoreWrite, FirestoreDelete {
* @return {object} the Document object written to Firestore
*/
createDocument(path: string, fields?: Record<string, any>): Document {
const request = new Request(this.baseUrl, this.authToken);
const request = Request.dbRequest(this.baseUrl, this.authToken, this.projectId, this.databaseName);
return this.createDocument_(path, fields || {}, request);
}

Expand All @@ -99,7 +109,7 @@ class Firestore implements FirestoreRead, FirestoreWrite, FirestoreDelete {
* @return {object} the Document object written to Firestore
*/
updateDocument(path: string, fields: Record<string, any>, mask?: boolean | string[]): Document {
const request = new Request(this.baseUrl, this.authToken);
const request = Request.dbRequest(this.baseUrl, this.authToken, this.projectId, this.databaseName);
return this.updateDocument_(path, fields, request, mask);
}

Expand All @@ -113,7 +123,7 @@ class Firestore implements FirestoreRead, FirestoreWrite, FirestoreDelete {
* @return {object} the JSON response from the DELETE request
*/
deleteDocument(path: string): FirestoreAPI.Empty {
const request = new Request(this.baseUrl, this.authToken);
const request = Request.dbRequest(this.baseUrl, this.authToken, this.projectId, this.databaseName);
return this.deleteDocument_(path, request);
}

Expand All @@ -127,7 +137,7 @@ class Firestore implements FirestoreRead, FirestoreWrite, FirestoreDelete {
* @return {object} the JSON response from the GET request
*/
query(path: string): Query {
const request = new Request(this.baseUrl, this.authToken);
const request = Request.dbRequest(this.baseUrl, this.authToken, this.projectId, this.databaseName);
return this.query_(path, request);
}
query_ = FirestoreRead.prototype.query_;
Expand All @@ -142,8 +152,15 @@ type Version = 'v1' | 'v1beta1' | 'v1beta2';
* @param {string} key the user private key (for authentication)
* @param {string} projectId the Firestore project ID
* @param {string} apiVersion [Optional] The Firestore API Version ("v1beta1", "v1beta2", or "v1")
* @param {string} databaseName [Optional] database name
* @return {Firestore} an authenticated interface with a Firestore project (function)
*/
function getFirestore(email: string, key: string, projectId: string, apiVersion: Version = 'v1'): Firestore {
return new Firestore(email, key, projectId, apiVersion);
function getFirestore(
email: string,
key: string,
projectId: string,
apiVersion: Version = 'v1',
databaseName = '(default)'
): Firestore {
return new Firestore(email, key, projectId, apiVersion, databaseName);
}
33 changes: 31 additions & 2 deletions Request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,54 @@ class Request {
url: string;
authToken: string;
queryString: string;
projectId: string;
databaseName: string;
options: RequestOptions;
nextPageToken?: string | null;
documents?: any[];
fields?: Record<string, any>;

/**
* @param url the base url to utilize
* @param authToken authorization token to make requests
* @param projectId id od the GCP project
* @param databaseName naem otf the databaseName
*/
static dbRequest(url: string, authToken: string, projectId: string, databaseName = '(default)') {
return new Request(url, authToken, projectId, databaseName);
}

/**
* @param url the base url to utilize
* @param options set of options to utilize over the default headers
*/
static authRequest(url: string, options: RequestOptions) {
return new Request(url, '', '', '', options);
}

/**
* @param url the base url to utilize
* @param authToken authorization token to make requests
* @param options [Optional] set of options to utilize over the default headers
*/
constructor(url: string, authToken?: string, options?: RequestOptions) {
constructor(
url: string,
authToken?: string,
projectId?: string,
databaseName = '(default)',
options?: RequestOptions
) {
this.url = url;
this.queryString = '';
this.authToken = authToken || '';
this.projectId = projectId || '';
this.databaseName = databaseName || '(default)';

if (!this.authToken) options = options || {};
// Set default header options if none are passed in
this.options = options || {
headers: {
'x-goog-request-params': `project_id=${projectId}&database_id=${databaseName}`,
'content-type': 'application/json',
'Authorization': 'Bearer ' + this.authToken,
},
Expand Down Expand Up @@ -139,7 +168,7 @@ class Request {
* @return {Request} A copy of this object
*/
clone(): Request {
return new Request(this.url, this.authToken, this.options);
return new Request(this.url, this.authToken, this.projectId, this.databaseName, this.options);
}

/**
Expand Down
8 changes: 4 additions & 4 deletions Tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Tests implements TestManager {
this.pass.push('Test_Get_Firestore');
} catch (e) {
// On failure, fail the remaining tests without execution
this.fail.set('Test_Get_Firestore', e);
this.fail.set('Test_Get_Firestore', <Error>e);
const err = new Error('Test Initialization Error');
err.stack = 'See Test_Get_Firestore Error';
for (const func of funcs) {
Expand Down Expand Up @@ -73,7 +73,7 @@ class Tests implements TestManager {
// eslint-disable-next-line no-ex-assign
e = err;
}
this.fail.set(func, e);
this.fail.set(func, <Error>e);
}
}
}
Expand Down Expand Up @@ -112,7 +112,7 @@ class Tests implements TestManager {
this.db.createDocument(path);
GSUnit.fail('Duplicate document without error');
} catch (e) {
if (e.message !== `Document already exists: ${this.db.basePath}${path}`) {
if ((<Error>e).message !== `Document already exists: ${this.db.basePath}${path}`) {
throw e;
}
}
Expand Down Expand Up @@ -219,7 +219,7 @@ class Tests implements TestManager {
this.db.getDocument(path);
GSUnit.fail('Missing document without error');
} catch (e) {
if (e.message !== `Document "${this.db.basePath}${path}" not found.`) {
if ((<Error>e).message !== `Document "${this.db.basePath}${path}" not found.`) {
throw e;
}
}
Expand Down