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.
Files changed (36) hide show
  1. package/lib/commands/deploy.js +3 -1
  2. package/lib/dataconnect/fileUtils.js +52 -11
  3. package/lib/dataconnect/provisionCloudSql.js +5 -1
  4. package/lib/deploy/extensions/planner.js +46 -1
  5. package/lib/deploy/extensions/prepare.js +99 -23
  6. package/lib/deploy/functions/build.js +5 -5
  7. package/lib/deploy/functions/deploy.js +12 -12
  8. package/lib/deploy/functions/params.js +5 -3
  9. package/lib/deploy/functions/prepare.js +16 -1
  10. package/lib/deploy/functions/release/index.js +4 -0
  11. package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
  12. package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +46 -0
  13. package/lib/emulator/downloadableEmulators.js +9 -9
  14. package/lib/emulator/functionsEmulator.js +8 -1
  15. package/lib/extensions/askUserForEventsConfig.js +18 -8
  16. package/lib/extensions/askUserForParam.js +2 -1
  17. package/lib/extensions/displayExtensionInfo.js +5 -5
  18. package/lib/extensions/extensionsApi.js +1 -1
  19. package/lib/extensions/extensionsHelper.js +42 -3
  20. package/lib/extensions/localHelper.js +1 -1
  21. package/lib/extensions/refs.js +26 -11
  22. package/lib/extensions/runtimes/common.js +75 -0
  23. package/lib/extensions/types.js +56 -1
  24. package/lib/frameworks/constants.js +1 -1
  25. package/lib/frameworks/next/constants.js +1 -1
  26. package/lib/frameworks/next/index.js +35 -15
  27. package/lib/frameworks/next/utils.js +53 -1
  28. package/lib/init/features/dataconnect/index.js +10 -7
  29. package/lib/init/features/dataconnect/sdk.js +67 -51
  30. package/lib/prompt.js +22 -1
  31. package/package.json +1 -1
  32. package/templates/init/dataconnect/connector.yaml +5 -3
  33. package/templates/init/dataconnect/dataconnect.yaml +5 -5
  34. package/templates/init/dataconnect/mutations.gql +44 -29
  35. package/templates/init/dataconnect/queries.gql +66 -38
  36. 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), { connectorIds: info.connectors.map((c) => `"./${c.id}"`).join(", ") }));
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.id, "connector.yaml"), subbedConnectorYaml);
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.id, f.path), f.content);
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
- connectorIds: "__connectorIds__",
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: c.name.split("/").pop(),
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 logger_1 = require("../../../logger");
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 IOS = "ios";
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 platforms = [];
45
- while (!platforms.length) {
46
- platforms = await (0, prompt_1.promptOnce)({
47
- message: "Which platforms do you want to set up a generated SDK for?",
48
- type: "checkbox",
49
- choices: [
50
- { name: "iOS (Swift)", value: IOS },
51
- { name: "Web (JavaScript)", value: WEB },
52
- { name: "Androd (Kotlin)", value: ANDROID },
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
- if (!platforms.length) {
56
- logger_1.logger.info("You must pick at least one platform.");
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
- if (platforms.includes(IOS)) {
64
- const outputDir = await (0, prompt_1.promptOnce)({
65
- message: `What directory do you want to write your Swift SDK code to? (If not absolute, path will be relative to '${connectorInfo.directory}')`,
66
- type: "input",
67
- default: ((_a = newConnectorYaml.generate.swiftSdk) === null || _a === void 0 ? void 0 : _a.outputDir) ||
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 (platforms.includes(WEB)) {
75
- const outputDir = await (0, prompt_1.promptOnce)({
76
- message: `What directory do you want to write your JavaScript SDK code to? (If not absolute, path will be relative to '${connectorInfo.directory}')`,
77
- type: "input",
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 (packageJSONDir) {
92
- javascriptSdk.packageJSONDir = packageJSONDir;
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 (platforms.includes(ANDROID)) {
97
- const outputDir = await (0, prompt_1.promptOnce)({
98
- message: `What directory do you want to write your Kotlin SDK code to? (If not absolute, path will be relative to '${connectorInfo.directory}')`,
99
- type: "input",
100
- default: ((_f = newConnectorYaml.generate.kotlinSdk) === null || _f === void 0 ? void 0 : _f.outputDir) ||
101
- `./../gensdk/${newConnectorYaml.connectorId}/kotlin-sdk`,
102
- });
103
- const pkg = (_h = (_g = newConnectorYaml.generate.kotlinSdk) === null || _g === void 0 ? void 0 : _g.package) !== null && _h !== void 0 ? _h : `connectors.${(0, lodash_1.snakeCase)(connectorInfo.connectorYaml.connectorId)}`;
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
- return { connectorYamlContents, connectorInfo, shouldGenerate };
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
- logger_1.logger.info(`Wrote new config to ${connectorYamlPath}`);
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
- logger_1.logger.info(`Generated SDK code for ${sdkInfo.connectorInfo.connectorYaml.connectorId}`);
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,6 +1,6 @@
1
1
  {
2
2
  "name": "firebase-tools",
3
- "version": "13.15.0",
3
+ "version": "13.15.2",
4
4
  "description": "Command-Line Interface for Firebase",
5
5
  "main": "./lib/index.js",
6
6
  "bin": {
@@ -1,13 +1,15 @@
1
- connectorId: "__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
- # packageJSONDir: < Optional. Path to your Javascript app's package.json>
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: "__serviceId__"
3
- location: "__location__"
2
+ serviceId: __serviceId__
3
+ location: __location__
4
4
  schema:
5
5
  source: "./schema"
6
6
  datasource:
7
7
  postgresql:
8
- database: "__cloudSqlDatabase__"
8
+ database: __cloudSqlDatabase__
9
9
  cloudSql:
10
- instanceId: "__cloudSqlInstanceId__"
11
- connectorDirs: [__connectorIds__]
10
+ instanceId: __cloudSqlInstanceId__
11
+ connectorDirs: __connectorDirs__
@@ -1,35 +1,50 @@
1
- # # Example mutations for a simple email app
1
+ # # Example mutations for a simple movie app
2
2
 
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.
6
- # user_insert(data: {
7
- # # Server values let your service populate sensitive data.
8
- # # Users can only setup their own account.
9
- # uid_expr: "auth.uid",
10
- # name: $name,
11
- # address: $address
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
- # # Logged in user can send emails from their account.
16
- # mutation CreateEmail($content: String, $subject: String) @auth(level: USER) {
17
- # email_insert(data: {
18
- # # The request variable name doesn't have to match the field name.
19
- # text: $content,
20
- # subject: $subject,
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.
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
- # mutation CreateRecipient($emailId: UUID) @auth(level: USER) {
31
- # recipient_insert(data: {
32
- # emailId: $emailId,
33
- # userUid_expr: "auth.uid"
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 email app.
1
+ # # Example queries for a simple movie app.
2
2
 
3
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
- # }
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
- # # Only admins should be able to list all users, so we use NO_ACCESS
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 { uid, name, address }
16
+ # users { id, username }
17
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.
23
- # emails(where: {
24
- # users_via_Recipient: {
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 recipients of the email.
33
- # to: recipients_on_email {
34
- # user { name address uid }
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
- # query GetUidByEmail($emails: [String!]) @auth(level: PUBLIC) {
40
- # users(where: { address: { in: $emails } }) {
41
- # uid address
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
- # query ListSent($uid: String) @auth(level: PUBLIC) {
46
- # emails(where: {
47
- # from: {uid: {eq: $uid }}
48
- # }) {
49
- # id subject sent
50
- # content: text
51
- # sender: from { name address uid }
52
- # to: recipients_on_email {
53
- # user { name address uid }
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 email app
2
- # type User @table(key: "uid") {
3
- # uid: String!
4
- # name: String!
5
- # address: String!
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
- # type Email @table {
9
- # subject: String!
10
- # sent: Date!
11
- # text: String!
12
- # from: User!
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
- # type Recipient @table(key: ["email", "user"]) {
16
- # email: Email!
17
- # user: User!
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
- # type EmailMeta @table(key: ["user", "email"]) {
21
- # user: User!
22
- # email: Email!
23
- # labels: [String]
24
- # read: Boolean!
25
- # starred: Boolean!
26
- # muted: Boolean!
27
- # snoozed: Date
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
  # }