push all website files

This commit is contained in:
Jacob Levine
2019-01-06 13:14:45 -06:00
parent d7301e26c3
commit d2d5d4c04e
15662 changed files with 2166516 additions and 0 deletions

View File

@@ -0,0 +1,206 @@
import { CloudFunction, EventContext } from '../cloud-functions';
/**
* Select analytics events to listen to for events.
* @param analyticsEventType Name of the analytics event type.
*/
export declare function event(analyticsEventType: string): AnalyticsEventBuilder;
/**
* The Firebase Analytics event builder interface.
*
* Access via [`functions.analytics.event()`](functions.analytics#event).
*/
export declare class AnalyticsEventBuilder {
private triggerResource;
private opts;
/**
* Event handler that fires every time a Firebase Analytics event occurs.
*
* @param {!function(!functions.Event<!functions.analytics.AnalyticsEvent>)}
* handler Event handler that fires every time a Firebase Analytics event
* occurs.
*
* @return {!functions.CloudFunction<!functions.analytics.AnalyticsEvent>} A
* Cloud Function you can export.
*/
onLog(handler: (event: AnalyticsEvent, context: EventContext) => PromiseLike<any> | any): CloudFunction<AnalyticsEvent>;
}
/**
* Interface representing a Firebase Analytics event that was logged for a specific user.
*/
export declare class AnalyticsEvent {
/**
* The date on which the event.was logged.
* (`YYYYMMDD` format in the registered timezone of your app).
*/
reportingDate: string;
/** The name of the event. */
name: string;
/**
* A map of parameters and their values associated with the event.
*
* Note: Values in this map are cast to the most appropriate type. Due to
* the nature of JavaScript's number handling, this might entail a loss of
* precision in cases of very large integers.
*/
params: {
[key: string]: any;
};
/** UTC client time when the event happened. */
logTime: string;
/** UTC client time when the previous event happened. */
previousLogTime?: string;
/** Value parameter in USD. */
valueInUSD?: number;
/** User-related dimensions. */
user?: UserDimensions;
}
/**
* Interface representing the user who triggered the events.
*/
export declare class UserDimensions {
/**
* The user ID set via the `setUserId` API.
* [Android](https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.html#setUserId(java.lang.String))
* [iOS](https://firebase.google.com/docs/reference/ios/firebaseanalytics/api/reference/Classes/FIRAnalytics#/c:objc(cs)FIRAnalytics(cm)setUserID)
*/
userId?: string;
/** The time (in UTC) at which the user first opened the app. */
firstOpenTime?: string;
/**
* A map of user properties set with the
* [`setUserProperty`](https://firebase.google.com/docs/analytics/android/properties) API.
*
* All values are [`UserPropertyValue`](functions.analytics.UserPropertyValue) objects.
*/
userProperties: {
[key: string]: UserPropertyValue;
};
/** Device information. */
deviceInfo: DeviceInfo;
/** User's geographic information. */
geoInfo: GeoInfo;
/** App information. */
appInfo?: AppInfo;
/** Information regarding the bundle in which these events were uploaded. */
bundleInfo: ExportBundleInfo;
}
/**
* Predefined or custom properties stored on the client side.
*/
export declare class UserPropertyValue {
/** Last set value of a user property. */
value: string;
/** UTC client time when the user property was last set. */
setTime: string;
}
/**
* Interface representing the device that triggered these Firebase Analytics events.
*/
export interface DeviceInfo {
/**
* Device category.
* Examples: "tablet" or "mobile".
*/
deviceCategory?: string;
/**
* Device brand name.
* Examples: "Samsung", "HTC"
*/
mobileBrandName?: string;
/**
* Device model name in human-readable format.
* Example: "iPhone 7"
*/
mobileModelName?: string;
/**
* Device marketing name.
* Example: "Galaxy S4 Mini"
*/
mobileMarketingName?: string;
/**
* Device model, as read from the OS.
* Example: "iPhone9,1"
*/
deviceModel?: string;
/**
* Device OS version when data capture ended.
* Example: "4.4.2"
*/
platformVersion?: string;
/**
* Vendor specific device identifier. This is IDFV on iOS. Not used for Android.
* Example: '599F9C00-92DC-4B5C-9464-7971F01F8370'
*/
deviceId?: string;
/**
* The type of the [`resettable_device_id`](https://support.google.com/dfp_premium/answer/6238701?hl=en)
* is IDFA on iOS (when available) and AdId on Android.
*
* Example: "71683BF9-FA3B-4B0D-9535-A1F05188BAF3"
*/
resettableDeviceId?: string;
/**
* The user language in language-country format, where language is an ISO 639
* value and country is an ISO 3166 value.
*
* Examples: "en-us", "en-za", "zh-tw", "jp"
*/
userDefaultLanguage: string;
/**
* The time zone of the device when data was uploaded, as seconds skew from UTC.
* Use this to calculate the device's local time for [`event.timestamp`](functions.Event#timestamp)`.
*/
deviceTimeZoneOffsetSeconds: number;
/**
* The device's Limit Ad Tracking setting.
* When `true`, you cannot use `resettableDeviceId` for remarketing, demographics or influencing ads serving
* behaviour. However, you can use resettableDeviceId for conversion tracking and campaign attribution.
*/
limitedAdTracking: boolean;
}
/**
* Interface representing the geographic origin of the events.
*/
export interface GeoInfo {
/** The geographic continent. Example: "Americas". */
continent?: string;
/** The geographic country. Example: "Brazil". */
country?: string;
/** The geographic region. Example: "State of Sao Paulo". */
region?: string;
/** The geographic city. Example: "Sao Paulo". */
city?: string;
}
/**
* Interface representing the application that triggered these events.
*/
export interface AppInfo {
/**
* The app's version name.
* Examples: "1.0", "4.3.1.1.213361", "2.3 (1824253)", "v1.8b22p6".
*/
appVersion?: string;
/**
* Unique id for this instance of the app.
* Example: "71683BF9FA3B4B0D9535A1F05188BAF3".
*/
appInstanceId: string;
/**
* The identifier of the store that installed the app.
* Examples: "com.sec.android.app.samsungapps", "com.amazon.venezia", "com.nokia.nstore".
*/
appStore?: string;
/** The app platform. Examples: "ANDROID", "IOS". */
appPlatform: string;
/** Unique application identifier within an app store. */
appId?: string;
}
/**
* Interface representing the bundle in which these events were uploaded.
*/
export declare class ExportBundleInfo {
/** Monotonically increasing index for each bundle set by the Analytics SDK. */
bundleSequenceId: number;
/** Timestamp offset (in milliseconds) between collection time and upload time. */
serverTimestampOffset: number;
}

View File

@@ -0,0 +1,236 @@
"use strict";
// The MIT License (MIT)
//
// Copyright (c) 2017 Firebase
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
Object.defineProperty(exports, "__esModule", { value: true });
const _ = require("lodash");
const cloud_functions_1 = require("../cloud-functions");
/** @internal */
exports.provider = 'google.analytics';
/** @internal */
exports.service = 'app-measurement.com';
/**
* Select analytics events to listen to for events.
* @param analyticsEventType Name of the analytics event type.
*/
function event(analyticsEventType) {
return _eventWithOpts(analyticsEventType, {});
}
exports.event = event;
/** @internal */
function _eventWithOpts(analyticsEventType, opts) {
return new AnalyticsEventBuilder(() => {
if (!process.env.GCLOUD_PROJECT) {
throw new Error('process.env.GCLOUD_PROJECT is not set.');
}
return ('projects/' + process.env.GCLOUD_PROJECT + '/events/' + analyticsEventType);
}, opts);
}
exports._eventWithOpts = _eventWithOpts;
/**
* The Firebase Analytics event builder interface.
*
* Access via [`functions.analytics.event()`](functions.analytics#event).
*/
class AnalyticsEventBuilder {
/** @internal */
constructor(triggerResource, opts) {
this.triggerResource = triggerResource;
this.opts = opts;
}
/**
* Event handler that fires every time a Firebase Analytics event occurs.
*
* @param {!function(!functions.Event<!functions.analytics.AnalyticsEvent>)}
* handler Event handler that fires every time a Firebase Analytics event
* occurs.
*
* @return {!functions.CloudFunction<!functions.analytics.AnalyticsEvent>} A
* Cloud Function you can export.
*/
onLog(handler) {
const dataConstructor = (raw) => {
return new AnalyticsEvent(raw.data);
};
return cloud_functions_1.makeCloudFunction({
handler,
provider: exports.provider,
eventType: 'event.log',
service: exports.service,
legacyEventType: `providers/google.firebase.analytics/eventTypes/event.log`,
triggerResource: this.triggerResource,
dataConstructor,
opts: this.opts,
});
}
}
exports.AnalyticsEventBuilder = AnalyticsEventBuilder;
/**
* Interface representing a Firebase Analytics event that was logged for a specific user.
*/
class AnalyticsEvent {
/** @internal */
constructor(wireFormat) {
this.params = {}; // In case of absent field, show empty (not absent) map.
if (wireFormat.eventDim && wireFormat.eventDim.length > 0) {
// If there's an eventDim, there'll always be exactly one.
let eventDim = wireFormat.eventDim[0];
copyField(eventDim, this, 'name');
copyField(eventDim, this, 'params', p => _.mapValues(p, unwrapValue));
copyFieldTo(eventDim, this, 'valueInUsd', 'valueInUSD');
copyFieldTo(eventDim, this, 'date', 'reportingDate');
copyTimestampToString(eventDim, this, 'timestampMicros', 'logTime');
copyTimestampToString(eventDim, this, 'previousTimestampMicros', 'previousLogTime');
}
copyFieldTo(wireFormat, this, 'userDim', 'user', dim => new UserDimensions(dim));
}
}
exports.AnalyticsEvent = AnalyticsEvent;
/**
* Interface representing the user who triggered the events.
*/
class UserDimensions {
/** @internal */
constructor(wireFormat) {
// These are interfaces or primitives, no transformation needed.
copyFields(wireFormat, this, [
'userId',
'deviceInfo',
'geoInfo',
'appInfo',
]);
// The following fields do need transformations of some sort.
copyTimestampToString(wireFormat, this, 'firstOpenTimestampMicros', 'firstOpenTime');
this.userProperties = {}; // With no entries in the wire format, present an empty (as opposed to absent) map.
copyField(wireFormat, this, 'userProperties', r => _.mapValues(r, p => new UserPropertyValue(p)));
copyField(wireFormat, this, 'bundleInfo', r => new ExportBundleInfo(r));
// BUG(36000368) Remove when no longer necessary
/* tslint:disable:no-string-literal */
if (!this.userId && this.userProperties['user_id']) {
this.userId = this.userProperties['user_id'].value;
}
/* tslint:enable:no-string-literal */
}
}
exports.UserDimensions = UserDimensions;
/**
* Predefined or custom properties stored on the client side.
*/
class UserPropertyValue {
/** @internal */
constructor(wireFormat) {
copyField(wireFormat, this, 'value', unwrapValueAsString);
copyTimestampToString(wireFormat, this, 'setTimestampUsec', 'setTime');
}
}
exports.UserPropertyValue = UserPropertyValue;
/**
* Interface representing the bundle in which these events were uploaded.
*/
class ExportBundleInfo {
/** @internal */
constructor(wireFormat) {
copyField(wireFormat, this, 'bundleSequenceId');
copyTimestampToMillis(wireFormat, this, 'serverTimestampOffsetMicros', 'serverTimestampOffset');
}
}
exports.ExportBundleInfo = ExportBundleInfo;
function copyFieldTo(from, to, fromField, toField, transform = _.identity) {
if (from[fromField] !== undefined) {
to[toField] = transform(from[fromField]);
}
}
function copyField(from, to, field, transform = _.identity) {
copyFieldTo(from, to, field, field, transform);
}
function copyFields(from, to, fields) {
for (let field of fields) {
copyField(from, to, field);
}
}
// The incoming payload will have fields like:
// {
// 'myInt': {
// 'intValue': '123'
// },
// 'myDouble': {
// 'doubleValue': 1.0
// },
// 'myFloat': {
// 'floatValue': 1.1
// },
// 'myString': {
// 'stringValue': 'hi!'
// }
// }
//
// The following method will remove these four types of 'xValue' fields, flattening them
// to just their values, as a string:
// {
// 'myInt': '123',
// 'myDouble': '1.0',
// 'myFloat': '1.1',
// 'myString': 'hi!'
// }
//
// Note that while 'intValue' will have a quoted payload, 'doubleValue' and 'floatValue' will not. This
// is due to the encoding library, which renders int64 values as strings to avoid loss of precision. This
// method always returns a string, similarly to avoid loss of precision, unlike the less-conservative
// 'unwrapValue' method just below.
function unwrapValueAsString(wrapped) {
let key = _.keys(wrapped)[0];
return _.toString(wrapped[key]);
}
// Ditto as the method above, but returning the values in the idiomatic JavaScript type (string for strings,
// number for numbers):
// {
// 'myInt': 123,
// 'myDouble': 1.0,
// 'myFloat': 1.1,
// 'myString': 'hi!'
// }
//
// The field names in the incoming xValue fields identify the type a value has, which for JavaScript's
// purposes can be divided into 'number' versus 'string'. This method will render all the numbers as
// JavaScript's 'number' type, since we prefer using idiomatic types. Note that this may lead to loss
// in precision for int64 fields, so use with care.
const xValueNumberFields = ['intValue', 'floatValue', 'doubleValue'];
function unwrapValue(wrapped) {
let key = _.keys(wrapped)[0];
let value = unwrapValueAsString(wrapped);
return _.includes(xValueNumberFields, key) ? _.toNumber(value) : value;
}
// The JSON payload delivers timestamp fields as strings of timestamps denoted in microseconds.
// The JavaScript convention is to use numbers denoted in milliseconds. This method
// makes it easy to convert a field of one type into the other.
function copyTimestampToMillis(from, to, fromName, toName) {
if (from[fromName] !== undefined) {
to[toName] = _.round(from[fromName] / 1000);
}
}
// The JSON payload delivers timestamp fields as strings of timestamps denoted in microseconds.
// In our SDK, we'd like to present timestamp as ISO-format strings. This method makes it easy
// to convert a field of one type into the other.
function copyTimestampToString(from, to, fromName, toName) {
if (from[fromName] !== undefined) {
to[toName] = new Date(from[fromName] / 1000).toISOString();
}
}

View File

@@ -0,0 +1,33 @@
import { CloudFunction, EventContext } from '../cloud-functions';
import * as firebase from 'firebase-admin';
/**
* Handle events related to Firebase authentication users.
*/
export declare function user(): UserBuilder;
export declare class UserRecordMetadata implements firebase.auth.UserMetadata {
creationTime: string;
lastSignInTime: string;
constructor(creationTime: string, lastSignInTime: string);
/** Returns a plain JavaScript object with the properties of UserRecordMetadata. */
toJSON(): {
creationTime: string;
lastSignInTime: string;
};
}
/** Builder used to create Cloud Functions for Firebase Auth user lifecycle events. */
export declare class UserBuilder {
private triggerResource;
private opts;
private static dataConstructor(raw);
/** Respond to the creation of a Firebase Auth user. */
onCreate(handler: (user: UserRecord, context: EventContext) => PromiseLike<any> | any): CloudFunction<UserRecord>;
/** Respond to the deletion of a Firebase Auth user. */
onDelete(handler: (user: UserRecord, context: EventContext) => PromiseLike<any> | any): CloudFunction<UserRecord>;
private onOperation(handler, eventType);
}
/**
* The UserRecord passed to Cloud Functions is the same UserRecord that is returned by the Firebase Admin
* SDK.
*/
export declare type UserRecord = firebase.auth.UserRecord;
export declare function userRecordConstructor(wireData: Object): firebase.auth.UserRecord;

View File

@@ -0,0 +1,143 @@
"use strict";
// The MIT License (MIT)
//
// Copyright (c) 2017 Firebase
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
Object.defineProperty(exports, "__esModule", { value: true });
const cloud_functions_1 = require("../cloud-functions");
const _ = require("lodash");
/** @internal */
exports.provider = 'google.firebase.auth';
/** @internal */
exports.service = 'firebaseauth.googleapis.com';
/**
* Handle events related to Firebase authentication users.
*/
function user() {
return _userWithOpts({});
}
exports.user = user;
/** @internal */
function _userWithOpts(opts) {
return new UserBuilder(() => {
if (!process.env.GCLOUD_PROJECT) {
throw new Error('process.env.GCLOUD_PROJECT is not set.');
}
return 'projects/' + process.env.GCLOUD_PROJECT;
}, opts);
}
exports._userWithOpts = _userWithOpts;
class UserRecordMetadata {
constructor(creationTime, lastSignInTime) {
this.creationTime = creationTime;
this.lastSignInTime = lastSignInTime;
}
/** Returns a plain JavaScript object with the properties of UserRecordMetadata. */
toJSON() {
return {
creationTime: this.creationTime,
lastSignInTime: this.lastSignInTime,
};
}
}
exports.UserRecordMetadata = UserRecordMetadata;
/** Builder used to create Cloud Functions for Firebase Auth user lifecycle events. */
class UserBuilder {
/** @internal */
constructor(triggerResource, opts) {
this.triggerResource = triggerResource;
this.opts = opts;
}
static dataConstructor(raw) {
return userRecordConstructor(raw.data);
}
/** Respond to the creation of a Firebase Auth user. */
onCreate(handler) {
return this.onOperation(handler, 'user.create');
}
/** Respond to the deletion of a Firebase Auth user. */
onDelete(handler) {
return this.onOperation(handler, 'user.delete');
}
onOperation(handler, eventType) {
return cloud_functions_1.makeCloudFunction({
handler,
provider: exports.provider,
eventType,
service: exports.service,
triggerResource: this.triggerResource,
dataConstructor: UserBuilder.dataConstructor,
legacyEventType: `providers/firebase.auth/eventTypes/${eventType}`,
opts: this.opts,
});
}
}
exports.UserBuilder = UserBuilder;
function userRecordConstructor(wireData) {
// Falsey values from the wire format proto get lost when converted to JSON, this adds them back.
let falseyValues = {
email: null,
emailVerified: false,
displayName: null,
photoURL: null,
phoneNumber: null,
disabled: false,
providerData: [],
customClaims: {},
passwordSalt: null,
passwordHash: null,
tokensValidAfterTime: null,
};
let record = _.assign({}, falseyValues, wireData);
let meta = _.get(record, 'metadata');
if (meta) {
_.set(record, 'metadata', new UserRecordMetadata(
// Transform payload to firebase-admin v5.0.0 format because wire format is different (BUG 63167395)
meta.createdAt || meta.creationTime, meta.lastSignedInAt || meta.lastSignInTime));
}
else {
_.set(record, 'metadata', new UserRecordMetadata(null, null));
}
_.forEach(record.providerData, entry => {
_.set(entry, 'toJSON', () => {
return entry;
});
});
_.set(record, 'toJSON', () => {
const json = _.pick(record, [
'uid',
'email',
'emailVerified',
'displayName',
'photoURL',
'phoneNumber',
'disabled',
'passwordHash',
'passwordSalt',
'tokensValidAfterTime',
]);
json.metadata = _.get(record, 'metadata').toJSON();
json.customClaims = _.cloneDeep(record.customClaims);
json.providerData = _.map(record.providerData, entry => entry.toJSON());
return json;
});
return record;
}
exports.userRecordConstructor = userRecordConstructor;

View File

@@ -0,0 +1,57 @@
import { CloudFunction, EventContext } from '../cloud-functions';
/**
* Handle events related to Crashlytics issues. An issue in Crashlytics is an
* aggregation of crashes which have a shared root cause.
*/
export declare function issue(): IssueBuilder;
/** Builder used to create Cloud Functions for Crashlytics issue events. */
export declare class IssueBuilder {
private triggerResource;
private opts;
/** Handle Crashlytics New Issue events. */
onNew(handler: (issue: Issue, context: EventContext) => PromiseLike<any> | any): CloudFunction<Issue>;
/** Handle Crashlytics Regressed Issue events. */
onRegressed(handler: (issue: Issue, context: EventContext) => PromiseLike<any> | any): CloudFunction<Issue>;
/** Handle Crashlytics Velocity Alert events. */
onVelocityAlert(handler: (issue: Issue, context: EventContext) => PromiseLike<any> | any): CloudFunction<Issue>;
private onEvent(handler, eventType);
}
/**
* Interface representing a Crashlytics issue event that was logged for a specific issue.
*/
export interface Issue {
/** Fabric Issue ID. */
issueId: string;
/** Issue title. */
issueTitle: string;
/** App information. */
appInfo: AppInfo;
/** When the issue was created (ISO8601 time stamp). */
createTime: string;
/** When the issue was resolved, if the issue has been resolved (ISO8601 time stamp). */
resolvedTime?: string;
/** Contains details about the velocity alert, if this event was triggered by a velocity alert. */
velocityAlert?: VelocityAlert;
}
export interface VelocityAlert {
/** The percentage of sessions which have been impacted by this issue. Example: .04 */
crashPercentage: number;
/** The number of crashes that this issue has caused. */
crashes: number;
}
/**
* Interface representing the application where this issue occurred.
*/
export interface AppInfo {
/** The app's name. Example: "My Awesome App". */
appName: string;
/** The app's platform. Examples: "android", "ios". */
appPlatform: string;
/** Unique application identifier within an app store, either the Android package name or the iOS bundle id. */
appId: string;
/**
* The latest app version which is affected by the issue.
* Examples: "1.0", "4.3.1.1.213361", "2.3 (1824253)", "v1.8b22p6".
*/
latestAppVersion: string;
}

View File

@@ -0,0 +1,82 @@
"use strict";
// The MIT License (MIT)
//
// Copyright (c) 2017 Firebase
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
Object.defineProperty(exports, "__esModule", { value: true });
const cloud_functions_1 = require("../cloud-functions");
/** @internal */
exports.provider = 'google.firebase.crashlytics';
/** @internal */
exports.service = 'fabric.io';
/**
* Handle events related to Crashlytics issues. An issue in Crashlytics is an
* aggregation of crashes which have a shared root cause.
*/
function issue() {
return _issueWithOpts({});
}
exports.issue = issue;
/** @internal */
function _issueWithOpts(opts) {
return new IssueBuilder(() => {
if (!process.env.GCLOUD_PROJECT) {
throw new Error('process.env.GCLOUD_PROJECT is not set.');
}
return 'projects/' + process.env.GCLOUD_PROJECT;
}, opts);
}
exports._issueWithOpts = _issueWithOpts;
/** Builder used to create Cloud Functions for Crashlytics issue events. */
class IssueBuilder {
/** @internal */
constructor(triggerResource, opts) {
this.triggerResource = triggerResource;
this.opts = opts;
}
/** @internal */
onNewDetected(handler) {
throw new Error('"onNewDetected" is now deprecated, please use "onNew"');
}
/** Handle Crashlytics New Issue events. */
onNew(handler) {
return this.onEvent(handler, 'issue.new');
}
/** Handle Crashlytics Regressed Issue events. */
onRegressed(handler) {
return this.onEvent(handler, 'issue.regressed');
}
/** Handle Crashlytics Velocity Alert events. */
onVelocityAlert(handler) {
return this.onEvent(handler, 'issue.velocityAlert');
}
onEvent(handler, eventType) {
return cloud_functions_1.makeCloudFunction({
handler,
provider: exports.provider,
eventType,
service: exports.service,
legacyEventType: `providers/firebase.crashlytics/eventTypes/${eventType}`,
triggerResource: this.triggerResource,
opts: this.opts,
});
}
}
exports.IssueBuilder = IssueBuilder;

View File

@@ -0,0 +1,82 @@
import { CloudFunction, EventContext, Change } from '../cloud-functions';
import * as firebase from 'firebase-admin';
/**
* Selects a database instance that will trigger the function.
* If omitted, will pick the default database for your project.
* @param instance The Realtime Database instance to use.
*/
export declare function instance(instance: string): InstanceBuilder;
/**
* Select Firebase Realtime Database Reference to listen to.
*
* This method behaves very similarly to the method of the same name in the
* client and Admin Firebase SDKs. Any change to the Database that affects the
* data at or below the provided `path` will fire an event in Cloud Functions.
*
* There are three important differences between listening to a Realtime
* Database event in Cloud Functions and using the Realtime Database in the
* client and Admin SDKs:
* 1. Cloud Functions allows wildcards in the `path` name. Any `path` component
* in curly brackets (`{}`) is a wildcard that matches all strings. The value
* that matched a certain invocation of a Cloud Function is returned as part
* of the `context.params` object. For example, `ref("messages/{messageId}")`
* matches changes at `/messages/message1` or `/messages/message2`, resulting
* in `context.params.messageId` being set to `"message1"` or `"message2"`,
* respectively.
* 2. Cloud Functions do not fire an event for data that already existed before
* the Cloud Function was deployed.
* 3. Cloud Function events have access to more information, including information
* about the user who triggered the Cloud Function.
* @param ref Path of the database to listen to.
*/
export declare function ref(path: string): RefBuilder;
export declare class InstanceBuilder {
private instance;
private opts;
ref(path: string): RefBuilder;
}
/** Builder used to create Cloud Functions for Firebase Realtime Database References. */
export declare class RefBuilder {
private apps;
private triggerResource;
private opts;
/** Respond to any write that affects a ref. */
onWrite(handler: (change: Change<DataSnapshot>, context: EventContext) => PromiseLike<any> | any): CloudFunction<Change<DataSnapshot>>;
/** Respond to update on a ref. */
onUpdate(handler: (change: Change<DataSnapshot>, context: EventContext) => PromiseLike<any> | any): CloudFunction<Change<DataSnapshot>>;
/** Respond to new data on a ref. */
onCreate(handler: (snapshot: DataSnapshot, context: EventContext) => PromiseLike<any> | any): CloudFunction<DataSnapshot>;
/** Respond to all data being deleted from a ref. */
onDelete(handler: (snapshot: DataSnapshot, context: EventContext) => PromiseLike<any> | any): CloudFunction<DataSnapshot>;
private onOperation<T>(handler, eventType, dataConstructor);
private changeConstructor;
}
export declare class DataSnapshot {
private app;
instance: string;
private _ref;
private _path;
private _data;
private _childPath;
constructor(data: any, path?: string, app?: firebase.app.App, instance?: string);
/** Ref returns a reference to the database with full admin access. */
readonly ref: firebase.database.Reference;
readonly key: string;
val(): any;
exportVal(): any;
getPriority(): string | number | null;
exists(): boolean;
child(childPath: string): DataSnapshot;
forEach(action: (a: DataSnapshot) => boolean): boolean;
hasChild(childPath: string): boolean;
hasChildren(): boolean;
numChildren(): number;
/**
* Prints the value of the snapshot; use '.previous.toJSON()' and '.current.toJSON()' to explicitly see
* the previous and current values of the snapshot.
*/
toJSON(): Object;
private _checkAndConvertToArray(node);
private _dup(childPath?);
private _fullPath();
}

View File

@@ -0,0 +1,315 @@
"use strict";
// The MIT License (MIT)
//
// Copyright (c) 2017 Firebase
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
Object.defineProperty(exports, "__esModule", { value: true });
const _ = require("lodash");
const apps_1 = require("../apps");
const cloud_functions_1 = require("../cloud-functions");
const utils_1 = require("../utils");
const config_1 = require("../config");
/** @internal */
exports.provider = 'google.firebase.database';
/** @internal */
exports.service = 'firebaseio.com';
// NOTE(inlined): Should we relax this a bit to allow staging or alternate implementations of our API?
const databaseURLRegex = new RegExp('https://([^.]+).firebaseio.com');
/**
* Selects a database instance that will trigger the function.
* If omitted, will pick the default database for your project.
* @param instance The Realtime Database instance to use.
*/
function instance(instance) {
return _instanceWithOpts(instance, {});
}
exports.instance = instance;
/**
* Select Firebase Realtime Database Reference to listen to.
*
* This method behaves very similarly to the method of the same name in the
* client and Admin Firebase SDKs. Any change to the Database that affects the
* data at or below the provided `path` will fire an event in Cloud Functions.
*
* There are three important differences between listening to a Realtime
* Database event in Cloud Functions and using the Realtime Database in the
* client and Admin SDKs:
* 1. Cloud Functions allows wildcards in the `path` name. Any `path` component
* in curly brackets (`{}`) is a wildcard that matches all strings. The value
* that matched a certain invocation of a Cloud Function is returned as part
* of the `context.params` object. For example, `ref("messages/{messageId}")`
* matches changes at `/messages/message1` or `/messages/message2`, resulting
* in `context.params.messageId` being set to `"message1"` or `"message2"`,
* respectively.
* 2. Cloud Functions do not fire an event for data that already existed before
* the Cloud Function was deployed.
* 3. Cloud Function events have access to more information, including information
* about the user who triggered the Cloud Function.
* @param ref Path of the database to listen to.
*/
function ref(path) {
return _refWithOpts(path, {});
}
exports.ref = ref;
/** @internal */
function _instanceWithOpts(instance, opts) {
return new InstanceBuilder(instance, opts);
}
exports._instanceWithOpts = _instanceWithOpts;
class InstanceBuilder {
/* @internal */
constructor(instance, opts) {
this.instance = instance;
this.opts = opts;
}
ref(path) {
const normalized = utils_1.normalizePath(path);
return new RefBuilder(apps_1.apps(), () => `projects/_/instances/${this.instance}/refs/${normalized}`, this.opts);
}
}
exports.InstanceBuilder = InstanceBuilder;
/** @internal */
function _refWithOpts(path, opts) {
const resourceGetter = () => {
const normalized = utils_1.normalizePath(path);
const databaseURL = config_1.firebaseConfig().databaseURL;
if (!databaseURL) {
throw new Error('Missing expected firebase config value databaseURL, ' +
'config is actually' +
JSON.stringify(config_1.firebaseConfig()) +
'\n If you are unit testing, please set process.env.FIREBASE_CONFIG');
}
const match = databaseURL.match(databaseURLRegex);
if (!match) {
throw new Error('Invalid value for config firebase.databaseURL: ' + databaseURL);
}
const subdomain = match[1];
return `projects/_/instances/${subdomain}/refs/${normalized}`;
};
return new RefBuilder(apps_1.apps(), resourceGetter, opts);
}
exports._refWithOpts = _refWithOpts;
/** Builder used to create Cloud Functions for Firebase Realtime Database References. */
class RefBuilder {
/** @internal */
constructor(apps, triggerResource, opts) {
this.apps = apps;
this.triggerResource = triggerResource;
this.opts = opts;
this.changeConstructor = (raw) => {
let [dbInstance, path] = resourceToInstanceAndPath(raw.context.resource.name);
let before = new DataSnapshot(raw.data.data, path, this.apps.admin, dbInstance);
let after = new DataSnapshot(utils_1.applyChange(raw.data.data, raw.data.delta), path, this.apps.admin, dbInstance);
return {
before: before,
after: after,
};
};
}
/** Respond to any write that affects a ref. */
onWrite(handler) {
return this.onOperation(handler, 'ref.write', this.changeConstructor);
}
/** Respond to update on a ref. */
onUpdate(handler) {
return this.onOperation(handler, 'ref.update', this.changeConstructor);
}
/** Respond to new data on a ref. */
onCreate(handler) {
let dataConstructor = (raw) => {
let [dbInstance, path] = resourceToInstanceAndPath(raw.context.resource.name);
return new DataSnapshot(raw.data.delta, path, this.apps.admin, dbInstance);
};
return this.onOperation(handler, 'ref.create', dataConstructor);
}
/** Respond to all data being deleted from a ref. */
onDelete(handler) {
let dataConstructor = (raw) => {
let [dbInstance, path] = resourceToInstanceAndPath(raw.context.resource.name);
return new DataSnapshot(raw.data.data, path, this.apps.admin, dbInstance);
};
return this.onOperation(handler, 'ref.delete', dataConstructor);
}
onOperation(handler, eventType, dataConstructor) {
return cloud_functions_1.makeCloudFunction({
handler,
provider: exports.provider,
service: exports.service,
eventType,
legacyEventType: `providers/${exports.provider}/eventTypes/${eventType}`,
triggerResource: this.triggerResource,
dataConstructor: dataConstructor,
before: event => this.apps.retain(),
after: event => this.apps.release(),
opts: this.opts,
});
}
}
exports.RefBuilder = RefBuilder;
/* Utility function to extract database reference from resource string */
/** @internal */
function resourceToInstanceAndPath(resource) {
let resourceRegex = `projects/([^/]+)/instances/([^/]+)/refs(/.+)?`;
let match = resource.match(new RegExp(resourceRegex));
if (!match) {
throw new Error(`Unexpected resource string for Firebase Realtime Database event: ${resource}. ` +
'Expected string in the format of "projects/_/instances/{firebaseioSubdomain}/refs/{ref=**}"');
}
let [, project, dbInstanceName, path] = match;
if (project !== '_') {
throw new Error(`Expect project to be '_' in a Firebase Realtime Database event`);
}
let dbInstance = 'https://' + dbInstanceName + '.firebaseio.com';
return [dbInstance, path];
}
exports.resourceToInstanceAndPath = resourceToInstanceAndPath;
class DataSnapshot {
constructor(data, path, // path will be undefined for the database root
app, instance) {
this.app = app;
if (instance) {
// SDK always supplies instance, but user's unit tests may not
this.instance = instance;
}
else if (app) {
this.instance = app.options.databaseURL;
}
else if (process.env.GCLOUD_PROJECT) {
this.instance =
'https://' + process.env.GCLOUD_PROJECT + '.firebaseio.com';
}
this._path = path;
this._data = data;
}
/** Ref returns a reference to the database with full admin access. */
get ref() {
if (!this.app) {
// may be unpopulated in user's unit tests
throw new Error('Please supply a Firebase app in the constructor for DataSnapshot' +
' in order to use the .ref method.');
}
if (!this._ref) {
this._ref = this.app.database(this.instance).ref(this._fullPath());
}
return this._ref;
}
get key() {
let last = _.last(utils_1.pathParts(this._fullPath()));
return !last || last === '' ? null : last;
}
val() {
let parts = utils_1.pathParts(this._childPath);
let source = this._data;
let node = _.cloneDeep(parts.length ? _.get(source, parts, null) : source);
return this._checkAndConvertToArray(node);
}
// TODO(inlined): figure out what to do here
exportVal() {
return this.val();
}
// TODO(inlined): figure out what to do here
getPriority() {
return 0;
}
exists() {
return !_.isNull(this.val());
}
child(childPath) {
if (!childPath) {
return this;
}
return this._dup(childPath);
}
forEach(action) {
let val = this.val();
if (_.isPlainObject(val)) {
return _.some(val, (value, key) => action(this.child(key)) === true);
}
return false;
}
hasChild(childPath) {
return this.child(childPath).exists();
}
hasChildren() {
let val = this.val();
return _.isPlainObject(val) && _.keys(val).length > 0;
}
numChildren() {
let val = this.val();
return _.isPlainObject(val) ? Object.keys(val).length : 0;
}
/**
* Prints the value of the snapshot; use '.previous.toJSON()' and '.current.toJSON()' to explicitly see
* the previous and current values of the snapshot.
*/
toJSON() {
return this.val();
}
/* Recursive function to check if keys are numeric & convert node object to array if they are */
_checkAndConvertToArray(node) {
if (node === null || typeof node === 'undefined') {
return null;
}
if (typeof node !== 'object') {
return node;
}
let obj = {};
let numKeys = 0;
let maxKey = 0;
let allIntegerKeys = true;
for (let key in node) {
if (!node.hasOwnProperty(key)) {
continue;
}
let childNode = node[key];
obj[key] = this._checkAndConvertToArray(childNode);
numKeys++;
const integerRegExp = /^(0|[1-9]\d*)$/;
if (allIntegerKeys && integerRegExp.test(key)) {
maxKey = Math.max(maxKey, Number(key));
}
else {
allIntegerKeys = false;
}
}
if (allIntegerKeys && maxKey < 2 * numKeys) {
// convert to array.
let array = [];
_.forOwn(obj, (val, key) => {
array[key] = val;
});
return array;
}
return obj;
}
_dup(childPath) {
let dup = new DataSnapshot(this._data, undefined, this.app, this.instance);
[dup._path, dup._childPath] = [this._path, this._childPath];
if (childPath) {
dup._childPath = utils_1.joinPath(dup._childPath, childPath);
}
return dup;
}
_fullPath() {
let out = (this._path || '') + '/' + (this._childPath || '');
return out;
}
}
exports.DataSnapshot = DataSnapshot;

View File

@@ -0,0 +1,36 @@
import * as firebase from 'firebase-admin';
import { CloudFunction, Change, EventContext } from '../cloud-functions';
export declare type DocumentSnapshot = firebase.firestore.DocumentSnapshot;
/**
* Select the Firestore document to listen to for events.
* @param path Full database path to listen to. This includes the name of
* the collection that the document is a part of. For example, if the
* collection is named "users" and the document is named "Ada", then the
* path is "/users/Ada".
*/
export declare function document(path: string): DocumentBuilder;
export declare class DatabaseBuilder {
private database;
private opts;
namespace(namespace: string): NamespaceBuilder;
document(path: string): DocumentBuilder;
}
export declare class NamespaceBuilder {
private database;
private opts;
private namespace;
document(path: string): DocumentBuilder;
}
export declare class DocumentBuilder {
private triggerResource;
private opts;
/** Respond to all document writes (creates, updates, or deletes). */
onWrite(handler: (change: Change<DocumentSnapshot>, context: EventContext) => PromiseLike<any> | any): CloudFunction<Change<DocumentSnapshot>>;
/** Respond only to document updates. */
onUpdate(handler: (change: Change<DocumentSnapshot>, context: EventContext) => PromiseLike<any> | any): CloudFunction<Change<DocumentSnapshot>>;
/** Respond only to document creations. */
onCreate(handler: (snapshot: DocumentSnapshot, context: EventContext) => PromiseLike<any> | any): CloudFunction<DocumentSnapshot>;
/** Respond only to document deletions. */
onDelete(handler: (snapshot: DocumentSnapshot, context: EventContext) => PromiseLike<any> | any): CloudFunction<DocumentSnapshot>;
private onOperation<T>(handler, eventType, dataConstructor);
}

View File

@@ -0,0 +1,182 @@
"use strict";
// The MIT License (MIT)
//
// Copyright (c) 2017 Firebase
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = require("path");
const _ = require("lodash");
const firebase = require("firebase-admin");
const apps_1 = require("../apps");
const cloud_functions_1 = require("../cloud-functions");
const encoder_1 = require("../encoder");
/** @internal */
exports.provider = 'google.firestore';
/** @internal */
exports.service = 'firestore.googleapis.com';
/** @internal */
exports.defaultDatabase = '(default)';
let firestoreInstance;
/**
* Select the Firestore document to listen to for events.
* @param path Full database path to listen to. This includes the name of
* the collection that the document is a part of. For example, if the
* collection is named "users" and the document is named "Ada", then the
* path is "/users/Ada".
*/
function document(path) {
return _documentWithOpts(path, {});
}
exports.document = document;
/** @internal */
// Multiple namespaces are not yet supported by Firestore.
function namespace(namespace) {
return _namespaceWithOpts(namespace, {});
}
exports.namespace = namespace;
/** @internal */
// Multiple databases are not yet supported by Firestore.
function database(database) {
return _databaseWithOpts(database, {});
}
exports.database = database;
/** @internal */
function _databaseWithOpts(database = exports.defaultDatabase, opts) {
return new DatabaseBuilder(database, opts);
}
exports._databaseWithOpts = _databaseWithOpts;
/** @internal */
function _namespaceWithOpts(namespace, opts) {
return _databaseWithOpts(exports.defaultDatabase, opts).namespace(namespace);
}
exports._namespaceWithOpts = _namespaceWithOpts;
/** @internal */
function _documentWithOpts(path, opts) {
return _databaseWithOpts(exports.defaultDatabase, opts).document(path);
}
exports._documentWithOpts = _documentWithOpts;
class DatabaseBuilder {
/** @internal */
constructor(database, opts) {
this.database = database;
this.opts = opts;
}
namespace(namespace) {
return new NamespaceBuilder(this.database, this.opts, namespace);
}
document(path) {
return new NamespaceBuilder(this.database, this.opts).document(path);
}
}
exports.DatabaseBuilder = DatabaseBuilder;
class NamespaceBuilder {
/** @internal */
constructor(database, opts, namespace) {
this.database = database;
this.opts = opts;
this.namespace = namespace;
}
document(path) {
return new DocumentBuilder(() => {
if (!process.env.GCLOUD_PROJECT) {
throw new Error('process.env.GCLOUD_PROJECT is not set.');
}
let database = path_1.posix.join('projects', process.env.GCLOUD_PROJECT, 'databases', this.database);
return path_1.posix.join(database, this.namespace ? `documents@${this.namespace}` : 'documents', path);
}, this.opts);
}
}
exports.NamespaceBuilder = NamespaceBuilder;
function _getValueProto(data, resource, valueFieldName) {
if (_.isEmpty(_.get(data, valueFieldName))) {
// Firestore#snapshot_ takes resource string instead of proto for a non-existent snapshot
return resource;
}
let proto = {
fields: _.get(data, [valueFieldName, 'fields'], {}),
createTime: encoder_1.dateToTimestampProto(_.get(data, [valueFieldName, 'createTime'])),
updateTime: encoder_1.dateToTimestampProto(_.get(data, [valueFieldName, 'updateTime'])),
name: _.get(data, [valueFieldName, 'name'], resource),
};
return proto;
}
/** @internal */
function snapshotConstructor(event) {
if (!firestoreInstance) {
firestoreInstance = firebase.firestore(apps_1.apps().admin);
firestoreInstance.settings({ timestampsInSnapshots: true });
}
let valueProto = _getValueProto(event.data, event.context.resource.name, 'value');
let readTime = encoder_1.dateToTimestampProto(_.get(event, 'data.value.readTime'));
return firestoreInstance.snapshot_(valueProto, readTime, 'json');
}
exports.snapshotConstructor = snapshotConstructor;
/** @internal */
// TODO remove this function when wire format changes to new format
function beforeSnapshotConstructor(event) {
if (!firestoreInstance) {
firestoreInstance = firebase.firestore(apps_1.apps().admin);
firestoreInstance.settings({ timestampsInSnapshots: true });
}
let oldValueProto = _getValueProto(event.data, event.context.resource.name, 'oldValue');
let oldReadTime = encoder_1.dateToTimestampProto(_.get(event, 'data.oldValue.readTime'));
return firestoreInstance.snapshot_(oldValueProto, oldReadTime, 'json');
}
exports.beforeSnapshotConstructor = beforeSnapshotConstructor;
function changeConstructor(raw) {
return cloud_functions_1.Change.fromObjects(beforeSnapshotConstructor(raw), snapshotConstructor(raw));
}
class DocumentBuilder {
/** @internal */
constructor(triggerResource, opts) {
this.triggerResource = triggerResource;
this.opts = opts;
// TODO what validation do we want to do here?
}
/** Respond to all document writes (creates, updates, or deletes). */
onWrite(handler) {
return this.onOperation(handler, 'document.write', changeConstructor);
}
/** Respond only to document updates. */
onUpdate(handler) {
return this.onOperation(handler, 'document.update', changeConstructor);
}
/** Respond only to document creations. */
onCreate(handler) {
return this.onOperation(handler, 'document.create', snapshotConstructor);
}
/** Respond only to document deletions. */
onDelete(handler) {
return this.onOperation(handler, 'document.delete', beforeSnapshotConstructor);
}
onOperation(handler, eventType, dataConstructor) {
return cloud_functions_1.makeCloudFunction({
handler,
provider: exports.provider,
eventType,
service: exports.service,
triggerResource: this.triggerResource,
legacyEventType: `providers/cloud.firestore/eventTypes/${eventType}`,
dataConstructor,
opts: this.opts,
});
}
}
exports.DocumentBuilder = DocumentBuilder;

View File

@@ -0,0 +1,91 @@
/// <reference types="express" />
import * as express from 'express';
import * as firebase from 'firebase-admin';
import { HttpsFunction, Runnable } from '../cloud-functions';
/**
* Handle HTTP requests.
* @param handler A function that takes a request and response object,
* same signature as an Express app.
*/
export declare function onRequest(handler: (req: express.Request, resp: express.Response) => void): HttpsFunction;
/**
* Declares a callable method for clients to call using a Firebase SDK.
* @param handler A method that takes a data and context and returns a value.
*/
export declare function onCall(handler: (data: any, context: CallableContext) => any | Promise<any>): HttpsFunction & Runnable<any>;
/**
* The set of Firebase Functions status codes. The codes are the same at the
* ones exposed by gRPC here:
* https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
*
* Possible values:
* - 'cancelled': The operation was cancelled (typically by the caller).
* - 'unknown': Unknown error or an error from a different error domain.
* - 'invalid-argument': Client specified an invalid argument. Note that this
* differs from 'failed-precondition'. 'invalid-argument' indicates
* arguments that are problematic regardless of the state of the system
* (e.g. an invalid field name).
* - 'deadline-exceeded': Deadline expired before operation could complete.
* For operations that change the state of the system, this error may be
* returned even if the operation has completed successfully. For example,
* a successful response from a server could have been delayed long enough
* for the deadline to expire.
* - 'not-found': Some requested document was not found.
* - 'already-exists': Some document that we attempted to create already
* exists.
* - 'permission-denied': The caller does not have permission to execute the
* specified operation.
* - 'resource-exhausted': Some resource has been exhausted, perhaps a
* per-user quota, or perhaps the entire file system is out of space.
* - 'failed-precondition': Operation was rejected because the system is not
* in a state required for the operation's execution.
* - 'aborted': The operation was aborted, typically due to a concurrency
* issue like transaction aborts, etc.
* - 'out-of-range': Operation was attempted past the valid range.
* - 'unimplemented': Operation is not implemented or not supported/enabled.
* - 'internal': Internal errors. Means some invariants expected by
* underlying system has been broken. If you see one of these errors,
* something is very broken.
* - 'unavailable': The service is currently unavailable. This is most likely
* a transient condition and may be corrected by retrying with a backoff.
* - 'data-loss': Unrecoverable data loss or corruption.
* - 'unauthenticated': The request does not have valid authentication
* credentials for the operation.
*/
export declare type FunctionsErrorCode = 'ok' | 'cancelled' | 'unknown' | 'invalid-argument' | 'deadline-exceeded' | 'not-found' | 'already-exists' | 'permission-denied' | 'resource-exhausted' | 'failed-precondition' | 'aborted' | 'out-of-range' | 'unimplemented' | 'internal' | 'unavailable' | 'data-loss' | 'unauthenticated';
/**
* An explicit error that can be thrown from a handler to send an error to the
* client that called the function.
*/
export declare class HttpsError extends Error {
/**
* A standard error code that will be returned to the client. This also
* determines the HTTP status code of the response, as defined in code.proto.
*/
readonly code: FunctionsErrorCode;
/**
* Extra data to be converted to JSON and included in the error response.
*/
readonly details?: any;
constructor(code: FunctionsErrorCode, message?: string, details?: any);
}
/**
* The interface for metadata for the API as passed to the handler.
*/
export interface CallableContext {
/**
* The result of decoding and verifying a Firebase Auth ID token.
*/
auth?: {
uid: string;
token: firebase.auth.DecodedIdToken;
};
/**
* An unverified token for a Firebase Instance ID.
*/
instanceIdToken?: string;
/**
* The raw request handled by the callable.
*/
rawRequest: express.Request;
}

View File

@@ -0,0 +1,359 @@
"use strict";
// The MIT License (MIT)
//
// Copyright (c) 2017 Firebase
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const _ = require("lodash");
const cors = require("cors");
const apps_1 = require("../apps");
const cloud_functions_1 = require("../cloud-functions");
/**
* Handle HTTP requests.
* @param handler A function that takes a request and response object,
* same signature as an Express app.
*/
function onRequest(handler) {
return _onRequestWithOpts(handler, {});
}
exports.onRequest = onRequest;
/**
* Declares a callable method for clients to call using a Firebase SDK.
* @param handler A method that takes a data and context and returns a value.
*/
function onCall(handler) {
return _onCallWithOpts(handler, {});
}
exports.onCall = onCall;
/** @internal */
function _onRequestWithOpts(handler, opts) {
// lets us add __trigger without altering handler:
let cloudFunction = (req, res) => {
handler(req, res);
};
cloudFunction.__trigger = _.assign(cloud_functions_1.optsToTrigger(opts), { httpsTrigger: {} });
// TODO parse the opts
return cloudFunction;
}
exports._onRequestWithOpts = _onRequestWithOpts;
/**
* Standard error codes for different ways a request can fail, as defined by:
* https://github.com/googleapis/googleapis/blob/master/google/rpc/code.proto
*
* This map is used primarily to convert from a client error code string to
* to the HTTP format error code string, and make sure it's in the supported set.
*/
const errorCodeMap = {
ok: 'OK',
cancelled: 'CANCELLED',
unknown: 'UNKNOWN',
'invalid-argument': 'INVALID_ARGUMENT',
'deadline-exceeded': 'DEADLINE_EXCEEDED',
'not-found': 'NOT_FOUND',
'already-exists': 'ALREADY_EXISTS',
'permission-denied': 'PERMISSION_DENIED',
unauthenticated: 'UNAUTHENTICATED',
'resource-exhausted': 'RESOURCE_EXHAUSTED',
'failed-precondition': 'FAILED_PRECONDITION',
aborted: 'ABORTED',
'out-of-range': 'OUT_OF_RANGE',
unimplemented: 'UNIMPLEMENTED',
internal: 'INTERNAL',
unavailable: 'UNAVAILABLE',
'data-loss': 'DATA_LOSS',
};
/**
* An explicit error that can be thrown from a handler to send an error to the
* client that called the function.
*/
class HttpsError extends Error {
constructor(code, message, details) {
super(message);
// This is a workaround for a bug in TypeScript when extending Error:
// tslint:disable-next-line
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
Object.setPrototypeOf(this, HttpsError.prototype);
if (!errorCodeMap[code]) {
throw new Error('Unknown error status: ' + code);
}
this.code = code;
this.details = details;
}
/**
* @internal
* A string representation of the Google error code for this error for HTTP.
*/
get status() {
return errorCodeMap[this.code];
}
/**
* @internal
* Returns the canonical http status code for the given error.
*/
get httpStatus() {
switch (this.code) {
case 'ok':
return 200;
case 'cancelled':
return 499;
case 'unknown':
return 500;
case 'invalid-argument':
return 400;
case 'deadline-exceeded':
return 504;
case 'not-found':
return 404;
case 'already-exists':
return 409;
case 'permission-denied':
return 403;
case 'unauthenticated':
return 401;
case 'resource-exhausted':
return 429;
case 'failed-precondition':
return 400;
case 'aborted':
return 409;
case 'out-of-range':
return 400;
case 'unimplemented':
return 501;
case 'internal':
return 500;
case 'unavailable':
return 503;
case 'data-loss':
return 500;
// This should never happen as long as the type system is doing its job.
default:
throw 'Invalid error code: ' + this.code;
}
}
/** @internal */
toJSON() {
const json = {
status: this.status,
message: this.message,
};
if (!_.isUndefined(this.details)) {
json.details = this.details;
}
return json;
}
}
exports.HttpsError = HttpsError;
// Returns true if req is a properly formatted callable request.
function isValidRequest(req) {
// The body must not be empty.
if (!req.body) {
console.warn('Request is missing body.');
return false;
}
// Make sure it's a POST.
if (req.method !== 'POST') {
console.warn('Request has invalid method.', req.method);
return false;
}
// Check that the Content-Type is JSON.
let contentType = (req.header('Content-Type') || '').toLowerCase();
// If it has a charset, just ignore it for now.
const semiColon = contentType.indexOf(';');
if (semiColon >= 0) {
contentType = contentType.substr(0, semiColon).trim();
}
if (contentType !== 'application/json') {
console.warn('Request has incorrect Content-Type.', contentType);
return false;
}
// The body must have data.
if (_.isUndefined(req.body.data)) {
console.warn('Request body is missing data.', req.body);
return false;
}
// TODO(klimt): Allow only whitelisted http headers.
// Verify that the body does not have any extra fields.
const extras = _.omit(req.body, 'data');
if (!_.isEmpty(extras)) {
console.warn('Request body has extra fields.', extras);
return false;
}
return true;
}
const LONG_TYPE = 'type.googleapis.com/google.protobuf.Int64Value';
const UNSIGNED_LONG_TYPE = 'type.googleapis.com/google.protobuf.UInt64Value';
/**
* Encodes arbitrary data in our special format for JSON.
* This is exposed only for testing.
*/
/** @internal */
function encode(data) {
if (_.isNull(data) || _.isUndefined(data)) {
return null;
}
// Oddly, _.isFinite(new Number(x)) always returns false, so unwrap Numbers.
if (data instanceof Number) {
data = data.valueOf();
}
if (_.isFinite(data)) {
// Any number in JS is safe to put directly in JSON and parse as a double
// without any loss of precision.
return data;
}
if (_.isBoolean(data)) {
return data;
}
if (_.isString(data)) {
return data;
}
if (_.isArray(data)) {
return _.map(data, encode);
}
if (_.isObject(data)) {
// It's not safe to use _.forEach, because the object might be 'array-like'
// if it has a key called 'length'. Note that this intentionally overrides
// any toJSON method that an object may have.
return _.mapValues(data, encode);
}
// If we got this far, the data is not encodable.
console.error('Data cannot be encoded in JSON.', data);
throw new Error('Data cannot be encoded in JSON: ' + data);
}
exports.encode = encode;
/**
* Decodes our special format for JSON into native types.
* This is exposed only for testing.
*/
/** @internal */
function decode(data) {
if (data === null) {
return data;
}
if (data['@type']) {
switch (data['@type']) {
case LONG_TYPE:
// Fall through and handle this the same as unsigned.
case UNSIGNED_LONG_TYPE: {
// Technically, this could work return a valid number for malformed
// data if there was a number followed by garbage. But it's just not
// worth all the extra code to detect that case.
const value = parseFloat(data.value);
if (_.isNaN(value)) {
console.error('Data cannot be decoded from JSON.', data);
throw new Error('Data cannot be decoded from JSON: ' + data);
}
return value;
}
default: {
console.error('Data cannot be decoded from JSON.', data);
throw new Error('Data cannot be decoded from JSON: ' + data);
}
}
}
if (_.isArray(data)) {
return _.map(data, decode);
}
if (_.isObject(data)) {
// It's not safe to use _.forEach, because the object might be 'array-like'
// if it has a key called 'length'.
return _.mapValues(data, decode);
}
// Anything else is safe to return.
return data;
}
exports.decode = decode;
const corsHandler = cors({ origin: true, methods: 'POST' });
/** @internal */
function _onCallWithOpts(handler, opts) {
const func = (req, res) => __awaiter(this, void 0, void 0, function* () {
try {
if (!isValidRequest(req)) {
console.error('Invalid request', req);
throw new HttpsError('invalid-argument', 'Bad Request');
}
const context = { rawRequest: req };
const authorization = req.header('Authorization');
if (authorization) {
const match = authorization.match(/^Bearer (.*)$/);
if (!match) {
throw new HttpsError('unauthenticated', 'Unauthenticated');
}
const idToken = match[1];
try {
const authToken = yield apps_1.apps()
.admin.auth()
.verifyIdToken(idToken);
context.auth = {
uid: authToken.uid,
token: authToken,
};
}
catch (e) {
throw new HttpsError('unauthenticated', 'Unauthenticated');
}
}
const instanceId = req.header('Firebase-Instance-ID-Token');
if (instanceId) {
// Validating the token requires an http request, so we don't do it.
// If the user wants to use it for something, it will be validated then.
// Currently, the only real use case for this token is for sending
// pushes with FCM. In that case, the FCM APIs will validate the token.
context.instanceIdToken = req.header('Firebase-Instance-ID-Token');
}
const data = decode(req.body.data);
let result = yield handler(data, context);
// Encode the result as JSON to preserve types like Dates.
result = encode(result);
// If there was some result, encode it in the body.
const responseBody = { result };
res.status(200).send(responseBody);
}
catch (error) {
if (!(error instanceof HttpsError)) {
// This doesn't count as an 'explicit' error.
console.error('Unhandled error', error);
error = new HttpsError('internal', 'INTERNAL');
}
const status = error.httpStatus;
const body = { error: error.toJSON() };
res.status(status).send(body);
}
});
// Wrap the function with a cors handler.
const corsFunc = (req, res) => {
return corsHandler(req, res, () => func(req, res));
};
corsFunc.__trigger = _.assign(cloud_functions_1.optsToTrigger(opts), {
httpsTrigger: {},
labels: { 'deployment-callable': 'true' },
});
corsFunc.run = handler;
return corsFunc;
}
exports._onCallWithOpts = _onCallWithOpts;

View File

@@ -0,0 +1,29 @@
import { CloudFunction, EventContext } from '../cloud-functions';
/** Select Cloud Pub/Sub topic to listen to.
* @param topic Name of Pub/Sub topic, must belong to the same project as the function.
*/
export declare function topic(topic: string): TopicBuilder;
/** Builder used to create Cloud Functions for Google Pub/Sub topics. */
export declare class TopicBuilder {
private triggerResource;
private opts;
/** Handle a Pub/Sub message that was published to a Cloud Pub/Sub topic */
onPublish(handler: (message: Message, context: EventContext) => PromiseLike<any> | any): CloudFunction<Message>;
}
/**
* A Pub/Sub message.
*
* This class has an additional .json helper which will correctly deserialize any
* message that was a JSON object when published with the JS SDK. .json will throw
* if the message is not a base64 encoded JSON string.
*/
export declare class Message {
readonly data: string;
readonly attributes: {
[key: string]: string;
};
private _json;
constructor(data: any);
readonly json: any;
toJSON(): any;
}

View File

@@ -0,0 +1,98 @@
"use strict";
// The MIT License (MIT)
//
// Copyright (c) 2017 Firebase
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
Object.defineProperty(exports, "__esModule", { value: true });
const cloud_functions_1 = require("../cloud-functions");
/** @internal */
exports.provider = 'google.pubsub';
/** @internal */
exports.service = 'pubsub.googleapis.com';
/** Select Cloud Pub/Sub topic to listen to.
* @param topic Name of Pub/Sub topic, must belong to the same project as the function.
*/
function topic(topic) {
return _topicWithOpts(topic, {});
}
exports.topic = topic;
/** @internal */
function _topicWithOpts(topic, opts) {
if (topic.indexOf('/') !== -1) {
throw new Error('Topic name may not have a /');
}
return new TopicBuilder(() => {
if (!process.env.GCLOUD_PROJECT) {
throw new Error('process.env.GCLOUD_PROJECT is not set.');
}
return `projects/${process.env.GCLOUD_PROJECT}/topics/${topic}`;
}, opts);
}
exports._topicWithOpts = _topicWithOpts;
/** Builder used to create Cloud Functions for Google Pub/Sub topics. */
class TopicBuilder {
/** @internal */
constructor(triggerResource, opts) {
this.triggerResource = triggerResource;
this.opts = opts;
}
/** Handle a Pub/Sub message that was published to a Cloud Pub/Sub topic */
onPublish(handler) {
return cloud_functions_1.makeCloudFunction({
handler,
provider: exports.provider,
service: exports.service,
triggerResource: this.triggerResource,
eventType: 'topic.publish',
dataConstructor: raw => new Message(raw.data),
opts: this.opts,
});
}
}
exports.TopicBuilder = TopicBuilder;
/**
* A Pub/Sub message.
*
* This class has an additional .json helper which will correctly deserialize any
* message that was a JSON object when published with the JS SDK. .json will throw
* if the message is not a base64 encoded JSON string.
*/
class Message {
constructor(data) {
[this.data, this.attributes, this._json] = [
data.data,
data.attributes || {},
data.json,
];
}
get json() {
if (typeof this._json === 'undefined') {
this._json = JSON.parse(new Buffer(this.data, 'base64').toString('utf8'));
}
return this._json;
}
toJSON() {
return {
data: this.data,
attributes: this.attributes,
};
}
}
exports.Message = Message;

View File

@@ -0,0 +1,35 @@
import { CloudFunction, EventContext } from '../cloud-functions';
/**
* Handle all updates (including rollbacks) that affect a Remote Config project.
* @param handler A function that takes the updated Remote Config template
* version metadata as an argument.
*/
export declare function onUpdate(handler: (version: TemplateVersion, context: EventContext) => PromiseLike<any> | any): CloudFunction<TemplateVersion>;
/**
* Interface representing a Remote Config template version metadata object that
* was emitted when the project was updated.
*/
export interface TemplateVersion {
/** The version number of the updated Remote Config template. */
versionNumber: number;
/** When the template was updated in format (ISO8601 timestamp). */
updateTime: string;
/** Metadata about the account that performed the update. */
updateUser: RemoteConfigUser;
/** A description associated with the particular Remote Config template. */
description: string;
/** The origin of the caller. */
updateOrigin: string;
/** The type of update action that was performed. */
updateType: string;
/**
* The version number of the Remote Config template that was rolled back to,
* if the update was a rollback.
*/
rollbackSource?: number;
}
export interface RemoteConfigUser {
name?: string;
email: string;
imageUrl?: string;
}

View File

@@ -0,0 +1,52 @@
"use strict";
// The MIT License (MIT)
//
// Copyright (c) 2018 Firebase
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
Object.defineProperty(exports, "__esModule", { value: true });
const cloud_functions_1 = require("../cloud-functions");
/** @internal */
exports.provider = 'google.firebase.remoteconfig';
/** @internal */
exports.service = 'firebaseremoteconfig.googleapis.com';
/**
* Handle all updates (including rollbacks) that affect a Remote Config project.
* @param handler A function that takes the updated Remote Config template
* version metadata as an argument.
*/
function onUpdate(handler) {
return _onUpdateWithOpts(handler, {});
}
exports.onUpdate = onUpdate;
/** @internal */
function _onUpdateWithOpts(handler, opts) {
if (!process.env.GCLOUD_PROJECT) {
throw new Error('process.env.GCLOUD_PROJECT is not set.');
}
return cloud_functions_1.makeCloudFunction({
handler,
provider: exports.provider,
service: exports.service,
triggerResource: () => `projects/${process.env.GCLOUD_PROJECT}`,
eventType: 'update',
opts: opts,
});
}
exports._onUpdateWithOpts = _onUpdateWithOpts;

View File

@@ -0,0 +1,85 @@
import { CloudFunction, EventContext } from '../cloud-functions';
/**
* The optional bucket function allows you to choose which buckets' events to handle.
* This step can be bypassed by calling object() directly, which will use the default
* Cloud Storage for Firebase bucket.
* @param bucket Name of the Google Cloud Storage bucket to listen to.
*/
export declare function bucket(bucket?: string): BucketBuilder;
/**
* Handle events related to Cloud Storage objects.
*/
export declare function object(): ObjectBuilder;
export declare class BucketBuilder {
private triggerResource;
private opts;
/** Handle events for objects in this bucket. */
object(): ObjectBuilder;
}
export declare class ObjectBuilder {
private triggerResource;
private opts;
/** Respond to archiving of an object, this is only for buckets that enabled object versioning. */
onArchive(handler: (object: ObjectMetadata, context: EventContext) => PromiseLike<any> | any): CloudFunction<ObjectMetadata>;
/** Respond to the deletion of an object (not to archiving, if object versioning is enabled). */
onDelete(handler: (object: ObjectMetadata, context: EventContext) => PromiseLike<any> | any): CloudFunction<ObjectMetadata>;
/** Respond to the successful creation of an object. */
onFinalize(handler: (object: ObjectMetadata, context: EventContext) => PromiseLike<any> | any): CloudFunction<ObjectMetadata>;
/** Respond to metadata updates of existing objects. */
onMetadataUpdate(handler: (object: ObjectMetadata, context: EventContext) => PromiseLike<any> | any): CloudFunction<ObjectMetadata>;
private onOperation(handler, eventType);
}
export interface ObjectMetadata {
kind: string;
id: string;
bucket: string;
storageClass: string;
size: string;
timeCreated: string;
updated: string;
selfLink?: string;
name?: string;
generation?: string;
contentType?: string;
metageneration?: string;
timeDeleted?: string;
timeStorageClassUpdated?: string;
md5Hash?: string;
mediaLink?: string;
contentEncoding?: string;
contentDisposition?: string;
contentLanguage?: string;
cacheControl?: string;
metadata?: {
[key: string]: string;
};
acl?: [{
kind?: string;
id?: string;
selfLink?: string;
bucket?: string;
object?: string;
generation?: string;
entity?: string;
role?: string;
email?: string;
entityId?: string;
domain?: string;
projectTeam?: {
projectNumber?: string;
team?: string;
};
etag?: string;
}];
owner?: {
entity?: string;
entityId?: string;
};
crc32c?: string;
componentCount?: string;
etag?: string;
customerEncryption?: {
encryptionAlgorithm?: string;
keySha256?: string;
};
}

View File

@@ -0,0 +1,118 @@
"use strict";
// The MIT License (MIT)
//
// Copyright (c) 2017 Firebase
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
Object.defineProperty(exports, "__esModule", { value: true });
const cloud_functions_1 = require("../cloud-functions");
const config_1 = require("../config");
/** @internal */
exports.provider = 'google.storage';
/** @internal */
exports.service = 'storage.googleapis.com';
/**
* The optional bucket function allows you to choose which buckets' events to handle.
* This step can be bypassed by calling object() directly, which will use the default
* Cloud Storage for Firebase bucket.
* @param bucket Name of the Google Cloud Storage bucket to listen to.
*/
function bucket(bucket) {
return _bucketWithOpts({}, bucket);
}
exports.bucket = bucket;
/**
* Handle events related to Cloud Storage objects.
*/
function object() {
return _objectWithOpts({});
}
exports.object = object;
/** @internal */
function _bucketWithOpts(opts, bucket) {
const resourceGetter = () => {
bucket = bucket || config_1.firebaseConfig().storageBucket;
if (!bucket) {
throw new Error('Missing bucket name. If you are unit testing, please provide a bucket name' +
' through `functions.storage.bucket(bucketName)`, or set process.env.FIREBASE_CONFIG.');
}
if (!/^[a-z\d][a-z\d\\._-]{1,230}[a-z\d]$/.test(bucket)) {
throw new Error(`Invalid bucket name ${bucket}`);
}
return `projects/_/buckets/${bucket}`;
};
return new BucketBuilder(resourceGetter, opts);
}
exports._bucketWithOpts = _bucketWithOpts;
/** @internal */
function _objectWithOpts(opts) {
return _bucketWithOpts(opts).object();
}
exports._objectWithOpts = _objectWithOpts;
class BucketBuilder {
/** @internal */
constructor(triggerResource, opts) {
this.triggerResource = triggerResource;
this.opts = opts;
}
/** Handle events for objects in this bucket. */
object() {
return new ObjectBuilder(this.triggerResource, this.opts);
}
}
exports.BucketBuilder = BucketBuilder;
class ObjectBuilder {
/** @internal */
constructor(triggerResource, opts) {
this.triggerResource = triggerResource;
this.opts = opts;
}
/** @internal */
onChange(handler) {
throw new Error('"onChange" is now deprecated, please use "onArchive", "onDelete", ' +
'"onFinalize", or "onMetadataUpdate".');
}
/** Respond to archiving of an object, this is only for buckets that enabled object versioning. */
onArchive(handler) {
return this.onOperation(handler, 'object.archive');
}
/** Respond to the deletion of an object (not to archiving, if object versioning is enabled). */
onDelete(handler) {
return this.onOperation(handler, 'object.delete');
}
/** Respond to the successful creation of an object. */
onFinalize(handler) {
return this.onOperation(handler, 'object.finalize');
}
/** Respond to metadata updates of existing objects. */
onMetadataUpdate(handler) {
return this.onOperation(handler, 'object.metadataUpdate');
}
onOperation(handler, eventType) {
return cloud_functions_1.makeCloudFunction({
handler,
provider: exports.provider,
service: exports.service,
eventType,
triggerResource: this.triggerResource,
opts: this.opts,
});
}
}
exports.ObjectBuilder = ObjectBuilder;