-
Notifications
You must be signed in to change notification settings - Fork 971
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
First pass at auto generating sdk configs #7833
Changes from 16 commits
c72612c
f740de4
0432624
2eeff9c
5e6bb73
c04efda
3b6022e
4ab3e9f
204f126
2bcb58c
0e67680
022a189
23bf76e
4f9bdfd
d4200f5
e6fa76d
fc20178
1bee4da
45506fe
bf0a1b3
c3963be
40d8397
db0ac18
1327522
e95bdad
b6af1e6
cb3a09e
f676ff8
76e7691
67fe143
e9c7280
4f744df
5c4af31
f8d1848
f3cd670
45b3f97
a37000c
453ec56
09f2ba3
f5248f1
5ab216f
9cb95e6
a236f0f
92bc6d6
804e0a7
7f05bb3
3409f41
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,24 @@ | ||
import * as ora from "ora"; | ||
import * as fs from "fs-extra"; | ||
|
||
import { Command } from "../command"; | ||
import { | ||
AppConfigurationData, | ||
AppMetadata, | ||
AppConfig, | ||
AppPlatform, | ||
getAppConfig, | ||
getAppConfigFile, | ||
getAppPlatform, | ||
listFirebaseApps, | ||
getPlatform, | ||
getSdkConfig, | ||
getSdkOutputPath, | ||
sdkInit, | ||
writeConfigToFile, | ||
} from "../management/apps"; | ||
import { needProjectId } from "../projectUtils"; | ||
import { getOrPromptProject } from "../management/projects"; | ||
import { FirebaseError } from "../error"; | ||
import { requireAuth } from "../requireAuth"; | ||
import { logger } from "../logger"; | ||
import { promptOnce } from "../prompt"; | ||
import { Options } from "../options"; | ||
import * as path from "path"; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: prefer grouping this with the other external imports at the top of the file. |
||
|
||
function checkForApps(apps: AppMetadata[], appPlatform: AppPlatform): void { | ||
if (!apps.length) { | ||
throw new FirebaseError( | ||
`There are no ${appPlatform === AppPlatform.ANY ? "" : appPlatform + " "}apps ` + | ||
"associated with this Firebase project", | ||
); | ||
} | ||
} | ||
|
||
async function selectAppInteractively( | ||
apps: AppMetadata[], | ||
appPlatform: AppPlatform, | ||
): Promise<AppMetadata> { | ||
checkForApps(apps, appPlatform); | ||
|
||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const choices = apps.map((app: any) => { | ||
return { | ||
name: | ||
`${app.displayName || app.bundleId || app.packageName}` + | ||
` - ${app.appId} (${app.platform})`, | ||
value: app, | ||
}; | ||
}); | ||
|
||
return await promptOnce({ | ||
type: "list", | ||
message: | ||
`Select the ${appPlatform === AppPlatform.ANY ? "" : appPlatform + " "}` + | ||
"app to get the configuration data:", | ||
choices, | ||
}); | ||
interface AppsSdkConfigOptions extends Options { | ||
out?: string | boolean; | ||
} | ||
|
||
export const command = new Command("apps:sdkconfig [platform] [appId]") | ||
|
@@ -59,79 +27,66 @@ | |
"[platform] can be IOS, ANDROID or WEB (case insensitive)", | ||
) | ||
.option("-o, --out [file]", "(optional) write config output to a file") | ||
// Note: Command behaves weirdly with optional string flags - when `--out`, options.out is a boolean | ||
// but when `--out myFile.json`, options.out is a string | ||
.before(requireAuth) | ||
.action(async (platform = "", appId = "", options: Options): Promise<AppConfigurationData> => { | ||
let appPlatform = getAppPlatform(platform); | ||
|
||
if (!appId) { | ||
let projectId = needProjectId(options); | ||
if (options.nonInteractive && !projectId) { | ||
throw new FirebaseError("Must supply app and project ids in non-interactive mode."); | ||
} else if (!projectId) { | ||
const result = await getOrPromptProject(options); | ||
projectId = result.projectId; | ||
.action( | ||
async ( | ||
platform: AppPlatform = AppPlatform.PLATFORM_UNSPECIFIED, | ||
appId = "", | ||
options: AppsSdkConfigOptions, | ||
): Promise<AppConfig> => { | ||
const config = options.config; | ||
const appDir = process.cwd(); | ||
if (!platform) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this ever hit? I don't think AppPlatform.PLATFORM_UNSPECIFIED is falsey (since its a string enum). Feels like this should maybe be |
||
// Auto-detect platform based on current directory if not specified | ||
platform = await getPlatform(appDir, config); | ||
} | ||
|
||
const apps = await listFirebaseApps(projectId, appPlatform); | ||
// Fail out early if there's no apps. | ||
checkForApps(apps, appPlatform); | ||
// if there's only one app, we don't need to prompt interactively | ||
if (apps.length === 1) { | ||
// If there's only one, use it. | ||
appId = apps[0].appId; | ||
appPlatform = apps[0].platform; | ||
} else if (options.nonInteractive) { | ||
// If there's > 1 and we're non-interactive, fail. | ||
throw new FirebaseError(`Project ${projectId} has multiple apps, must specify an app id.`); | ||
} else { | ||
// > 1, ask what the user wants. | ||
const appMetadata: AppMetadata = await selectAppInteractively(apps, appPlatform); | ||
appId = appMetadata.appId; | ||
appPlatform = appMetadata.platform; | ||
let sdkConfig: AppConfig | undefined; | ||
while (sdkConfig === undefined) { | ||
try { | ||
sdkConfig = await getSdkConfig(options, getAppPlatform(platform), appId); | ||
} catch (e) { | ||
if ((e as Error).message.includes("associated with this Firebase project")) { | ||
await sdkInit(platform as unknown as AppPlatform, options); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this type washing is now unnecessary (since you strongly typed platform in all locations afaict) |
||
} else { | ||
throw e; | ||
} | ||
} | ||
} | ||
} | ||
|
||
let configData; | ||
const spinner = ora( | ||
`Downloading configuration data of your Firebase ${appPlatform} app`, | ||
).start(); | ||
try { | ||
configData = await getAppConfig(appId, appPlatform); | ||
} catch (err: any) { | ||
spinner.fail(); | ||
throw err; | ||
} | ||
spinner.succeed(); | ||
|
||
const fileInfo = getAppConfigFile(configData, appPlatform); | ||
if (appPlatform === AppPlatform.WEB) { | ||
fileInfo.sdkConfig = configData; | ||
} | ||
|
||
if (options.out === undefined) { | ||
logger.info(fileInfo.fileContents); | ||
return fileInfo; | ||
} | ||
|
||
const shouldUseDefaultFilename = options.out === true || options.out === ""; | ||
const filename = shouldUseDefaultFilename ? configData.fileName : options.out; | ||
if (fs.existsSync(filename)) { | ||
if (options.nonInteractive) { | ||
throw new FirebaseError(`${filename} already exists`); | ||
let writeToFile = false; | ||
let outputPath: string = ""; | ||
if (typeof options.out === "boolean") { | ||
writeToFile = options.out; | ||
outputPath = getSdkOutputPath(appDir, platform); | ||
} else if (typeof options.out === "string") { | ||
writeToFile = true; | ||
outputPath = options.out; | ||
} | ||
const overwrite = await promptOnce({ | ||
type: "confirm", | ||
default: false, | ||
message: `${filename} already exists. Do you want to overwrite?`, | ||
}); | ||
|
||
if (!overwrite) { | ||
return configData; | ||
if (writeToFile) { | ||
const outputDir = path.dirname(outputPath!); | ||
fs.mkdirpSync(outputDir); | ||
const fileInfo = getAppConfigFile(sdkConfig, platform); | ||
await writeConfigToFile(outputPath!, options.nonInteractive, fileInfo.fileContents); | ||
if (platform === AppPlatform.WEB) { | ||
logger.info(` | ||
How to use your JS SDK Config: | ||
ES Module: | ||
import { initializeApp } from 'firebase/app'; | ||
import json from './firebase-js-config.json'; | ||
initializeApp(json); | ||
// CommonJS Module: | ||
const { initializeApp } = require('firebase/app'); | ||
const json = require('./firebase-js-config.json'); | ||
initializeApp(json);// instead of initializeApp(config); | ||
`); | ||
} | ||
logger.info(`App configuration is written in ${fileInfo}`); | ||
} | ||
} | ||
|
||
fs.writeFileSync(filename, fileInfo.fileContents); | ||
logger.info(`App configuration is written in ${filename}`); | ||
|
||
return configData; | ||
}); | ||
return sdkConfig; | ||
}, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While you're here, could you add a type for options on this command as well?