firebase-tools 13.11.2 → 13.11.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/apphosting/githubConnections.js +2 -2
- package/lib/commands/setup-emulators-dataconnect.js +23 -3
- package/lib/dataconnect/client.js +55 -9
- package/lib/emulator/adminSdkConfig.test.js +16 -0
- package/lib/emulator/dataconnectEmulator.js +7 -4
- package/lib/emulator/downloadableEmulators.js +16 -15
- package/lib/emulator/registry.js +1 -1
- package/lib/emulator/storage/apis/shared.js +2 -1
- package/lib/emulator/storage/persistence.js +5 -13
- package/lib/emulator/storage/rfc.js +9 -0
- package/lib/firestore/api-sort.test.js +74 -0
- package/lib/firestore/backupUtils.test.js +18 -0
- package/lib/firestore/pretty-print.test.js +53 -0
- package/lib/firestore/util.test.js +42 -0
- package/lib/frameworks/compose/discover/mockFileSystem.js +36 -0
- package/lib/frameworks/index.js +2 -6
- package/lib/frameworks/next/utils.js +2 -8
- package/lib/listFiles.js +0 -1
- package/lib/rc.js +1 -0
- package/package.json +8 -6
- package/templates/init/dataconnect/mutations.gql +19 -14
- package/templates/init/dataconnect/queries.gql +28 -23
- package/templates/init/dataconnect/schema.gql +1 -1
|
@@ -86,13 +86,13 @@ async function createFullyInstalledConnection(projectId, location, connectionId,
|
|
|
86
86
|
authorizerCredential: (_b = oauthConn.githubConfig) === null || _b === void 0 ? void 0 : _b.authorizerCredential,
|
|
87
87
|
});
|
|
88
88
|
while (conn.installationState.stage !== "COMPLETE") {
|
|
89
|
-
utils.logBullet("Install the Firebase GitHub app to enable access to GitHub repositories");
|
|
89
|
+
utils.logBullet("Install the Firebase App Hosting GitHub app to enable access to GitHub repositories");
|
|
90
90
|
const targetUri = conn.installationState.actionUri;
|
|
91
91
|
utils.logBullet(targetUri);
|
|
92
92
|
await utils.openInBrowser(targetUri);
|
|
93
93
|
await (0, prompt_1.promptOnce)({
|
|
94
94
|
type: "input",
|
|
95
|
-
message: "Press Enter once you have installed or configured the Firebase GitHub app to access your GitHub repo.",
|
|
95
|
+
message: "Press Enter once you have installed or configured the Firebase App Hosting GitHub app to access your GitHub repo.",
|
|
96
96
|
});
|
|
97
97
|
conn = await devConnect.getConnection(projectId, location, connectionId);
|
|
98
98
|
}
|
|
@@ -2,11 +2,31 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.command = void 0;
|
|
4
4
|
const command_1 = require("../command");
|
|
5
|
-
const download_1 = require("../emulator/download");
|
|
6
5
|
const types_1 = require("../emulator/types");
|
|
6
|
+
const emulators_1 = require("../init/features/emulators");
|
|
7
|
+
const prompt_1 = require("../prompt");
|
|
8
|
+
const logger_1 = require("../logger");
|
|
9
|
+
const downloadableEmulators_1 = require("../emulator/downloadableEmulators");
|
|
7
10
|
const NAME = types_1.Emulators.DATACONNECT;
|
|
8
11
|
exports.command = new command_1.Command(`setup:emulators:${NAME}`)
|
|
9
12
|
.description(`downloads the ${NAME} emulator`)
|
|
10
|
-
.action(() => {
|
|
11
|
-
|
|
13
|
+
.action(async (options) => {
|
|
14
|
+
var _a, _b;
|
|
15
|
+
await (0, downloadableEmulators_1.downloadIfNecessary)(NAME);
|
|
16
|
+
if (!options.config) {
|
|
17
|
+
logger_1.logger.info("Not currently in a Firebase project directory. Run this command from a project directory to configure the Data Connect emulator.");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!options.nonInteractive) {
|
|
21
|
+
const dataconnectEmulatorConfig = options.rc.getDataconnect();
|
|
22
|
+
const defaultConnectionString = (_b = (_a = dataconnectEmulatorConfig === null || dataconnectEmulatorConfig === void 0 ? void 0 : dataconnectEmulatorConfig.postgres) === null || _a === void 0 ? void 0 : _a.localConnectionString) !== null && _b !== void 0 ? _b : emulators_1.DEFAULT_POSTGRES_CONNECTION;
|
|
23
|
+
const localConnectionString = await (0, prompt_1.promptOnce)({
|
|
24
|
+
type: "input",
|
|
25
|
+
name: "localConnectionString",
|
|
26
|
+
message: `What is the connection string of the local Postgres instance you would like to use with the Data Connect emulator?`,
|
|
27
|
+
default: defaultConnectionString,
|
|
28
|
+
});
|
|
29
|
+
options.rc.setDataconnect(localConnectionString);
|
|
30
|
+
}
|
|
31
|
+
logger_1.logger.info("Setup complete!");
|
|
12
32
|
});
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.upsertConnector = exports.listConnectors = exports.deleteConnector = exports.getConnector = exports.upsertSchema = exports.getSchema = exports.
|
|
3
|
+
exports.upsertConnector = exports.listConnectors = exports.deleteConnector = exports.getConnector = exports.deleteSchema = exports.upsertSchema = exports.getSchema = exports.deleteServiceAndChildResources = exports.createService = exports.listAllServices = exports.getService = exports.listLocations = void 0;
|
|
4
4
|
const api_1 = require("../api");
|
|
5
5
|
const apiv2_1 = require("../apiv2");
|
|
6
6
|
const operationPoller = require("../operation-poller");
|
|
7
7
|
const types = require("./types");
|
|
8
8
|
const DATACONNECT_API_VERSION = "v1alpha";
|
|
9
|
+
const PAGE_SIZE_MAX = 100;
|
|
9
10
|
const dataconnectClient = () => new apiv2_1.Client({
|
|
10
11
|
urlPrefix: (0, api_1.dataconnectOrigin)(),
|
|
11
12
|
apiVersion: DATACONNECT_API_VERSION,
|
|
@@ -17,6 +18,11 @@ async function listLocations(projectId) {
|
|
|
17
18
|
return (_c = (_b = (_a = res.body) === null || _a === void 0 ? void 0 : _a.locations) === null || _b === void 0 ? void 0 : _b.map((l) => l.locationId)) !== null && _c !== void 0 ? _c : [];
|
|
18
19
|
}
|
|
19
20
|
exports.listLocations = listLocations;
|
|
21
|
+
async function getService(serviceName) {
|
|
22
|
+
const res = await dataconnectClient().get(serviceName);
|
|
23
|
+
return res.body;
|
|
24
|
+
}
|
|
25
|
+
exports.getService = getService;
|
|
20
26
|
async function listAllServices(projectId) {
|
|
21
27
|
var _a;
|
|
22
28
|
const res = await dataconnectClient().get(`/projects/${projectId}/locations/-/services`);
|
|
@@ -39,8 +45,8 @@ async function createService(projectId, locationId, serviceId) {
|
|
|
39
45
|
return pollRes;
|
|
40
46
|
}
|
|
41
47
|
exports.createService = createService;
|
|
42
|
-
async function deleteService(
|
|
43
|
-
const op = await dataconnectClient().delete(
|
|
48
|
+
async function deleteService(serviceName) {
|
|
49
|
+
const op = await dataconnectClient().delete(serviceName);
|
|
44
50
|
const pollRes = await operationPoller.pollOperation({
|
|
45
51
|
apiOrigin: (0, api_1.dataconnectOrigin)(),
|
|
46
52
|
apiVersion: DATACONNECT_API_VERSION,
|
|
@@ -48,7 +54,20 @@ async function deleteService(projectId, locationId, serviceId) {
|
|
|
48
54
|
});
|
|
49
55
|
return pollRes;
|
|
50
56
|
}
|
|
51
|
-
|
|
57
|
+
async function deleteServiceAndChildResources(serviceName) {
|
|
58
|
+
const connectors = await listConnectors(serviceName);
|
|
59
|
+
await Promise.all(connectors.map(async (c) => deleteConnector(c.name)));
|
|
60
|
+
try {
|
|
61
|
+
await deleteSchema(serviceName);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
if (err.status !== 404) {
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
await deleteService(serviceName);
|
|
69
|
+
}
|
|
70
|
+
exports.deleteServiceAndChildResources = deleteServiceAndChildResources;
|
|
52
71
|
async function getSchema(serviceName) {
|
|
53
72
|
try {
|
|
54
73
|
const res = await dataconnectClient().get(`${serviceName}/schemas/${types.SCHEMA_ID}`);
|
|
@@ -79,20 +98,47 @@ async function upsertSchema(schema, validateOnly = false) {
|
|
|
79
98
|
});
|
|
80
99
|
}
|
|
81
100
|
exports.upsertSchema = upsertSchema;
|
|
101
|
+
async function deleteSchema(serviceName) {
|
|
102
|
+
const op = await dataconnectClient().delete(`${serviceName}/schemas/${types.SCHEMA_ID}`);
|
|
103
|
+
await operationPoller.pollOperation({
|
|
104
|
+
apiOrigin: (0, api_1.dataconnectOrigin)(),
|
|
105
|
+
apiVersion: DATACONNECT_API_VERSION,
|
|
106
|
+
operationResourceName: op.body.name,
|
|
107
|
+
});
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
exports.deleteSchema = deleteSchema;
|
|
82
111
|
async function getConnector(name) {
|
|
83
112
|
const res = await dataconnectClient().get(name);
|
|
84
113
|
return res.body;
|
|
85
114
|
}
|
|
86
115
|
exports.getConnector = getConnector;
|
|
87
116
|
async function deleteConnector(name) {
|
|
88
|
-
const
|
|
89
|
-
|
|
117
|
+
const op = await dataconnectClient().delete(name);
|
|
118
|
+
await operationPoller.pollOperation({
|
|
119
|
+
apiOrigin: (0, api_1.dataconnectOrigin)(),
|
|
120
|
+
apiVersion: DATACONNECT_API_VERSION,
|
|
121
|
+
operationResourceName: op.body.name,
|
|
122
|
+
});
|
|
123
|
+
return;
|
|
90
124
|
}
|
|
91
125
|
exports.deleteConnector = deleteConnector;
|
|
92
126
|
async function listConnectors(serviceName) {
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
127
|
+
const connectors = [];
|
|
128
|
+
const getNextPage = async (pageToken = "") => {
|
|
129
|
+
const res = await dataconnectClient().get(`${serviceName}/connectors`, {
|
|
130
|
+
queryParams: {
|
|
131
|
+
pageSize: PAGE_SIZE_MAX,
|
|
132
|
+
pageToken,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
connectors.push(...res.body.connectors);
|
|
136
|
+
if (res.body.nextPageToken) {
|
|
137
|
+
await getNextPage(res.body.nextPageToken);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
await getNextPage();
|
|
141
|
+
return connectors;
|
|
96
142
|
}
|
|
97
143
|
exports.listConnectors = listConnectors;
|
|
98
144
|
async function upsertConnector(connector) {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chai_1 = require("chai");
|
|
4
|
+
const adminSdkConfig_1 = require("./adminSdkConfig");
|
|
5
|
+
describe("adminSdkConfig", () => {
|
|
6
|
+
describe("getProjectAdminSdkConfigOrCached", () => {
|
|
7
|
+
it("should return a fake config for a demo project id", async () => {
|
|
8
|
+
const projectId = "demo-project-1234";
|
|
9
|
+
await (0, chai_1.expect)((0, adminSdkConfig_1.getProjectAdminSdkConfigOrCached)(projectId)).to.eventually.deep.equal({
|
|
10
|
+
projectId: "demo-project-1234",
|
|
11
|
+
databaseURL: "https://demo-project-1234.firebaseio.com",
|
|
12
|
+
storageBucket: "demo-project-1234.appspot.com",
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DataConnectEmulatorClient = exports.DataConnectEmulator = void 0;
|
|
4
4
|
const childProcess = require("child_process");
|
|
5
|
+
const clc = require("colorette");
|
|
5
6
|
const api_1 = require("../api");
|
|
6
7
|
const constants_1 = require("./constants");
|
|
7
8
|
const downloadableEmulators_1 = require("./downloadableEmulators");
|
|
@@ -36,7 +37,6 @@ class DataConnectEmulator {
|
|
|
36
37
|
auto_download: this.args.auto_download,
|
|
37
38
|
listen: (0, portUtils_1.listenSpecsToString)(this.args.listen),
|
|
38
39
|
config_dir: this.args.configDir,
|
|
39
|
-
project_id: this.args.projectId,
|
|
40
40
|
service_location: this.args.locationId,
|
|
41
41
|
});
|
|
42
42
|
}
|
|
@@ -62,6 +62,8 @@ class DataConnectEmulator {
|
|
|
62
62
|
static async generate(args) {
|
|
63
63
|
const commandInfo = await (0, downloadableEmulators_1.downloadIfNecessary)(types_1.Emulators.DATACONNECT);
|
|
64
64
|
const cmd = [
|
|
65
|
+
"--logtostderr",
|
|
66
|
+
"-v=2",
|
|
65
67
|
"generate",
|
|
66
68
|
`--service_location=${args.locationId}`,
|
|
67
69
|
`--config_dir=${args.configDir}`,
|
|
@@ -78,7 +80,7 @@ class DataConnectEmulator {
|
|
|
78
80
|
static async build(args) {
|
|
79
81
|
var _a;
|
|
80
82
|
const commandInfo = await (0, downloadableEmulators_1.downloadIfNecessary)(types_1.Emulators.DATACONNECT);
|
|
81
|
-
const cmd = ["build", `--config_dir=${args.configDir}`];
|
|
83
|
+
const cmd = ["--logtostderr", "-v=2", "build", `--config_dir=${args.configDir}`];
|
|
82
84
|
const res = childProcess.spawnSync(commandInfo.binary, cmd, { encoding: "utf-8" });
|
|
83
85
|
if (res.error) {
|
|
84
86
|
throw new error_1.FirebaseError(`Error starting up Data Connect build: ${res.error.message}`, {
|
|
@@ -108,8 +110,9 @@ class DataConnectEmulator {
|
|
|
108
110
|
async connectToPostgres(localConnectionString, database, serviceId) {
|
|
109
111
|
const connectionString = localConnectionString !== null && localConnectionString !== void 0 ? localConnectionString : this.getLocalConectionString();
|
|
110
112
|
if (!connectionString) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
+
const msg = `No Postgres connection string found in '.firebaserc'. The Data Connect emulator will not be able to execute operations.
|
|
114
|
+
Run ${clc.bold("firebase setup:emulators:dataconnect")} to set up a Postgres connection.`;
|
|
115
|
+
throw new error_1.FirebaseError(msg);
|
|
113
116
|
}
|
|
114
117
|
await this.emulatorClient.configureEmulator({ connectionString, database, serviceId });
|
|
115
118
|
return true;
|
|
@@ -46,20 +46,20 @@ const EMULATOR_UPDATE_DETAILS = {
|
|
|
46
46
|
},
|
|
47
47
|
dataconnect: process.platform === "darwin"
|
|
48
48
|
? {
|
|
49
|
-
version: "1.2.
|
|
50
|
-
expectedSize:
|
|
51
|
-
expectedChecksum: "
|
|
49
|
+
version: "1.2.2",
|
|
50
|
+
expectedSize: 24007488,
|
|
51
|
+
expectedChecksum: "c1fb77895203681479ee5dd22d57249f",
|
|
52
52
|
}
|
|
53
53
|
: process.platform === "win32"
|
|
54
54
|
? {
|
|
55
|
-
version: "1.2.
|
|
56
|
-
expectedSize:
|
|
57
|
-
expectedChecksum: "
|
|
55
|
+
version: "1.2.2",
|
|
56
|
+
expectedSize: 24414208,
|
|
57
|
+
expectedChecksum: "7e263c2b2bc9055ead2db8102e883534",
|
|
58
58
|
}
|
|
59
59
|
: {
|
|
60
|
-
version: "1.2.
|
|
61
|
-
expectedSize:
|
|
62
|
-
expectedChecksum: "
|
|
60
|
+
version: "1.2.2",
|
|
61
|
+
expectedSize: 24023300,
|
|
62
|
+
expectedChecksum: "12467418226ac9657fb64b4d719d0e1d",
|
|
63
63
|
},
|
|
64
64
|
};
|
|
65
65
|
exports.DownloadDetails = {
|
|
@@ -139,7 +139,7 @@ exports.DownloadDetails = {
|
|
|
139
139
|
expectedChecksum: EMULATOR_UPDATE_DETAILS.dataconnect.expectedChecksum,
|
|
140
140
|
skipChecksumAndSize: false,
|
|
141
141
|
namePrefix: "dataconnect-emulator",
|
|
142
|
-
auth:
|
|
142
|
+
auth: false,
|
|
143
143
|
},
|
|
144
144
|
},
|
|
145
145
|
};
|
|
@@ -224,7 +224,7 @@ const Commands = {
|
|
|
224
224
|
shell: false,
|
|
225
225
|
},
|
|
226
226
|
pubsub: {
|
|
227
|
-
binary: getExecPath(types_1.Emulators.PUBSUB)
|
|
227
|
+
binary: `${getExecPath(types_1.Emulators.PUBSUB)}`,
|
|
228
228
|
args: [],
|
|
229
229
|
optionalArgs: ["port", "host"],
|
|
230
230
|
joinArgs: true,
|
|
@@ -238,16 +238,14 @@ const Commands = {
|
|
|
238
238
|
shell: false,
|
|
239
239
|
},
|
|
240
240
|
dataconnect: {
|
|
241
|
-
binary: getExecPath(types_1.Emulators.DATACONNECT)
|
|
242
|
-
args: ["dev"],
|
|
241
|
+
binary: `${getExecPath(types_1.Emulators.DATACONNECT)}`,
|
|
242
|
+
args: ["--logtostderr", "-v=2", "dev"],
|
|
243
243
|
optionalArgs: [
|
|
244
244
|
"listen",
|
|
245
245
|
"config_dir",
|
|
246
|
-
"project_id",
|
|
247
246
|
"service_location",
|
|
248
247
|
"disable_sdk_generation",
|
|
249
248
|
"resolvers_emulator",
|
|
250
|
-
"vertex_location",
|
|
251
249
|
"rpc_retry_count",
|
|
252
250
|
],
|
|
253
251
|
joinArgs: true,
|
|
@@ -342,6 +340,9 @@ async function _runBinary(emulator, command, extraEnv) {
|
|
|
342
340
|
};
|
|
343
341
|
if (command.shell && utils.IS_WINDOWS) {
|
|
344
342
|
opts.shell = true;
|
|
343
|
+
if (command.binary.includes(" ")) {
|
|
344
|
+
command.binary = `"${command.binary}"`;
|
|
345
|
+
}
|
|
345
346
|
}
|
|
346
347
|
emulator.instance = childProcess.spawn(command.binary, command.args, opts);
|
|
347
348
|
}
|
package/lib/emulator/registry.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.sendFileBytes = void 0;
|
|
4
4
|
const zlib_1 = require("zlib");
|
|
5
5
|
const crc_1 = require("../crc");
|
|
6
|
+
const rfc_1 = require("../rfc");
|
|
6
7
|
function sendFileBytes(md, data, req, res) {
|
|
7
8
|
let didGunzip = false;
|
|
8
9
|
if (md.contentEncoding === "gzip") {
|
|
@@ -16,7 +17,7 @@ function sendFileBytes(md, data, req, res) {
|
|
|
16
17
|
res.setHeader("Accept-Ranges", "bytes");
|
|
17
18
|
res.setHeader("Content-Type", md.contentType || "application/octet-stream");
|
|
18
19
|
const fileName = md.name.split("/").pop();
|
|
19
|
-
res.setHeader("Content-Disposition", `${md.contentDisposition || "attachment"}; filename
|
|
20
|
+
res.setHeader("Content-Disposition", `${md.contentDisposition || "attachment"}; filename*=${(0, rfc_1.encodeRFC5987)(fileName)}`);
|
|
20
21
|
if (didGunzip) {
|
|
21
22
|
res.setHeader("Transfer-Encoding", "chunked");
|
|
22
23
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Persistence = void 0;
|
|
4
4
|
const fs_1 = require("fs");
|
|
5
|
-
const
|
|
5
|
+
const rimraf_1 = require("rimraf");
|
|
6
6
|
const fs = require("fs");
|
|
7
7
|
const fse = require("fs-extra");
|
|
8
8
|
const path = require("path");
|
|
@@ -56,18 +56,10 @@ class Persistence {
|
|
|
56
56
|
}
|
|
57
57
|
this._diskPathMap.delete(fileName);
|
|
58
58
|
}
|
|
59
|
-
deleteAll() {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
reject(err);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
this._diskPathMap = new Map();
|
|
67
|
-
resolve();
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
});
|
|
59
|
+
async deleteAll() {
|
|
60
|
+
await (0, rimraf_1.rimraf)(this._dirPath);
|
|
61
|
+
this._diskPathMap = new Map();
|
|
62
|
+
return;
|
|
71
63
|
}
|
|
72
64
|
renameFile(oldName, newName) {
|
|
73
65
|
const oldNameId = this.getDiskFileName(oldName);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.encodeRFC5987 = void 0;
|
|
4
|
+
function encodeRFC5987(str) {
|
|
5
|
+
return encodeURIComponent(str)
|
|
6
|
+
.replace(/['()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`)
|
|
7
|
+
.replace(/%(7C|60|5E)/g, (str, hex) => String.fromCharCode(parseInt(hex, 16)));
|
|
8
|
+
}
|
|
9
|
+
exports.encodeRFC5987 = encodeRFC5987;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chai_1 = require("chai");
|
|
4
|
+
const firestore_1 = require("../gcp/firestore");
|
|
5
|
+
const proto_1 = require("../gcp/proto");
|
|
6
|
+
const sort = require("./api-sort");
|
|
7
|
+
describe("compareApiBackup", () => {
|
|
8
|
+
it("should compare backups by location", () => {
|
|
9
|
+
const nam5Backup = {
|
|
10
|
+
name: "projects/example/locations/nam5/backups/backupid",
|
|
11
|
+
};
|
|
12
|
+
const usWest1Backup = {
|
|
13
|
+
name: "projects/example/locations/us-west1/backups/backupid",
|
|
14
|
+
};
|
|
15
|
+
(0, chai_1.expect)(sort.compareApiBackup(usWest1Backup, nam5Backup)).to.greaterThanOrEqual(1);
|
|
16
|
+
(0, chai_1.expect)(sort.compareApiBackup(nam5Backup, usWest1Backup)).to.lessThanOrEqual(-1);
|
|
17
|
+
});
|
|
18
|
+
it("should compare backups by snapshotTime (descending) if location is the same", () => {
|
|
19
|
+
const earlierBackup = {
|
|
20
|
+
name: "projects/example/locations/nam5/backups/backupid",
|
|
21
|
+
snapshotTime: "2024-01-01T00:00:00.000000Z",
|
|
22
|
+
};
|
|
23
|
+
const laterBackup = {
|
|
24
|
+
name: "projects/example/locations/nam5/backups/backupid",
|
|
25
|
+
snapshotTime: "2024-02-02T00:00:00.000000Z",
|
|
26
|
+
};
|
|
27
|
+
(0, chai_1.expect)(sort.compareApiBackup(earlierBackup, laterBackup)).to.greaterThanOrEqual(1);
|
|
28
|
+
(0, chai_1.expect)(sort.compareApiBackup(laterBackup, earlierBackup)).to.lessThanOrEqual(-1);
|
|
29
|
+
});
|
|
30
|
+
it("should compare backups by full name if location and snapshotTime are the same", () => {
|
|
31
|
+
const nam5Backup1 = {
|
|
32
|
+
name: "projects/example/locations/nam5/backups/earlier-backupid",
|
|
33
|
+
snapshotTime: "2024-01-01T00:00:00.000000Z",
|
|
34
|
+
};
|
|
35
|
+
const nam5Backup2 = {
|
|
36
|
+
name: "projects/example/locations/nam5/backups/later-backupid",
|
|
37
|
+
snapshotTime: "2024-01-01T00:00:00.000000Z",
|
|
38
|
+
};
|
|
39
|
+
(0, chai_1.expect)(sort.compareApiBackup(nam5Backup2, nam5Backup1)).to.greaterThanOrEqual(1);
|
|
40
|
+
(0, chai_1.expect)(sort.compareApiBackup(nam5Backup1, nam5Backup2)).to.lessThanOrEqual(-1);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe("compareApiBackupSchedule", () => {
|
|
44
|
+
it("daily schedules should precede weekly ones", () => {
|
|
45
|
+
const dailySchedule = {
|
|
46
|
+
name: "projects/example/databases/mydatabase/backupSchedules/schedule",
|
|
47
|
+
dailyRecurrence: {},
|
|
48
|
+
retention: (0, proto_1.durationFromSeconds)(60 * 60 * 24),
|
|
49
|
+
};
|
|
50
|
+
const weeklySchedule = {
|
|
51
|
+
name: "projects/example/databases/mydatabase/backupSchedules/schedule",
|
|
52
|
+
weeklyRecurrence: {
|
|
53
|
+
day: firestore_1.DayOfWeek.FRIDAY,
|
|
54
|
+
},
|
|
55
|
+
retention: (0, proto_1.durationFromSeconds)(60 * 60 * 24 * 7),
|
|
56
|
+
};
|
|
57
|
+
(0, chai_1.expect)(sort.compareApiBackupSchedule(weeklySchedule, dailySchedule)).to.greaterThanOrEqual(1);
|
|
58
|
+
(0, chai_1.expect)(sort.compareApiBackup(dailySchedule, weeklySchedule)).to.lessThanOrEqual(-1);
|
|
59
|
+
});
|
|
60
|
+
it("should compare schedules with the same recurrence by name", () => {
|
|
61
|
+
const dailySchedule1 = {
|
|
62
|
+
name: "projects/example/databases/mydatabase/backupSchedules/schedule1",
|
|
63
|
+
dailyRecurrence: {},
|
|
64
|
+
retention: (0, proto_1.durationFromSeconds)(60 * 60 * 24),
|
|
65
|
+
};
|
|
66
|
+
const dailySchedule2 = {
|
|
67
|
+
name: "projects/example/databases/mydatabase/backupSchedules/schedule2",
|
|
68
|
+
dailyRecurrence: {},
|
|
69
|
+
retention: (0, proto_1.durationFromSeconds)(60 * 60 * 24),
|
|
70
|
+
};
|
|
71
|
+
(0, chai_1.expect)(sort.compareApiBackupSchedule(dailySchedule1, dailySchedule2)).to.lessThanOrEqual(-1);
|
|
72
|
+
(0, chai_1.expect)(sort.compareApiBackup(dailySchedule2, dailySchedule1)).to.greaterThanOrEqual(1);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chai_1 = require("chai");
|
|
4
|
+
const backupUtils_1 = require("./backupUtils");
|
|
5
|
+
describe("calculateRetention", () => {
|
|
6
|
+
it("should accept minutes", () => {
|
|
7
|
+
(0, chai_1.expect)((0, backupUtils_1.calculateRetention)("5m")).to.eq(300);
|
|
8
|
+
});
|
|
9
|
+
it("should accept hours", () => {
|
|
10
|
+
(0, chai_1.expect)((0, backupUtils_1.calculateRetention)("3h")).to.eq(10800);
|
|
11
|
+
});
|
|
12
|
+
it("should accept days", () => {
|
|
13
|
+
(0, chai_1.expect)((0, backupUtils_1.calculateRetention)("2d")).to.eq(172800);
|
|
14
|
+
});
|
|
15
|
+
it("should accept weeks", () => {
|
|
16
|
+
(0, chai_1.expect)((0, backupUtils_1.calculateRetention)("3w")).to.eq(1814400);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chai_1 = require("chai");
|
|
4
|
+
const API = require("./api-types");
|
|
5
|
+
const pretty_print_1 = require("./pretty-print");
|
|
6
|
+
const printer = new pretty_print_1.PrettyPrint();
|
|
7
|
+
describe("prettyIndexString", () => {
|
|
8
|
+
it("should correctly print an order type Index", () => {
|
|
9
|
+
(0, chai_1.expect)(printer.prettyIndexString({
|
|
10
|
+
name: "/projects/project/databases/(default)/collectionGroups/collectionB/indexes/a",
|
|
11
|
+
queryScope: API.QueryScope.COLLECTION,
|
|
12
|
+
fields: [
|
|
13
|
+
{ fieldPath: "foo", order: API.Order.ASCENDING },
|
|
14
|
+
{ fieldPath: "bar", order: API.Order.DESCENDING },
|
|
15
|
+
],
|
|
16
|
+
}, false)).to.contain("(foo,ASCENDING) (bar,DESCENDING) ");
|
|
17
|
+
});
|
|
18
|
+
it("should correctly print a contains type Index", () => {
|
|
19
|
+
(0, chai_1.expect)(printer.prettyIndexString({
|
|
20
|
+
name: "/projects/project/databases/(default)/collectionGroups/collectionB/indexes/a",
|
|
21
|
+
queryScope: API.QueryScope.COLLECTION,
|
|
22
|
+
fields: [
|
|
23
|
+
{ fieldPath: "foo", order: API.Order.ASCENDING },
|
|
24
|
+
{ fieldPath: "baz", arrayConfig: API.ArrayConfig.CONTAINS },
|
|
25
|
+
],
|
|
26
|
+
}, false)).to.contain("(foo,ASCENDING) (baz,CONTAINS) ");
|
|
27
|
+
});
|
|
28
|
+
it("should correctly print a vector type Index", () => {
|
|
29
|
+
(0, chai_1.expect)(printer.prettyIndexString({
|
|
30
|
+
name: "/projects/project/databases/(default)/collectionGroups/collectionB/indexes/a",
|
|
31
|
+
queryScope: API.QueryScope.COLLECTION,
|
|
32
|
+
fields: [{ fieldPath: "foo", vectorConfig: { dimension: 100, flat: {} } }],
|
|
33
|
+
}, false)).to.contain("(foo,VECTOR<100>) ");
|
|
34
|
+
});
|
|
35
|
+
it("should correctly print a vector type Index with other fields", () => {
|
|
36
|
+
(0, chai_1.expect)(printer.prettyIndexString({
|
|
37
|
+
name: "/projects/project/databases/(default)/collectionGroups/collectionB/indexes/a",
|
|
38
|
+
queryScope: API.QueryScope.COLLECTION,
|
|
39
|
+
fields: [
|
|
40
|
+
{ fieldPath: "foo", order: API.Order.ASCENDING },
|
|
41
|
+
{ fieldPath: "bar", vectorConfig: { dimension: 200, flat: {} } },
|
|
42
|
+
],
|
|
43
|
+
}, false)).to.contain("(foo,ASCENDING) (bar,VECTOR<200>) ");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe("firebaseConsoleDatabaseUrl", () => {
|
|
47
|
+
it("should provide a console link", () => {
|
|
48
|
+
(0, chai_1.expect)(printer.firebaseConsoleDatabaseUrl("example-project", "example-db")).to.equal("https://console.firebase.google.com/project/example-project/firestore/databases/example-db/data");
|
|
49
|
+
});
|
|
50
|
+
it("should convert (default) to -default-", () => {
|
|
51
|
+
(0, chai_1.expect)(printer.firebaseConsoleDatabaseUrl("example-project", "(default)")).to.equal("https://console.firebase.google.com/project/example-project/firestore/databases/-default-/data");
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const chai_1 = require("chai");
|
|
4
|
+
const util = require("./util");
|
|
5
|
+
describe("IndexNameParsing", () => {
|
|
6
|
+
it("should parse an index name correctly", () => {
|
|
7
|
+
const name = "/projects/myproject/databases/(default)/collectionGroups/collection/indexes/abc123/";
|
|
8
|
+
(0, chai_1.expect)(util.parseIndexName(name)).to.eql({
|
|
9
|
+
projectId: "myproject",
|
|
10
|
+
databaseId: "(default)",
|
|
11
|
+
collectionGroupId: "collection",
|
|
12
|
+
indexId: "abc123",
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
it("should parse a field name correctly", () => {
|
|
16
|
+
const name = "/projects/myproject/databases/(default)/collectionGroups/collection/fields/abc123/";
|
|
17
|
+
(0, chai_1.expect)(util.parseFieldName(name)).to.eql({
|
|
18
|
+
projectId: "myproject",
|
|
19
|
+
databaseId: "(default)",
|
|
20
|
+
collectionGroupId: "collection",
|
|
21
|
+
fieldPath: "abc123",
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
it("should parse an index name from a named database correctly", () => {
|
|
25
|
+
const name = "/projects/myproject/databases/named-db/collectionGroups/collection/indexes/abc123/";
|
|
26
|
+
(0, chai_1.expect)(util.parseIndexName(name)).to.eql({
|
|
27
|
+
projectId: "myproject",
|
|
28
|
+
databaseId: "named-db",
|
|
29
|
+
collectionGroupId: "collection",
|
|
30
|
+
indexId: "abc123",
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
it("should parse a field name from a named database correctly", () => {
|
|
34
|
+
const name = "/projects/myproject/databases/named-db/collectionGroups/collection/fields/abc123/";
|
|
35
|
+
(0, chai_1.expect)(util.parseFieldName(name)).to.eql({
|
|
36
|
+
projectId: "myproject",
|
|
37
|
+
databaseId: "named-db",
|
|
38
|
+
collectionGroupId: "collection",
|
|
39
|
+
fieldPath: "abc123",
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MockFileSystem = void 0;
|
|
4
|
+
class MockFileSystem {
|
|
5
|
+
constructor(fileSys) {
|
|
6
|
+
this.fileSys = fileSys;
|
|
7
|
+
this.existsCache = {};
|
|
8
|
+
this.contentCache = {};
|
|
9
|
+
}
|
|
10
|
+
exists(path) {
|
|
11
|
+
if (!(path in this.existsCache)) {
|
|
12
|
+
this.existsCache[path] = path in this.fileSys;
|
|
13
|
+
}
|
|
14
|
+
return Promise.resolve(this.existsCache[path]);
|
|
15
|
+
}
|
|
16
|
+
read(path) {
|
|
17
|
+
if (!(path in this.contentCache)) {
|
|
18
|
+
if (!(path in this.fileSys)) {
|
|
19
|
+
const err = new Error("File path not found");
|
|
20
|
+
err.cause = "ENOENT";
|
|
21
|
+
throw err;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
this.contentCache[path] = this.fileSys[path];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return Promise.resolve(this.contentCache[path]);
|
|
28
|
+
}
|
|
29
|
+
getContentCache(path) {
|
|
30
|
+
return this.contentCache[path];
|
|
31
|
+
}
|
|
32
|
+
getExistsCache(path) {
|
|
33
|
+
return this.existsCache[path];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.MockFileSystem = MockFileSystem;
|
package/lib/frameworks/index.js
CHANGED
|
@@ -7,8 +7,8 @@ const child_process_1 = require("child_process");
|
|
|
7
7
|
const cross_spawn_1 = require("cross-spawn");
|
|
8
8
|
const promises_1 = require("fs/promises");
|
|
9
9
|
const fs_extra_1 = require("fs-extra");
|
|
10
|
+
const glob_1 = require("glob");
|
|
10
11
|
const process = require("node:process");
|
|
11
|
-
const glob = require("glob");
|
|
12
12
|
const projectUtils_1 = require("../projectUtils");
|
|
13
13
|
const config_1 = require("../hosting/config");
|
|
14
14
|
const api_1 = require("../hosting/api");
|
|
@@ -366,11 +366,7 @@ async function prepareFrameworks(purpose, targetNames, context, options, emulato
|
|
|
366
366
|
await (0, promises_1.writeFile)((0, path_1.join)(functionsDist, ".env"), `${dotEnvContents}
|
|
367
367
|
__FIREBASE_FRAMEWORKS_ENTRY__=${frameworksEntry}
|
|
368
368
|
${firebaseDefaults ? `__FIREBASE_DEFAULTS__=${JSON.stringify(firebaseDefaults)}\n` : ""}`.trimStart());
|
|
369
|
-
const envs = await
|
|
370
|
-
if (err)
|
|
371
|
-
reject(err);
|
|
372
|
-
resolve(matches);
|
|
373
|
-
}));
|
|
369
|
+
const envs = await (0, glob_1.glob)(getProjectPath(".env.*"));
|
|
374
370
|
await Promise.all(envs.map((path) => (0, promises_1.copyFile)(path, (0, path_1.join)(functionsDist, (0, path_1.basename)(path)))));
|
|
375
371
|
(0, child_process_1.execSync)(`npm i --omit dev --no-audit`, {
|
|
376
372
|
cwd: functionsDist,
|
|
@@ -6,7 +6,6 @@ const fs_extra_1 = require("fs-extra");
|
|
|
6
6
|
const path_1 = require("path");
|
|
7
7
|
const promises_1 = require("fs/promises");
|
|
8
8
|
const glob_1 = require("glob");
|
|
9
|
-
const glob = require("glob");
|
|
10
9
|
const semver_1 = require("semver");
|
|
11
10
|
const utils_1 = require("../utils");
|
|
12
11
|
const constants_1 = require("./constants");
|
|
@@ -226,17 +225,12 @@ function getRoutesWithServerAction(serverReferenceManifest, appPathRoutesManifes
|
|
|
226
225
|
}
|
|
227
226
|
exports.getRoutesWithServerAction = getRoutesWithServerAction;
|
|
228
227
|
async function getProductionDistDirFiles(sourceDir, distDir) {
|
|
229
|
-
|
|
228
|
+
return (0, glob_1.glob)("**", {
|
|
230
229
|
ignore: [(0, path_1.join)("cache", "webpack", "*-development", "**"), (0, path_1.join)("cache", "eslint", "**")],
|
|
231
230
|
cwd: (0, path_1.join)(sourceDir, distDir),
|
|
232
231
|
nodir: true,
|
|
233
232
|
absolute: false,
|
|
234
|
-
}
|
|
235
|
-
if (err)
|
|
236
|
-
reject(err);
|
|
237
|
-
resolve(matches);
|
|
238
|
-
}));
|
|
239
|
-
return productionDistDirFiles;
|
|
233
|
+
});
|
|
240
234
|
}
|
|
241
235
|
exports.getProductionDistDirFiles = getProductionDistDirFiles;
|
|
242
236
|
async function whichNextConfigFile(dir) {
|
package/lib/listFiles.js
CHANGED
package/lib/rc.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firebase-tools",
|
|
3
|
-
"version": "13.11.
|
|
3
|
+
"version": "13.11.3",
|
|
4
4
|
"description": "Command-Line Interface for Firebase",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -54,6 +54,8 @@
|
|
|
54
54
|
".ts"
|
|
55
55
|
],
|
|
56
56
|
"exclude": [
|
|
57
|
+
"src/**/*.spec.*",
|
|
58
|
+
"src/**/testing/**/*",
|
|
57
59
|
"src/test/**/*"
|
|
58
60
|
]
|
|
59
61
|
},
|
|
@@ -62,7 +64,7 @@
|
|
|
62
64
|
"@google-cloud/pubsub": "^4.4.0",
|
|
63
65
|
"abort-controller": "^3.0.0",
|
|
64
66
|
"ajv": "^6.12.6",
|
|
65
|
-
"archiver": "^
|
|
67
|
+
"archiver": "^7.0.0",
|
|
66
68
|
"async-lock": "1.3.2",
|
|
67
69
|
"body-parser": "^1.19.0",
|
|
68
70
|
"chokidar": "^3.0.2",
|
|
@@ -76,7 +78,7 @@
|
|
|
76
78
|
"cross-spawn": "^7.0.3",
|
|
77
79
|
"csv-parse": "^5.0.4",
|
|
78
80
|
"deep-equal-in-any-order": "^2.0.6",
|
|
79
|
-
"exegesis": "^4.1.
|
|
81
|
+
"exegesis": "^4.1.2",
|
|
80
82
|
"exegesis-express": "^4.0.0",
|
|
81
83
|
"express": "^4.16.4",
|
|
82
84
|
"filesize": "^6.1.0",
|
|
@@ -84,7 +86,7 @@
|
|
|
84
86
|
"fs-extra": "^10.1.0",
|
|
85
87
|
"fuzzy": "^0.1.3",
|
|
86
88
|
"gaxios": "^6.1.1",
|
|
87
|
-
"glob": "^
|
|
89
|
+
"glob": "^10.4.1",
|
|
88
90
|
"google-auth-library": "^9.7.0",
|
|
89
91
|
"inquirer": "^8.2.6",
|
|
90
92
|
"inquirer-autocomplete-prompt": "^2.0.1",
|
|
@@ -106,7 +108,7 @@
|
|
|
106
108
|
"progress": "^2.0.3",
|
|
107
109
|
"proxy-agent": "^6.3.0",
|
|
108
110
|
"retry": "^0.13.1",
|
|
109
|
-
"rimraf": "^
|
|
111
|
+
"rimraf": "^5.0.0",
|
|
110
112
|
"semver": "^7.5.2",
|
|
111
113
|
"sql-formatter": "^15.3.0",
|
|
112
114
|
"stream-chain": "^2.2.4",
|
|
@@ -115,7 +117,7 @@
|
|
|
115
117
|
"superstatic": "^9.0.3",
|
|
116
118
|
"tar": "^6.1.11",
|
|
117
119
|
"tcp-port-used": "^1.0.2",
|
|
118
|
-
"tmp": "^0.2.
|
|
120
|
+
"tmp": "^0.2.3",
|
|
119
121
|
"triple-beam": "^1.3.0",
|
|
120
122
|
"universal-analytics": "^0.5.3",
|
|
121
123
|
"update-notifier-cjs": "^5.1.6",
|
|
@@ -1,30 +1,35 @@
|
|
|
1
1
|
# # Example mutations for a simple email app
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
|
|
3
|
+
# # Logged in user can create their own account.
|
|
4
|
+
# mutation CreateUser($name: String!, $address: String!) @auth(level: USER) {
|
|
5
|
+
# # <type>_insert lets you create a new row in your table.
|
|
5
6
|
# user_insert(data: {
|
|
6
|
-
#
|
|
7
|
+
# # Server values let your service populate sensitive data.
|
|
8
|
+
# # Users can only setup their own account.
|
|
9
|
+
# uid_expr: "auth.uid",
|
|
7
10
|
# name: $name,
|
|
8
11
|
# address: $address
|
|
9
12
|
# })
|
|
10
13
|
# }
|
|
11
|
-
|
|
14
|
+
|
|
15
|
+
# # Logged in user can send emails from their account.
|
|
16
|
+
# mutation CreateEmail($content: String, $subject: String) @auth(level: USER) {
|
|
12
17
|
# email_insert(data: {
|
|
13
|
-
#
|
|
18
|
+
# # The request variable name doesn't have to match the field name.
|
|
19
|
+
# text: $content,
|
|
14
20
|
# subject: $subject,
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
|
|
21
|
+
# # Server values let your service populate sensitive data.
|
|
22
|
+
# # Users are only allowed to create emails sent from their account.
|
|
23
|
+
# fromUid_expr: "auth.uid",
|
|
24
|
+
# # Server values let your service populate data for you
|
|
25
|
+
# # Here, we use sent_date: { today: true } to set 'sent' to today's date.
|
|
18
26
|
# sent_date: { today: true }
|
|
19
27
|
# })
|
|
20
28
|
# }
|
|
21
|
-
|
|
29
|
+
|
|
30
|
+
# mutation CreateRecipient($emailId: UUID) @auth(level: USER) {
|
|
22
31
|
# recipient_insert(data: {
|
|
23
32
|
# emailId: $emailId,
|
|
24
|
-
#
|
|
33
|
+
# userUid_expr: "auth.uid"
|
|
25
34
|
# })
|
|
26
35
|
# }
|
|
27
|
-
# mutation DeleteEmail($emailId: UUID, $uid: String) @auth(level: PUBLIC) {
|
|
28
|
-
## <type>_ delete lets you delete rows from your table.
|
|
29
|
-
# recipient_delete(key: {emailId: $emailId, userUid: $uid})
|
|
30
|
-
# }
|
|
@@ -1,51 +1,56 @@
|
|
|
1
1
|
# # Example queries for a simple email app.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
# query
|
|
6
|
-
#
|
|
3
|
+
# # @auth() directives control who can call each operation.
|
|
4
|
+
# # Only admins should be able to list all emails, so we use NO_ACCESS
|
|
5
|
+
# query ListEmails @auth(level: NO_ACCESS) {
|
|
6
|
+
# emails {
|
|
7
|
+
# id, subject, text, sent
|
|
8
|
+
# from {
|
|
9
|
+
# name
|
|
10
|
+
# }
|
|
11
|
+
# }
|
|
7
12
|
# }
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
# query
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
# # Only admins should be able to list all users, so we use NO_ACCESS
|
|
15
|
+
# query ListUsers @auth(level: NO_ACCESS) {
|
|
16
|
+
# users { uid, name, address }
|
|
17
|
+
# }
|
|
18
|
+
|
|
19
|
+
# # Logged in users should be able to see their inbox though, so we use USER
|
|
20
|
+
# query ListInbox @auth(level: USER) {
|
|
21
|
+
# # where allows you to filter lists
|
|
22
|
+
# # Here, we use it to filter to only emails that are sent to the logged in user.
|
|
15
23
|
# emails(where: {
|
|
16
24
|
# users_via_Recipient: {
|
|
17
|
-
# exist: { uid: {
|
|
25
|
+
# exist: { uid: { eq_expr: "auth.uid" }
|
|
18
26
|
# }}
|
|
19
27
|
# }) {
|
|
20
28
|
# id subject sent
|
|
21
29
|
# content: text # Select the `text` field but alias it as `content` in the response.
|
|
22
|
-
# sender: from { name
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
## Here, we use it to grab all the recipients of the email.
|
|
30
|
+
# sender: from { name address uid }
|
|
31
|
+
# # <field>_on_<foreign_key_field> makes it easy to grab info from another table
|
|
32
|
+
# # Here, we use it to grab all the recipients of the email.
|
|
26
33
|
# to: recipients_on_email {
|
|
27
|
-
# user { name
|
|
34
|
+
# user { name address uid }
|
|
28
35
|
# }
|
|
29
36
|
# }
|
|
30
37
|
# }
|
|
31
38
|
|
|
32
39
|
# query GetUidByEmail($emails: [String!]) @auth(level: PUBLIC) {
|
|
33
40
|
# users(where: { address: { in: $emails } }) {
|
|
34
|
-
# uid
|
|
41
|
+
# uid address
|
|
35
42
|
# }
|
|
36
43
|
# }
|
|
37
44
|
|
|
38
|
-
# query ListSent(
|
|
39
|
-
# $uid: String
|
|
40
|
-
# ) @auth(level: PUBLIC) {
|
|
45
|
+
# query ListSent($uid: String) @auth(level: PUBLIC) {
|
|
41
46
|
# emails(where: {
|
|
42
|
-
#
|
|
47
|
+
# from: {uid: {eq: $uid }}
|
|
43
48
|
# }) {
|
|
44
49
|
# id subject sent
|
|
45
50
|
# content: text
|
|
46
|
-
# sender: from { name
|
|
51
|
+
# sender: from { name address uid }
|
|
47
52
|
# to: recipients_on_email {
|
|
48
|
-
# user { name
|
|
53
|
+
# user { name address uid }
|
|
49
54
|
# }
|
|
50
55
|
# }
|
|
51
56
|
# }
|