Merge branch 'master' into patch-1

This commit is contained in:
Michael Bleigh
2019-01-07 12:29:19 -08:00
committed by GitHub
7 changed files with 70 additions and 25 deletions

View File

@@ -0,0 +1,9 @@
fixed - Fixed Firestore emulator bug related to https://github.com/firebase/firebase-tools/issues/1073
fixed - Fixed Firestore emulator bug regarding array ordering during writing/reading
fixed - Fixed Firestore emulator handling of query cursors using document names
fixed - Fixed a race condition when deploying Firestore indexes (issues #1080 and #1081)
fixed - Fixed an error that occurs when a Firestore field override removes all indexes
feature - Firestore emulator now has the ability to produce rule-coverage reports
changed - Firestore emulator now exposes the v1 service definition
changed - Firestore emulator has various runtime improvements
changed - Clearer empty state when pretty-printing Firestore indexes

View File

@@ -11,13 +11,15 @@ var logger = require("../logger");
var path = require("path");
function runCommand(command, childOptions) {
var escapedCommand = command.replace(/\"/g, '\\"');
var translatedCommand =
'"' +
process.execPath +
'" "' +
path.resolve(require.resolve("cross-env"), "..", "bin", "cross-env.js") +
'" ' +
command;
path.resolve(require.resolve("cross-env"), "..", "bin", "cross-env-shell.js") +
'" "' +
escapedCommand +
'"';
return new Promise(function(resolve, reject) {
logger.info("Running command: " + command);

View File

@@ -24,8 +24,8 @@ const _emulators = {
stdout: null,
cacheDir: CACHE_DIR,
remoteUrl:
"https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.2.2.jar",
localPath: path.join(CACHE_DIR, "cloud-firestore-emulator-v1.2.2.jar"),
"https://storage.googleapis.com/firebase-preview-drop/emulator/cloud-firestore-emulator-v1.2.3.jar",
localPath: path.join(CACHE_DIR, "cloud-firestore-emulator-v1.2.3.jar"),
},
};

View File

@@ -65,5 +65,5 @@ export interface Field {
*/
export interface IndexConfig {
ancestorField?: string;
indexes: Index[];
indexes?: Index[];
}

View File

@@ -59,16 +59,17 @@ export class FirestoreIndexes {
);
}
indexesToDeploy.forEach(async (index) => {
const indexPromises: Array<Promise<any>> = [];
indexesToDeploy.forEach((index) => {
const exists = existingIndexes.some((x) => this.indexMatchesSpec(x, index));
if (exists) {
logger.debug(`Skipping existing index: ${JSON.stringify(index)}`);
return;
} else {
logger.debug(`Creating new index: ${JSON.stringify(index)}`);
indexPromises.push(this.createIndex(project, index));
}
logger.debug(`Creating new index: ${JSON.stringify(index)}`);
await this.createIndex(project, index);
});
await Promise.all(indexPromises);
if (existingFieldOverrides.length > fieldOverridesToDeploy.length) {
utils.logBullet(
@@ -78,16 +79,17 @@ export class FirestoreIndexes {
);
}
fieldOverridesToDeploy.forEach(async (field) => {
const fieldPromises: Array<Promise<any>> = [];
fieldOverridesToDeploy.forEach((field) => {
const exists = existingFieldOverrides.some((x) => this.fieldMatchesSpec(x, field));
if (exists) {
logger.debug(`Skipping existing field override: ${JSON.stringify(field)}`);
return;
} else {
logger.debug(`Updating field override: ${JSON.stringify(field)}`);
fieldPromises.push(this.patchField(project, field));
}
logger.debug(`Updating field override: ${JSON.stringify(field)}`);
await this.patchField(project, field);
});
await Promise.all(fieldPromises);
}
/**
@@ -172,11 +174,12 @@ export class FirestoreIndexes {
const fieldsJson = fields.map((field) => {
const parsedName = this.parseFieldName(field.name);
const fieldIndexes = field.indexConfig.indexes || [];
return {
collectionGroup: parsedName.collectionGroupId,
fieldPath: parsedName.fieldPath,
indexes: field.indexConfig.indexes.map((index) => {
indexes: fieldIndexes.map((index) => {
const firstField = index.fields[0];
return {
order: firstField.order,
@@ -198,6 +201,11 @@ export class FirestoreIndexes {
* @param indexes the array of indexes.
*/
prettyPrintIndexes(indexes: API.Index[]): void {
if (indexes.length === 0) {
logger.info("None");
return;
}
indexes.forEach((index) => {
logger.info(this.prettyIndexString(index));
});
@@ -208,6 +216,11 @@ export class FirestoreIndexes {
* @param fields the array of field overrides.
*/
printFieldOverrides(fields: API.Field[]): void {
if (fields.length === 0) {
logger.info("None");
return;
}
fields.forEach((field) => {
logger.info(this.prettyFieldString(field));
});
@@ -390,11 +403,12 @@ export class FirestoreIndexes {
return false;
}
if (field.indexConfig.indexes.length !== spec.indexes.length) {
const fieldIndexes = field.indexConfig.indexes || [];
if (fieldIndexes.length !== spec.indexes.length) {
return false;
}
const fieldModes = field.indexConfig.indexes.map((index) => {
const fieldModes = fieldIndexes.map((index) => {
const firstField = index.fields[0];
return firstField.order || firstField.arrayConfig;
});
@@ -563,11 +577,16 @@ export class FirestoreIndexes {
clc.yellow(parsedName.fieldPath) +
"] --";
field.indexConfig.indexes.forEach((index) => {
const firstField = index.fields[0];
const mode = firstField.order || firstField.arrayConfig;
result += " (" + mode + ")";
});
const fieldIndexes = field.indexConfig.indexes || [];
if (fieldIndexes.length > 0) {
fieldIndexes.forEach((index) => {
const firstField = index.fields[0];
const mode = firstField.order || firstField.arrayConfig;
result += ` (${mode})`;
});
} else {
result += " (no indexes)";
}
return result;
}

View File

@@ -39,7 +39,7 @@ module.exports = function(setup, config) {
return config
.askWriteProjectFile("functions/package.json", PACKAGE_LINTING_TEMPLATE)
.then(function() {
config.askWriteProjectFile("functions/tslint.json", TSLINT_TEMPLATE);
return config.askWriteProjectFile("functions/tslint.json", TSLINT_TEMPLATE);
});
}
_.set(setup, "config.functions.predeploy", 'npm --prefix "$RESOURCE_DIR" run build');

View File

@@ -198,6 +198,21 @@ describe("IndexSpecMatching", () => {
expect(idx.fieldMatchesSpec(apiField, specField)).to.eql(true);
});
it("should match a field spec with all indexes excluded", () => {
const apiField = {
name: "/projects/myproject/databases/(default)/collectionGroups/collection/fields/abc123",
indexConfig: {},
} as API.Field;
const specField = {
collectionGroup: "collection",
fieldPath: "abc123",
indexes: [],
} as Spec.FieldOverride;
expect(idx.fieldMatchesSpec(apiField, specField)).to.eql(true);
});
it("should identify a negative field spec match", () => {
const apiField = {
name: "/projects/myproject/databases/(default)/collectionGroups/collection/fields/abc123",