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
12 changes: 8 additions & 4 deletions Auth.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import Request from './Request';
import Util_ from './Util';
import type * as AuthTypes from './typings/Auth';

/**
* Auth token is formatted to {@link https://developers.google.com/identity/protocols/oauth2/service-account#authorizingrequests}
*
Expand All @@ -7,7 +11,7 @@
* @param authUrl the authorization url
* @returns {string} the access token needed for making future requests
*/
class Auth {
export default class Auth {
email: string;
key: string;
authUrl: string;
Expand All @@ -31,7 +35,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 = new Request(this.authUrl, '', this.options_).post<AuthTypes.TokenResponse>();
return request.access_token;
}

Expand All @@ -47,7 +51,7 @@ class Auth {
return `${signatureInput}.${Utilities.base64EncodeWebSafe(signature)}`;
}

get jwtPayload_(): JwtClaim {
get jwtPayload_(): AuthTypes.JwtClaim {
const seconds = ~~(new Date().getTime() / 1000);
return {
iss: this.email,
Expand All @@ -58,7 +62,7 @@ class Auth {
};
}

get jwtHeader_(): JwtHeader {
get jwtHeader_(): AuthTypes.JwtHeader {
return {
alg: 'RS256',
typ: 'JWT',
Expand Down
30 changes: 17 additions & 13 deletions Document.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import FirestoreAPI = gapi.client.firestore;
import { FirestoreGoogleAppsScript as T } from './typings';
import Util_ from './Util';

/**
* Firestore Document
*/
class Document implements FirestoreAPI.Document, FirestoreAPI.MapValue {
export default class Document implements FirestoreAPI.Document, FirestoreAPI.MapValue {
fields?: Record<string, FirestoreAPI.Value>;
createTime?: string;
updateTime?: string;
Expand All @@ -14,13 +18,13 @@ class Document implements FirestoreAPI.Document, FirestoreAPI.MapValue {
* @param obj
* @param name
*/
constructor(obj: Value | FirestoreAPI.Document, name?: string | Document | FirestoreAPI.ReadOnly) {
constructor(obj: T.Value | FirestoreAPI.Document, name?: string | Document | FirestoreAPI.ReadOnly) {
//Treat parameters as existing Document with extra parameters to merge in
if (typeof name === 'object') {
Object.assign(this, obj);
Object.assign(this, name);
} else {
this.fields = Document.wrapMap(obj as ValueObject).fields;
this.fields = Document.wrapMap(obj as T.ValueObject).fields;
if (name) {
this.name = name;
}
Expand Down Expand Up @@ -49,15 +53,15 @@ class Document implements FirestoreAPI.Document, FirestoreAPI.MapValue {
* @param {object} firestoreDoc the Firestore document whose fields will be extracted
* @return {object} an object with the given document's fields and values
*/
get obj(): Record<string, Value> {
get obj(): Record<string, T.Value> {
return Document.unwrapObject(this);
}

toString(): string {
return `Document (${Util_.getDocumentFromPath(this.name as string)[1]})`;
}

static unwrapValue(obj: FirestoreAPI.Value): Value {
static unwrapValue(obj: FirestoreAPI.Value): T.Value {
// eslint-disable-next-line prefer-const
let [type, val]: [string, any] = Object.entries(obj)[0];
switch (type) {
Expand All @@ -83,17 +87,17 @@ class Document implements FirestoreAPI.Document, FirestoreAPI.MapValue {
}
}

static unwrapObject(obj: FirestoreAPI.MapValue): ValueObject {
static unwrapObject(obj: FirestoreAPI.MapValue): T.ValueObject {
return Object.entries(obj.fields || {}).reduce(
(o: Record<string, Value>, [key, val]: [string, FirestoreAPI.Value]) => {
(o: Record<string, T.Value>, [key, val]: [string, FirestoreAPI.Value]) => {
o[key] = Document.unwrapValue(val);
return o;
},
{}
);
}

static unwrapArray(wrappedArray: FirestoreAPI.Value[] = []): Value[] {
static unwrapArray(wrappedArray: FirestoreAPI.Value[] = []): T.Value[] {
return wrappedArray.map(this.unwrapValue, this);
}

Expand All @@ -102,13 +106,13 @@ class Document implements FirestoreAPI.Document, FirestoreAPI.MapValue {
return new Date(wrappedDate.replace(Util_.regexDatePrecision, '$1'));
}

static wrapValue(val: Value): FirestoreAPI.Value {
static wrapValue(val: T.Value): FirestoreAPI.Value {
const type = typeof val;
switch (type) {
case 'string':
return this.wrapString(val as string);
case 'object':
return this.wrapObject(val as ValueObject);
return this.wrapObject(val as T.ValueObject);
case 'number':
return this.wrapNumber(val as number);
case 'boolean':
Expand All @@ -132,7 +136,7 @@ class Document implements FirestoreAPI.Document, FirestoreAPI.MapValue {
return { stringValue: string };
}

static wrapObject(obj: ValueObject): FirestoreAPI.Value {
static wrapObject(obj: T.ValueObject): FirestoreAPI.Value {
if (!obj) {
return this.wrapNull();
}
Expand All @@ -154,9 +158,9 @@ class Document implements FirestoreAPI.Document, FirestoreAPI.MapValue {
return { mapValue: this.wrapMap(obj) };
}

static wrapMap(obj: ValueObject): FirestoreAPI.MapValue {
static wrapMap(obj: T.ValueObject): FirestoreAPI.MapValue {
return {
fields: Object.entries(obj).reduce((o: Record<string, FirestoreAPI.Value>, [key, val]: [string, Value]) => {
fields: Object.entries(obj).reduce((o: Record<string, FirestoreAPI.Value>, [key, val]: [string, T.Value]) => {
o[key] = Document.wrapValue(val);
return o;
}, {}),
Expand Down
17 changes: 14 additions & 3 deletions Firestore.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
import FirestoreAPI = gapi.client.firestore;
import FirestoreRead from './FirestoreRead';
import FirestoreWrite from './FirestoreWrite';
import FirestoreDelete from './FirestoreDelete';
import Document from './Document';
import Query from './Query';
import Auth from './Auth';
import Request from './Request';

export { Auth, Document, Firestore, FirestoreRead, FirestoreWrite, FirestoreDelete, Query, Request };

/* eslint @typescript-eslint/no-unused-vars: ["error", { "varsIgnorePattern": "^getFirestore$" }] */

/**
* An authenticated interface to a Firestore project.
*/
class Firestore implements FirestoreRead, FirestoreWrite, FirestoreDelete {
export default class Firestore implements FirestoreRead, FirestoreWrite, FirestoreDelete {
auth: Auth;
basePath: string;
baseUrl: string;
Expand Down Expand Up @@ -133,7 +144,7 @@ class Firestore implements FirestoreRead, FirestoreWrite, FirestoreDelete {
query_ = FirestoreRead.prototype.query_;
}

type Version = 'v1' | 'v1beta1' | 'v1beta2';
export type Version = 'v1' | 'v1beta1' | 'v1beta2';

/**
* Get an object that acts as an authenticated interface with a Firestore project.
Expand All @@ -144,6 +155,6 @@ type Version = 'v1' | 'v1beta1' | 'v1beta2';
* @param {string} apiVersion [Optional] The Firestore API Version ("v1beta1", "v1beta2", or "v1")
* @return {Firestore} an authenticated interface with a Firestore project (function)
*/
function getFirestore(email: string, key: string, projectId: string, apiVersion: Version = 'v1'): Firestore {
export function getFirestore(email: string, key: string, projectId: string, apiVersion: Version = 'v1'): Firestore {
return new Firestore(email, key, projectId, apiVersion);
}
5 changes: 4 additions & 1 deletion FirestoreDelete.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import FirestoreAPI = gapi.client.firestore;
import Request from './Request';

/**
* Extends Firestore class with private method
*/
class FirestoreDelete {
export default class FirestoreDelete {
/**
* Delete the Firestore document at the given path.
* Note: this deletes ONLY this document, and not any subcollections.
Expand Down
8 changes: 7 additions & 1 deletion FirestoreRead.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import FirestoreAPI = gapi.client.firestore;
import Document from './Document';
import Request from './Request';
import Query from './Query';
import Util_ from './Util';

/**
* Extends Firestore class with private method
*/
class FirestoreRead {
export default class FirestoreRead {
/**
* Get the Firestore document or collection at a given path.
* If the collection contains enough IDs to return a paginated result, this method only returns the first page.
Expand Down
7 changes: 6 additions & 1 deletion FirestoreWrite.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import FirestoreAPI = gapi.client.firestore;
import Document from './Document';
import Request from './Request';
import Util_ from './Util';

/**
* Extends Firestore class with private method
*/
class FirestoreWrite {
export default class FirestoreWrite {
/**
* Create a document with the given ID and fields.
*
Expand Down
16 changes: 11 additions & 5 deletions Query.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
interface QueryCallback {
import FirestoreAPI = gapi.client.firestore;
import Document from './Document';
import Util_ from './Util';

import type { FilterOp } from './typings/Query';

export interface QueryCallback {
(query: Query): Document[];
}
/**
* @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1/StructuredQuery#Operator_1 FieldFilter Operator}
*/
enum FieldFilterOps_ {
export enum FieldFilterOps_ {
'==' = 'EQUAL',
'===' = 'EQUAL',
'<' = 'LESS_THAN',
Expand All @@ -18,7 +24,7 @@ enum FieldFilterOps_ {
/**
* @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1/StructuredQuery#Operator_2 UnaryFilter Operator}
*/
enum UnaryFilterOps_ {
export enum UnaryFilterOps_ {
'nan' = 'IS_NAN',
'null' = 'IS_NULL',
}
Expand All @@ -29,7 +35,7 @@ enum UnaryFilterOps_ {
*
* @see {@link https://firebase.google.com/docs/firestore/reference/rest/v1/StructuredQuery Firestore Structured Query}
*/
class Query implements FirestoreAPI.StructuredQuery {
export default class Query implements FirestoreAPI.StructuredQuery {
select?: FirestoreAPI.Projection;
from?: FirestoreAPI.CollectionSelector[];
where?: FirestoreAPI.Filter;
Expand Down Expand Up @@ -104,7 +110,7 @@ class Query implements FirestoreAPI.StructuredQuery {

filter_(field: string, operator: string | number | null, value: any): FirestoreAPI.Filter {
if (typeof operator === 'string') {
operator = operator.toLowerCase().replace('_', '') as FilterOp;
operator = (operator.toLowerCase().replace('_', '') as FilterOp) as string;
} else if (value == null) {
// Covers null and undefined values
operator = 'null';
Expand Down
4 changes: 3 additions & 1 deletion Request.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import Util_ from './Util';

/**
* Manages the requests to send. Chain methods to update options.
* Must call .get/.post/.patch/.remove to send the request with given options.
*/
class Request {
export default class Request {
url: string;
authToken: string;
queryString: string;
Expand Down
16 changes: 12 additions & 4 deletions Tests.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
import { getFirestore } from './Firestore';
import { FirestoreGoogleAppsScript } from './typings/index';
import Util_ from './Util';

import type { Version } from './Firestore';
type Firestore = FirestoreGoogleAppsScript.Firestore;
type Value = FirestoreGoogleAppsScript.Value;

function StoreCredentials_(): void {
/** DO NOT SAVE CREDENTIALS HERE */
const email = '[email protected]';
Expand Down Expand Up @@ -30,7 +38,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', e as Error);
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 +81,7 @@ class Tests implements TestManager {
// eslint-disable-next-line no-ex-assign
e = err;
}
this.fail.set(func, e);
this.fail.set(func, e as Error);
}
}
}
Expand Down Expand Up @@ -112,7 +120,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 ((e as Error).message !== `Document already exists: ${this.db.basePath}${path}`) {
throw e;
}
}
Expand Down Expand Up @@ -219,7 +227,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 ((e as Error).message !== `Document "${this.db.basePath}${path}" not found.`) {
throw e;
}
}
Expand Down
3 changes: 2 additions & 1 deletion Util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
class Util_ {
import FirestoreAPI = gapi.client.firestore;
export default class Util_ {
/**
* RegEx test for root path references. Groups relative path for extraction.
*/
Expand Down
Loading