firebase-tools 13.15.0 → 13.15.2
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/commands/deploy.js +3 -1
- package/lib/dataconnect/fileUtils.js +52 -11
- package/lib/dataconnect/provisionCloudSql.js +5 -1
- package/lib/deploy/extensions/planner.js +46 -1
- package/lib/deploy/extensions/prepare.js +99 -23
- package/lib/deploy/functions/build.js +5 -5
- package/lib/deploy/functions/deploy.js +12 -12
- package/lib/deploy/functions/params.js +5 -3
- package/lib/deploy/functions/prepare.js +16 -1
- package/lib/deploy/functions/release/index.js +4 -0
- package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +46 -0
- package/lib/emulator/downloadableEmulators.js +9 -9
- package/lib/emulator/functionsEmulator.js +8 -1
- package/lib/extensions/askUserForEventsConfig.js +18 -8
- package/lib/extensions/askUserForParam.js +2 -1
- package/lib/extensions/displayExtensionInfo.js +5 -5
- package/lib/extensions/extensionsApi.js +1 -1
- package/lib/extensions/extensionsHelper.js +42 -3
- package/lib/extensions/localHelper.js +1 -1
- package/lib/extensions/refs.js +26 -11
- package/lib/extensions/runtimes/common.js +75 -0
- package/lib/extensions/types.js +56 -1
- package/lib/frameworks/constants.js +1 -1
- package/lib/frameworks/next/constants.js +1 -1
- package/lib/frameworks/next/index.js +35 -15
- package/lib/frameworks/next/utils.js +53 -1
- package/lib/init/features/dataconnect/index.js +10 -7
- package/lib/init/features/dataconnect/sdk.js +67 -51
- package/lib/prompt.js +22 -1
- package/package.json +1 -1
- package/templates/init/dataconnect/connector.yaml +5 -3
- package/templates/init/dataconnect/dataconnect.yaml +5 -5
- package/templates/init/dataconnect/mutations.gql +44 -29
- package/templates/init/dataconnect/queries.gql +66 -38
- package/templates/init/dataconnect/schema.gql +38 -21
|
@@ -21,6 +21,7 @@ const QUERIES_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/que
|
|
|
21
21
|
const MUTATIONS_TEMPLATE = (0, templates_1.readTemplateSync)("init/dataconnect/mutations.gql");
|
|
22
22
|
const defaultConnector = {
|
|
23
23
|
id: "default",
|
|
24
|
+
path: "./connector",
|
|
24
25
|
files: [
|
|
25
26
|
{
|
|
26
27
|
path: "queries.gql",
|
|
@@ -92,7 +93,7 @@ exports.actuate = actuate;
|
|
|
92
93
|
async function writeFiles(config, info) {
|
|
93
94
|
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
94
95
|
console.log(dir);
|
|
95
|
-
const subbedDataconnectYaml = subDataconnectYamlValues(Object.assign(Object.assign({}, info), {
|
|
96
|
+
const subbedDataconnectYaml = subDataconnectYamlValues(Object.assign(Object.assign({}, info), { connectorDirs: info.connectors.map((c) => c.path) }));
|
|
96
97
|
config.set("dataconnect", { source: dir });
|
|
97
98
|
await config.askWriteProjectFile((0, path_1.join)(dir, "dataconnect.yaml"), subbedDataconnectYaml);
|
|
98
99
|
if (info.schemaGql.length) {
|
|
@@ -111,9 +112,9 @@ async function writeFiles(config, info) {
|
|
|
111
112
|
async function writeConnectorFiles(config, connectorInfo) {
|
|
112
113
|
const subbedConnectorYaml = subConnectorYamlValues({ connectorId: connectorInfo.id });
|
|
113
114
|
const dir = config.get("dataconnect.source") || "dataconnect";
|
|
114
|
-
await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.
|
|
115
|
+
await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.path, "connector.yaml"), subbedConnectorYaml);
|
|
115
116
|
for (const f of connectorInfo.files) {
|
|
116
|
-
await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.
|
|
117
|
+
await config.askWriteProjectFile((0, path_1.join)(dir, connectorInfo.path, f.path), f.content);
|
|
117
118
|
}
|
|
118
119
|
}
|
|
119
120
|
function subDataconnectYamlValues(replacementValues) {
|
|
@@ -121,12 +122,12 @@ function subDataconnectYamlValues(replacementValues) {
|
|
|
121
122
|
serviceId: "__serviceId__",
|
|
122
123
|
cloudSqlDatabase: "__cloudSqlDatabase__",
|
|
123
124
|
cloudSqlInstanceId: "__cloudSqlInstanceId__",
|
|
124
|
-
|
|
125
|
+
connectorDirs: "__connectorDirs__",
|
|
125
126
|
locationId: "__location__",
|
|
126
127
|
};
|
|
127
128
|
let replaced = DATACONNECT_YAML_TEMPLATE;
|
|
128
129
|
for (const [k, v] of Object.entries(replacementValues)) {
|
|
129
|
-
replaced = replaced.replace(replacements[k], v);
|
|
130
|
+
replaced = replaced.replace(replacements[k], JSON.stringify(v));
|
|
130
131
|
}
|
|
131
132
|
return replaced;
|
|
132
133
|
}
|
|
@@ -136,7 +137,7 @@ function subConnectorYamlValues(replacementValues) {
|
|
|
136
137
|
};
|
|
137
138
|
let replaced = CONNECTOR_YAML_TEMPLATE;
|
|
138
139
|
for (const [k, v] of Object.entries(replacementValues)) {
|
|
139
|
-
replaced = replaced.replace(replacements[k], v);
|
|
140
|
+
replaced = replaced.replace(replacements[k], JSON.stringify(v));
|
|
140
141
|
}
|
|
141
142
|
return replaced;
|
|
142
143
|
}
|
|
@@ -181,8 +182,10 @@ async function promptForService(setup, info) {
|
|
|
181
182
|
const connectors = await (0, client_1.listConnectors)(choice.service.name);
|
|
182
183
|
if (connectors.length) {
|
|
183
184
|
info.connectors = connectors.map((c) => {
|
|
185
|
+
const id = c.name.split("/").pop();
|
|
184
186
|
return {
|
|
185
|
-
id
|
|
187
|
+
id,
|
|
188
|
+
path: `./${id}`,
|
|
186
189
|
files: c.source.files || [],
|
|
187
190
|
};
|
|
188
191
|
});
|
|
@@ -4,23 +4,22 @@ exports.actuate = exports.doSetup = void 0;
|
|
|
4
4
|
const yaml = require("yaml");
|
|
5
5
|
const fs = require("fs");
|
|
6
6
|
const clc = require("colorette");
|
|
7
|
+
const path = require("path");
|
|
7
8
|
const prompt_1 = require("../../../prompt");
|
|
8
9
|
const fileUtils_1 = require("../../../dataconnect/fileUtils");
|
|
9
10
|
const load_1 = require("../../../dataconnect/load");
|
|
10
|
-
const
|
|
11
|
+
const types_1 = require("../../../dataconnect/types");
|
|
11
12
|
const dataconnectEmulator_1 = require("../../../emulator/dataconnectEmulator");
|
|
12
13
|
const error_1 = require("../../../error");
|
|
13
14
|
const lodash_1 = require("lodash");
|
|
14
|
-
const
|
|
15
|
-
const WEB = "web";
|
|
16
|
-
const ANDROID = "android";
|
|
15
|
+
const utils_1 = require("../../../utils");
|
|
17
16
|
async function doSetup(setup, config) {
|
|
18
17
|
const sdkInfo = await askQuestions(setup, config);
|
|
19
18
|
await actuate(sdkInfo, setup.projectId);
|
|
20
19
|
}
|
|
21
20
|
exports.doSetup = doSetup;
|
|
22
21
|
async function askQuestions(setup, config) {
|
|
23
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
22
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
24
23
|
const serviceCfgs = (0, fileUtils_1.readFirebaseJson)(config);
|
|
25
24
|
const serviceInfos = await Promise.all(serviceCfgs.map((c) => (0, load_1.load)(setup.projectId || "", config, c.source)));
|
|
26
25
|
const connectorChoices = serviceInfos
|
|
@@ -41,66 +40,78 @@ async function askQuestions(setup, config) {
|
|
|
41
40
|
type: "list",
|
|
42
41
|
choices: connectorChoices,
|
|
43
42
|
});
|
|
44
|
-
let
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
let targetPlatform = types_1.Platform.UNDETERMINED;
|
|
44
|
+
let appDir;
|
|
45
|
+
const cwdPlatformGuess = await (0, fileUtils_1.getPlatformFromFolder)(process.cwd());
|
|
46
|
+
if (cwdPlatformGuess !== types_1.Platform.UNDETERMINED) {
|
|
47
|
+
(0, utils_1.logSuccess)(`Detected ${cwdPlatformGuess} app in current directory ${process.cwd()}`);
|
|
48
|
+
targetPlatform = cwdPlatformGuess;
|
|
49
|
+
appDir = process.cwd();
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
(0, utils_1.logBullet)(`Couldn't automatically detect your app directory.`);
|
|
53
|
+
appDir = await (0, prompt_1.promptForDirectory)({
|
|
54
|
+
config,
|
|
55
|
+
message: "Where is your app directory?",
|
|
54
56
|
});
|
|
55
|
-
|
|
56
|
-
|
|
57
|
+
const platformGuess = await (0, fileUtils_1.getPlatformFromFolder)(appDir);
|
|
58
|
+
if (platformGuess !== types_1.Platform.UNDETERMINED) {
|
|
59
|
+
(0, utils_1.logSuccess)(`Detected ${platformGuess} app in directory ${appDir}`);
|
|
60
|
+
targetPlatform = platformGuess;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
(0, utils_1.logBullet)("Couldn't automatically detect your app's platform.");
|
|
64
|
+
targetPlatform = await (0, prompt_1.promptOnce)({
|
|
65
|
+
message: "Which platform do you want to set up a generated SDK for?",
|
|
66
|
+
type: "list",
|
|
67
|
+
choices: [
|
|
68
|
+
{ name: "iOS (Swift)", value: types_1.Platform.IOS },
|
|
69
|
+
{ name: "Web (JavaScript)", value: types_1.Platform.WEB },
|
|
70
|
+
{ name: "Android (Kotlin)", value: types_1.Platform.ANDROID },
|
|
71
|
+
],
|
|
72
|
+
});
|
|
57
73
|
}
|
|
58
74
|
}
|
|
59
75
|
const newConnectorYaml = JSON.parse(JSON.stringify(connectorInfo.connectorYaml));
|
|
60
76
|
if (!newConnectorYaml.generate) {
|
|
61
77
|
newConnectorYaml.generate = {};
|
|
62
78
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
`./../gensdk/${newConnectorYaml.connectorId}/swift-sdk`,
|
|
69
|
-
});
|
|
70
|
-
const pkg = (0, lodash_1.camelCase)(newConnectorYaml.connectorId);
|
|
79
|
+
let displayIOSWarning = false;
|
|
80
|
+
if (targetPlatform === types_1.Platform.IOS) {
|
|
81
|
+
const outputDir = ((_a = newConnectorYaml.generate.swiftSdk) === null || _a === void 0 ? void 0 : _a.outputDir) ||
|
|
82
|
+
path.relative(connectorInfo.directory, path.join(appDir, `generated/swift`));
|
|
83
|
+
const pkg = (_c = (_b = newConnectorYaml.generate.swiftSdk) === null || _b === void 0 ? void 0 : _b.package) !== null && _c !== void 0 ? _c : (0, lodash_1.upperFirst)((0, lodash_1.camelCase)(newConnectorYaml.connectorId));
|
|
71
84
|
const swiftSdk = { outputDir, package: pkg };
|
|
72
85
|
newConnectorYaml.generate.swiftSdk = swiftSdk;
|
|
86
|
+
displayIOSWarning = true;
|
|
73
87
|
}
|
|
74
|
-
if (
|
|
75
|
-
const outputDir =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
default: ((_b = newConnectorYaml.generate.javascriptSdk) === null || _b === void 0 ? void 0 : _b.outputDir) ||
|
|
79
|
-
`./../gensdk/${newConnectorYaml.connectorId}/javascript-sdk`,
|
|
80
|
-
});
|
|
81
|
-
const pkg = (_d = (_c = newConnectorYaml.generate.javascriptSdk) === null || _c === void 0 ? void 0 : _c.package) !== null && _d !== void 0 ? _d : `@firebasegen/${connectorInfo.connectorYaml.connectorId}`;
|
|
82
|
-
const packageJSONDir = await (0, prompt_1.promptOnce)({
|
|
83
|
-
message: "Which directory contains the package.json that you would like to add the JavaScript SDK dependency to? (Leave blank to skip)",
|
|
84
|
-
type: "input",
|
|
85
|
-
default: (_e = newConnectorYaml.generate.javascriptSdk) === null || _e === void 0 ? void 0 : _e.packageJSONDir,
|
|
86
|
-
});
|
|
88
|
+
if (targetPlatform === types_1.Platform.WEB) {
|
|
89
|
+
const outputDir = ((_d = newConnectorYaml.generate.javascriptSdk) === null || _d === void 0 ? void 0 : _d.outputDir) ||
|
|
90
|
+
path.relative(connectorInfo.directory, path.join(appDir, `generated/javascript/${newConnectorYaml.connectorId}`));
|
|
91
|
+
const pkg = (_f = (_e = newConnectorYaml.generate.javascriptSdk) === null || _e === void 0 ? void 0 : _e.package) !== null && _f !== void 0 ? _f : `@firebasegen/${connectorInfo.connectorYaml.connectorId}`;
|
|
87
92
|
const javascriptSdk = {
|
|
88
93
|
outputDir,
|
|
89
94
|
package: pkg,
|
|
90
95
|
};
|
|
91
|
-
if (
|
|
92
|
-
|
|
96
|
+
if ((await (0, fileUtils_1.directoryHasPackageJson)(appDir)) &&
|
|
97
|
+
(await (0, prompt_1.confirm)({
|
|
98
|
+
message: "Would you like to add a dependency on the generated SDK to your package.json?",
|
|
99
|
+
}))) {
|
|
100
|
+
javascriptSdk.packageJsonDir = path.relative(connectorInfo.directory, appDir);
|
|
93
101
|
}
|
|
94
102
|
newConnectorYaml.generate.javascriptSdk = javascriptSdk;
|
|
95
103
|
}
|
|
96
|
-
if (
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
if (targetPlatform === types_1.Platform.ANDROID) {
|
|
105
|
+
let baseDir = path.join(appDir, `generated/kotlin`);
|
|
106
|
+
for (const candidateSubdir of ["app/src/main/java", "app/src/main/kotlin"]) {
|
|
107
|
+
const candidateDir = path.join(appDir, candidateSubdir);
|
|
108
|
+
if (fs.existsSync(candidateDir)) {
|
|
109
|
+
baseDir = candidateDir;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const outputDir = ((_g = newConnectorYaml.generate.kotlinSdk) === null || _g === void 0 ? void 0 : _g.outputDir) ||
|
|
113
|
+
path.relative(connectorInfo.directory, baseDir);
|
|
114
|
+
const pkg = (_j = (_h = newConnectorYaml.generate.kotlinSdk) === null || _h === void 0 ? void 0 : _h.package) !== null && _j !== void 0 ? _j : `connectors.${(0, lodash_1.snakeCase)(connectorInfo.connectorYaml.connectorId)}`;
|
|
104
115
|
const kotlinSdk = {
|
|
105
116
|
outputDir,
|
|
106
117
|
package: pkg,
|
|
@@ -113,18 +124,23 @@ async function askQuestions(setup, config) {
|
|
|
113
124
|
default: true,
|
|
114
125
|
})));
|
|
115
126
|
const connectorYamlContents = yaml.stringify(newConnectorYaml);
|
|
116
|
-
|
|
127
|
+
connectorInfo.connectorYaml = newConnectorYaml;
|
|
128
|
+
return { connectorYamlContents, connectorInfo, shouldGenerate, displayIOSWarning };
|
|
117
129
|
}
|
|
118
130
|
async function actuate(sdkInfo, projectId) {
|
|
131
|
+
var _a;
|
|
119
132
|
const connectorYamlPath = `${sdkInfo.connectorInfo.directory}/connector.yaml`;
|
|
120
133
|
fs.writeFileSync(connectorYamlPath, sdkInfo.connectorYamlContents, "utf8");
|
|
121
|
-
|
|
134
|
+
(0, utils_1.logBullet)(`Wrote new config to ${connectorYamlPath}`);
|
|
122
135
|
if (projectId && sdkInfo.shouldGenerate) {
|
|
123
136
|
await dataconnectEmulator_1.DataConnectEmulator.generate({
|
|
124
137
|
configDir: sdkInfo.connectorInfo.directory,
|
|
125
138
|
connectorId: sdkInfo.connectorInfo.connectorYaml.connectorId,
|
|
126
139
|
});
|
|
127
|
-
|
|
140
|
+
(0, utils_1.logBullet)(`Generated SDK code for ${sdkInfo.connectorInfo.connectorYaml.connectorId}`);
|
|
141
|
+
}
|
|
142
|
+
if (((_a = sdkInfo.connectorInfo.connectorYaml.generate) === null || _a === void 0 ? void 0 : _a.swiftSdk) && sdkInfo.displayIOSWarning) {
|
|
143
|
+
(0, utils_1.logBullet)(clc.bold("Please follow the instructions here to add your generated sdk to your XCode project:\n\thttps://firebase.google.com/docs/data-connect/gp/ios-sdk#set-client"));
|
|
128
144
|
}
|
|
129
145
|
}
|
|
130
146
|
exports.actuate = actuate;
|
package/lib/prompt.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.confirm = exports.promptOnce = exports.prompt = void 0;
|
|
3
|
+
exports.promptForDirectory = exports.confirm = exports.promptOnce = exports.prompt = void 0;
|
|
4
4
|
const inquirer = require("inquirer");
|
|
5
|
+
const fsutils_1 = require("./fsutils");
|
|
5
6
|
const error_1 = require("./error");
|
|
7
|
+
const logger_1 = require("./logger");
|
|
6
8
|
inquirer.registerPrompt("autocomplete", require("inquirer-autocomplete-prompt"));
|
|
7
9
|
async function prompt(options, questions) {
|
|
8
10
|
const prompts = [];
|
|
@@ -49,3 +51,22 @@ async function confirm(args) {
|
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
exports.confirm = confirm;
|
|
54
|
+
async function promptForDirectory(args) {
|
|
55
|
+
let dir = "";
|
|
56
|
+
while (!dir) {
|
|
57
|
+
const target = args.config.path(await promptOnce({
|
|
58
|
+
message: "Where is your app directory?",
|
|
59
|
+
}));
|
|
60
|
+
if ((0, fsutils_1.fileExistsSync)(target)) {
|
|
61
|
+
logger_1.logger.error(`Expected a directory, but ${target} is a file. Please provide a path to a directory.`);
|
|
62
|
+
}
|
|
63
|
+
else if (!(0, fsutils_1.dirExistsSync)(target)) {
|
|
64
|
+
logger_1.logger.error(`Directory ${target} not found. Please provide a path to a directory`);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
dir = target;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return dir;
|
|
71
|
+
}
|
|
72
|
+
exports.promptForDirectory = promptForDirectory;
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
connectorId:
|
|
2
|
-
authMode: "PUBLIC"
|
|
1
|
+
connectorId: __connectorId__
|
|
2
|
+
authMode: "PUBLIC"
|
|
3
3
|
## ## Here's an example of how to add generated SDKs.
|
|
4
4
|
## ## You'll need to replace the outputDirs with ones pointing to where you want the generated code in your app.
|
|
5
5
|
# generate:
|
|
6
6
|
# javascriptSdk:
|
|
7
7
|
# outputDir: <Path where you want the generated SDK to be written to, relative to this file>
|
|
8
8
|
# package: "@firebasegen/my-connector"
|
|
9
|
-
#
|
|
9
|
+
# packageJsonDir: < Optional. Path to your Javascript app's package.json>
|
|
10
10
|
# swiftSdk:
|
|
11
11
|
# outputDir: <Path where you want the generated SDK to be written to, relative to this file>
|
|
12
|
+
# package: "firebasegen/default"
|
|
12
13
|
# kotlinSdk:
|
|
13
14
|
# outputDir: <Path where you want the generated SDK to be written to, relative to this file>
|
|
15
|
+
# package: connectors.default
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
specVersion: "v1alpha"
|
|
2
|
-
serviceId:
|
|
3
|
-
location:
|
|
2
|
+
serviceId: __serviceId__
|
|
3
|
+
location: __location__
|
|
4
4
|
schema:
|
|
5
5
|
source: "./schema"
|
|
6
6
|
datasource:
|
|
7
7
|
postgresql:
|
|
8
|
-
database:
|
|
8
|
+
database: __cloudSqlDatabase__
|
|
9
9
|
cloudSql:
|
|
10
|
-
instanceId:
|
|
11
|
-
connectorDirs:
|
|
10
|
+
instanceId: __cloudSqlInstanceId__
|
|
11
|
+
connectorDirs: __connectorDirs__
|
|
@@ -1,35 +1,50 @@
|
|
|
1
|
-
# # Example mutations for a simple
|
|
1
|
+
# # Example mutations for a simple movie app
|
|
2
2
|
|
|
3
|
-
# #
|
|
4
|
-
# mutation
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
3
|
+
# # Create a movie based on user input
|
|
4
|
+
# mutation CreateMovie(
|
|
5
|
+
# $title: String!
|
|
6
|
+
# $genre: String!
|
|
7
|
+
# $imageUrl: String!
|
|
8
|
+
# ) @auth(level: USER_EMAIL_VERIFIED) {
|
|
9
|
+
# movie_insert(
|
|
10
|
+
# data: {
|
|
11
|
+
# title: $title
|
|
12
|
+
# genre: $genre
|
|
13
|
+
# imageUrl: $imageUrl
|
|
14
|
+
# }
|
|
15
|
+
# )
|
|
13
16
|
# }
|
|
14
17
|
|
|
15
|
-
# #
|
|
16
|
-
# mutation
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
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.
|
|
26
|
-
# sent_date: { today: true }
|
|
27
|
-
# })
|
|
18
|
+
# # Upsert (update or insert) a user's username based on their auth.uid
|
|
19
|
+
# mutation UpsertUser($username: String!) @auth(level: USER) {
|
|
20
|
+
# user_upsert(
|
|
21
|
+
# data: {
|
|
22
|
+
# id_expr: "auth.uid"
|
|
23
|
+
# username: $username
|
|
24
|
+
# }
|
|
25
|
+
# )
|
|
28
26
|
# }
|
|
29
27
|
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
28
|
+
# # Add a review for a movie
|
|
29
|
+
# mutation AddReview(
|
|
30
|
+
# $movieId: UUID!
|
|
31
|
+
# $rating: Int!
|
|
32
|
+
# $reviewText: String!
|
|
33
|
+
# ) @auth(level: USER) {
|
|
34
|
+
# review_upsert(
|
|
35
|
+
# data: {
|
|
36
|
+
# userId_expr: "auth.uid"
|
|
37
|
+
# movieId: $movieId
|
|
38
|
+
# rating: $rating
|
|
39
|
+
# reviewText: $reviewText
|
|
40
|
+
# # reviewDate defaults to today in the schema. No need to set it manually.
|
|
41
|
+
# }
|
|
42
|
+
# )
|
|
43
|
+
# }
|
|
44
|
+
|
|
45
|
+
# # Logged in user can delete their review for a movie
|
|
46
|
+
# mutation DeleteReview(
|
|
47
|
+
# $movieId: UUID!
|
|
48
|
+
# ) @auth(level: USER) {
|
|
49
|
+
# review_delete(key: { userId_expr: "auth.uid", movieId: $movieId })
|
|
35
50
|
# }
|
|
@@ -1,56 +1,84 @@
|
|
|
1
|
-
# # Example queries for a simple
|
|
1
|
+
# # Example queries for a simple movie app.
|
|
2
2
|
|
|
3
3
|
# # @auth() directives control who can call each operation.
|
|
4
|
-
# #
|
|
5
|
-
# query
|
|
6
|
-
#
|
|
7
|
-
# id
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
4
|
+
# # Anyone should be able to list all movies, so the auth level is set to PUBLIC
|
|
5
|
+
# query ListMovies @auth(level: PUBLIC) {
|
|
6
|
+
# movies {
|
|
7
|
+
# id
|
|
8
|
+
# title
|
|
9
|
+
# imageUrl
|
|
10
|
+
# genre
|
|
11
11
|
# }
|
|
12
12
|
# }
|
|
13
13
|
|
|
14
|
-
# #
|
|
14
|
+
# # List all users, only admins should be able to list all users, so we use NO_ACCESS
|
|
15
15
|
# query ListUsers @auth(level: NO_ACCESS) {
|
|
16
|
-
# users {
|
|
16
|
+
# users { id, username }
|
|
17
17
|
# }
|
|
18
|
-
|
|
19
|
-
# # Logged in
|
|
20
|
-
# query
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# exist: { uid: { eq_expr: "auth.uid" }
|
|
26
|
-
# }}
|
|
27
|
-
# }) {
|
|
28
|
-
# id subject sent
|
|
29
|
-
# content: text # Select the `text` field but alias it as `content` in the response.
|
|
30
|
-
# sender: from { name address uid }
|
|
18
|
+
|
|
19
|
+
# # Logged in user can list all their reviews and movie titles associated with the review
|
|
20
|
+
# # Since the query requires the uid of the current authenticated user, the auth level is set to USER
|
|
21
|
+
# query ListUserReviews @auth(level: USER) {
|
|
22
|
+
# user(key: {id_expr: "auth.uid"}) {
|
|
23
|
+
# id
|
|
24
|
+
# username
|
|
31
25
|
# # <field>_on_<foreign_key_field> makes it easy to grab info from another table
|
|
32
|
-
# # Here, we use it to grab all the
|
|
33
|
-
#
|
|
34
|
-
#
|
|
26
|
+
# # Here, we use it to grab all the reviews written by the user.
|
|
27
|
+
# reviews: reviews_on_user {
|
|
28
|
+
# id
|
|
29
|
+
# rating
|
|
30
|
+
# reviewDate
|
|
31
|
+
# reviewText
|
|
32
|
+
# movie {
|
|
33
|
+
# id
|
|
34
|
+
# title
|
|
35
|
+
# }
|
|
35
36
|
# }
|
|
36
37
|
# }
|
|
37
38
|
# }
|
|
38
39
|
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
40
|
+
# # Get movie by id
|
|
41
|
+
# query GetMovieById($id: UUID!) @auth(level: PUBLIC) {
|
|
42
|
+
# movie(id: $id) {
|
|
43
|
+
# id
|
|
44
|
+
# title
|
|
45
|
+
# imageUrl
|
|
46
|
+
# genre
|
|
47
|
+
# metadata: movieMetadatas_on_movie {
|
|
48
|
+
# director
|
|
49
|
+
# rating
|
|
50
|
+
# releaseYear
|
|
51
|
+
# description
|
|
52
|
+
# }
|
|
53
|
+
# reviews: reviews_on_movie {
|
|
54
|
+
# id
|
|
55
|
+
# reviewText
|
|
56
|
+
# reviewDate
|
|
57
|
+
# rating
|
|
58
|
+
# user {
|
|
59
|
+
# id
|
|
60
|
+
# username
|
|
61
|
+
# }
|
|
62
|
+
# }
|
|
42
63
|
# }
|
|
43
64
|
# }
|
|
44
65
|
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
66
|
+
# # Search for movies, actors, and reviews
|
|
67
|
+
# query SearchMovie(
|
|
68
|
+
# $titleInput: String
|
|
69
|
+
# $genre: String
|
|
70
|
+
# ) @auth(level: PUBLIC) {
|
|
71
|
+
# movies(
|
|
72
|
+
# where: {
|
|
73
|
+
# _and: [
|
|
74
|
+
# { genre: { eq: $genre } }
|
|
75
|
+
# { title: { contains: $titleInput } }
|
|
76
|
+
# ]
|
|
54
77
|
# }
|
|
78
|
+
# ) {
|
|
79
|
+
# id
|
|
80
|
+
# title
|
|
81
|
+
# genre
|
|
82
|
+
# imageUrl
|
|
55
83
|
# }
|
|
56
84
|
# }
|
|
@@ -1,28 +1,45 @@
|
|
|
1
|
-
# # Example schema for simple
|
|
2
|
-
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
1
|
+
# # Example schema for simple movie review app
|
|
2
|
+
|
|
3
|
+
# # Users
|
|
4
|
+
# # Suppose a user can leave reviews for movies
|
|
5
|
+
# # user -> reviews is a one to many relationship,
|
|
6
|
+
# # movie -> reviews is a one to many relationship
|
|
7
|
+
# # movie <-> user is a many to many relationship
|
|
8
|
+
# type User @table {
|
|
9
|
+
# id: String! @col(name: "user_auth")
|
|
10
|
+
# username: String! @col(name: "username", dataType: "varchar(50)")
|
|
11
|
+
# # The following are generated by the user: User! field in the Review table
|
|
12
|
+
# # reviews_on_user
|
|
13
|
+
# # movies_via_Review
|
|
6
14
|
# }
|
|
7
15
|
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
16
|
+
# # Movies
|
|
17
|
+
# type Movie @table {
|
|
18
|
+
# # The below parameter values are generated by default with @table, and can be edited manually.
|
|
19
|
+
# # implies directive `@col(name: "movie_id")`, generating a column name
|
|
20
|
+
# id: UUID! @default(expr: "uuidV4()")
|
|
21
|
+
# title: String!
|
|
22
|
+
# imageUrl: String!
|
|
23
|
+
# genre: String
|
|
13
24
|
# }
|
|
14
25
|
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
26
|
+
# # Movie Metadata
|
|
27
|
+
# # Movie - MovieMetadata is a one-to-one relationship
|
|
28
|
+
# type MovieMetadata @table {
|
|
29
|
+
# # @unique indicates a 1-1 relationship
|
|
30
|
+
# movie: Movie! @unique
|
|
31
|
+
# # movieId: UUID <- this is created by the above reference
|
|
32
|
+
# rating: Float
|
|
33
|
+
# releaseYear: Int
|
|
34
|
+
# description: String
|
|
18
35
|
# }
|
|
19
36
|
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
37
|
+
# # Reviews
|
|
38
|
+
# type Review @table(name: "Reviews", key: ["movie", "user"]) {
|
|
39
|
+
# id: UUID! @default(expr: "uuidV4()")
|
|
40
|
+
# user: User!
|
|
41
|
+
# movie: Movie!
|
|
42
|
+
# rating: Int
|
|
43
|
+
# reviewText: String
|
|
44
|
+
# reviewDate: Date! @default(expr: "request.time")
|
|
28
45
|
# }
|