diff --git a/cli/script/command-executor.ts b/cli/script/command-executor.ts index 7821ace3..38d13d96 100644 --- a/cli/script/command-executor.ts +++ b/cli/script/command-executor.ts @@ -18,7 +18,7 @@ import wordwrap = require("wordwrap"); import * as cli from "../definitions/cli"; import { AcquisitionStatus } from "code-push/script/acquisition-sdk"; -import { AccessKey, AccountManager, App, CollaboratorMap, CollaboratorProperties, Deployment, DeploymentMetrics, Package, Permissions, UpdateMetrics } from "code-push"; +import { AccessKey, Account, AccountManager, App, CollaboratorMap, CollaboratorProperties, Deployment, DeploymentMetrics, Package, Permissions, UpdateMetrics } from "code-push"; var packageJson = require("../package.json"); import Promise = Q.Promise; var progress = require("progress"); @@ -107,21 +107,16 @@ function accessKeyRemove(command: cli.IAccessKeyRemoveCommand): Promise { if (command.accessKey === sdk.accessKey) { throw new Error("Cannot remove the access key for the current session. Please run 'code-push logout' if you would like to remove this access key."); } else { - return getAccessKeyId(command.accessKey) - .then((accessKeyId: string): Promise => { - throwForInvalidAccessKeyId(accessKeyId, command.accessKey); - - return confirm() - .then((wasConfirmed: boolean): Promise => { - if (wasConfirmed) { - return sdk.removeAccessKey(accessKeyId) - .then((): void => { - log("Successfully removed the \"" + command.accessKey + "\" access key."); - }); - } - - log("Access key removal cancelled."); - }); + return confirm() + .then((wasConfirmed: boolean): Promise => { + if (wasConfirmed) { + return sdk.removeAccessKey(command.accessKey) + .then((): void => { + log("Successfully removed the \"" + command.accessKey + "\" access key."); + }); + } + + log("Access key removal cancelled."); }); } } @@ -148,7 +143,7 @@ function appList(command: cli.IAppListCommand): Promise { apps = retrievedApps; var deploymentListPromises: Promise[] = apps.map((app: App) => { - return sdk.getDeployments(app.id) + return sdk.getDeployments(app.name) .then((deployments: Deployment[]) => { var deploymentList: string[] = deployments .map((deployment: Deployment) => deployment.name) @@ -166,33 +161,21 @@ function appList(command: cli.IAppListCommand): Promise { } function appRemove(command: cli.IAppRemoveCommand): Promise { - return getAppId(command.appName) - .then((appId: string): Promise => { - throwForInvalidAppId(appId, command.appName); - - return confirm() - .then((wasConfirmed: boolean): Promise => { - if (wasConfirmed) { - return sdk.removeApp(appId) - .then((): void => { - log("Successfully removed the \"" + command.appName + "\" app."); - }); - } + return confirm() + .then((wasConfirmed: boolean): Promise => { + if (wasConfirmed) { + return sdk.removeApp(command.appName) + .then((): void => { + log("Successfully removed the \"" + command.appName + "\" app."); + }); + } - log("App removal cancelled."); - }); + log("App removal cancelled."); }); } function appRename(command: cli.IAppRenameCommand): Promise { - return getApp(command.currentAppName) - .then((app: App): Promise => { - throwForInvalidApp(app, command.currentAppName); - - app.name = command.newAppName; - - return sdk.updateApp(app); - }) + return sdk.updateApp(command.currentAppName, { name: command.newAppName }) .then((): void => { log("Successfully renamed the \"" + command.currentAppName + "\" app to \"" + command.newAppName + "\"."); }); @@ -201,69 +184,50 @@ function appRename(command: cli.IAppRenameCommand): Promise { function appTransfer(command: cli.IAppTransferCommand): Promise { throwForInvalidEmail(command.email); - return getAppId(command.appName) - .then((appId: string): Promise => { - throwForInvalidAppId(appId, command.appName); - - return confirm() - .then((wasConfirmed: boolean): Promise => { - if (wasConfirmed) { - return sdk.transferApp(appId, command.email) - .then((): void => { - log("Successfully transferred the ownership of app \"" + command.appName + "\" to the account with email \"" + command.email + "\"."); - }); - } + return confirm() + .then((wasConfirmed: boolean): Promise => { + if (wasConfirmed) { + return sdk.transferApp(command.appName, command.email) + .then((): void => { + log("Successfully transferred the ownership of app \"" + command.appName + "\" to the account with email \"" + command.email + "\"."); + }); + } - log("App transfer cancelled."); - }); + log("App transfer cancelled."); }); } function addCollaborator(command: cli.ICollaboratorAddCommand): Promise { throwForInvalidEmail(command.email); - return getAppId(command.appName) - .then((appId: string): Promise => { - throwForInvalidAppId(appId, command.appName); - - return sdk.addCollaborator(appId, command.email) - .then((): void => { - log("Successfully added \"" + command.email + "\" as a collaborator to the app \"" + command.appName + "\"."); - }); + return sdk.addCollaborator(command.appName, command.email) + .then((): void => { + log("Successfully added \"" + command.email + "\" as a collaborator to the app \"" + command.appName + "\"."); }); } function listCollaborators(command: cli.ICollaboratorListCommand): Promise { throwForInvalidOutputFormat(command.format); - return getAppId(command.appName) - .then((appId: string): Promise => { - throwForInvalidAppId(appId, command.appName); - return sdk.getCollaboratorsList(appId) - .then((retrievedCollaborators: CollaboratorMap): void => { - printCollaboratorsList(command.format, retrievedCollaborators); - }); + return sdk.getCollaboratorsList(command.appName) + .then((retrievedCollaborators: CollaboratorMap): void => { + printCollaboratorsList(command.format, retrievedCollaborators); }); } function removeCollaborator(command: cli.ICollaboratorRemoveCommand): Promise { throwForInvalidEmail(command.email); - return getAppId(command.appName) - .then((appId: string): Promise => { - throwForInvalidAppId(appId, command.appName); - - return confirm() - .then((wasConfirmed: boolean): Promise => { - if (wasConfirmed) { - return sdk.removeCollaborator(appId, command.email) - .then((): void => { - log("Successfully removed \"" + command.email + "\" as a collaborator from the app \"" + command.appName + "\"."); - }); - } + return confirm() + .then((wasConfirmed: boolean): Promise => { + if (wasConfirmed) { + return sdk.removeCollaborator(command.appName, command.email) + .then((): void => { + log("Successfully removed \"" + command.email + "\" as a collaborator from the app \"" + command.appName + "\"."); + }); + } - log("App collaborator removal cancelled."); - }); + log("App collaborator removal cancelled."); }); } @@ -277,35 +241,23 @@ function deleteConnectionInfoCache(): void { } function deploymentAdd(command: cli.IDeploymentAddCommand): Promise { - return getAppId(command.appName) - .then((appId: string): Promise => { - throwForInvalidAppId(appId, command.appName); - - return sdk.addDeployment(appId, command.deploymentName) - .then((deployment: Deployment): void => { - log("Successfully added the \"" + command.deploymentName + "\" deployment with key \"" + deployment.key + "\" to the \"" + command.appName + "\" app."); - }); - }) + return sdk.addDeployment(command.appName, command.deploymentName) + .then((deployment: Deployment): void => { + log("Successfully added the \"" + command.deploymentName + "\" deployment with key \"" + deployment.key + "\" to the \"" + command.appName + "\" app."); + }); } export var deploymentList = (command: cli.IDeploymentListCommand, showPackage: boolean = true): Promise => { throwForInvalidOutputFormat(command.format); - var theAppId: string; var deployments: Deployment[]; - return getAppId(command.appName) - .then((appId: string): Promise => { - throwForInvalidAppId(appId, command.appName); - theAppId = appId; - - return sdk.getDeployments(appId); - }) + return sdk.getDeployments(command.appName) .then((retrievedDeployments: Deployment[]) => { deployments = retrievedDeployments; if (showPackage) { var metricsPromises: Promise[] = deployments.map((deployment: Deployment) => { if (deployment.package) { - return sdk.getDeploymentMetrics(theAppId, deployment.id) + return sdk.getDeploymentMetrics(command.appName, deployment.name) .then((metrics: DeploymentMetrics): void => { if (metrics[deployment.package.label]) { var totalActive: number = getTotalActiveFromDeploymentMetrics(metrics); @@ -332,87 +284,48 @@ export var deploymentList = (command: cli.IDeploymentListCommand, showPackage: b } function deploymentRemove(command: cli.IDeploymentRemoveCommand): Promise { - return getAppId(command.appName) - .then((appId: string): Promise => { - throwForInvalidAppId(appId, command.appName); - - return getDeploymentId(appId, command.deploymentName) - .then((deploymentId: string): Promise => { - throwForInvalidDeploymentId(deploymentId, command.deploymentName, command.appName); - - return confirm() - .then((wasConfirmed: boolean): Promise => { - if (wasConfirmed) { - return sdk.removeDeployment(appId, deploymentId) - .then((): void => { - log("Successfully removed the \"" + command.deploymentName + "\" deployment from the \"" + command.appName + "\" app."); - }) - } - - log("Deployment removal cancelled."); - }); - }); + return confirm() + .then((wasConfirmed: boolean): Promise => { + if (wasConfirmed) { + return sdk.removeDeployment(command.appName, command.deploymentName) + .then((): void => { + log("Successfully removed the \"" + command.deploymentName + "\" deployment from the \"" + command.appName + "\" app."); + }) + } + + log("Deployment removal cancelled."); }); } function deploymentRename(command: cli.IDeploymentRenameCommand): Promise { - return getAppId(command.appName) - .then((appId: string): Promise => { - throwForInvalidAppId(appId, command.appName); - - return getDeployment(appId, command.currentDeploymentName) - .then((deployment: Deployment): Promise => { - throwForInvalidDeployment(deployment, command.currentDeploymentName, command.appName); - - deployment.name = command.newDeploymentName; - - return sdk.updateDeployment(appId, deployment); - }) - .then((): void => { - log("Successfully renamed the \"" + command.currentDeploymentName + "\" deployment to \"" + command.newDeploymentName + "\" for the \"" + command.appName + "\" app."); - }); + return sdk.updateDeployment(command.appName, command.currentDeploymentName, { name: command.newDeploymentName }) + .then((): void => { + log("Successfully renamed the \"" + command.currentDeploymentName + "\" deployment to \"" + command.newDeploymentName + "\" for the \"" + command.appName + "\" app."); }); } function deploymentHistory(command: cli.IDeploymentHistoryCommand): Promise { throwForInvalidOutputFormat(command.format); - var storedAppId: string; - var storedDeploymentId: string; - var deployments: Deployment[]; - var currentUserEmail: string; - - return getApp(command.appName) - .then((app: App): Promise => { - throwForInvalidAppId(app.id, command.appName); - storedAppId = app.id; - currentUserEmail = getCurrentUserEmail(app.collaborators); - - return getDeploymentId(app.id, command.deploymentName); - }) - .then((deploymentId: string): Promise => { - throwForInvalidDeploymentId(deploymentId, command.deploymentName, command.appName); - storedDeploymentId = deploymentId; - - return sdk.getPackageHistory(storedAppId, deploymentId); - }) - .then((packageHistory: Package[]): Promise => { - return sdk.getDeploymentMetrics(storedAppId, storedDeploymentId) - .then((metrics: DeploymentMetrics): void => { - var totalActive: number = getTotalActiveFromDeploymentMetrics(metrics); - packageHistory.forEach((packageObject: Package) => { - if (metrics[packageObject.label]) { - (packageObject).metrics = { - active: metrics[packageObject.label].active, - downloaded: metrics[packageObject.label].downloaded, - failed: metrics[packageObject.label].failed, - installed: metrics[packageObject.label].installed, - totalActive: totalActive - }; - } - }); - printDeploymentHistory(command, packageHistory, currentUserEmail); - }); + return Q.all([ + sdk.getAccountInfo(), + sdk.getPackageHistory(command.appName, command.deploymentName), + sdk.getDeploymentMetrics(command.appName, command.deploymentName) + ]) + .spread((account: Account, packageHistory: Package[], metrics: DeploymentMetrics): void => { + var totalActive: number = getTotalActiveFromDeploymentMetrics(metrics); + packageHistory.forEach((packageObject: Package) => { + if (metrics[packageObject.label]) { + (packageObject).metrics = { + active: metrics[packageObject.label].active, + downloaded: metrics[packageObject.label].downloaded, + failed: metrics[packageObject.label].failed, + installed: metrics[packageObject.label].installed, + totalActive: totalActive + }; + } + }); + printDeploymentHistory(command, packageHistory, account.email); }); } @@ -535,75 +448,6 @@ function generateRandomFilename(length: number): string { return filename; } -function getAccessKey(accessKeyName: string): Promise { - return sdk.getAccessKeys() - .then((accessKeys: AccessKey[]): AccessKey => { - for (var i = 0; i < accessKeys.length; ++i) { - var accessKey: AccessKey = accessKeys[i]; - - if (accessKey.name === accessKeyName) { - return accessKey; - } - } - }); -} - -function getAccessKeyId(accessKeyName: string): Promise { - return getAccessKey(accessKeyName) - .then((accessKey: AccessKey): string => { - if (accessKey) { - return accessKey.id; - } - - return null; - }); -} - -function getApp(appName: string): Promise { - var ownerEmailValue: string; - var appNameValue: string = appName; - var delimiterIndex: number = appName.indexOf("/"); - - if (delimiterIndex !== -1) { - ownerEmailValue = appName.substring(0, delimiterIndex); - appNameValue = appName.substring(delimiterIndex + 1); - - throwForInvalidEmail(ownerEmailValue); - } - - return sdk.getApps() - .then((apps: App[]): App => { - var foundApp = false; - var possibleApp: App; - - for (var i = 0; i < apps.length; ++i) { - var app: App = apps[i]; - if (app.name === appNameValue) { - var isCurrentUserOwner: boolean = isCurrentAccountOwner(app.collaborators); - if (ownerEmailValue) { - var appOwner: string = getOwnerEmail(app.collaborators); - foundApp = appOwner && appOwner === ownerEmailValue; - } else if (!isCurrentUserOwner) { - // found an app name matching given value but is not the owner of the app - // its possible there is another app with same name of which this user - // is the owner, so keep this pointer for future use. - possibleApp = app; - } else { - foundApp = isCurrentUserOwner; - } - } - - if (foundApp) { - return app; - } - } - - if (possibleApp) { - return possibleApp; - } - }); -} - function isCurrentAccountOwner(map: CollaboratorMap): boolean { if (map) { var ownerEmail: string = getOwnerEmail(map); @@ -613,18 +457,6 @@ function isCurrentAccountOwner(map: CollaboratorMap): boolean { return false; } -function getCurrentUserEmail(map: CollaboratorMap): string { - if (map) { - for (var key of Object.keys(map)) { - if (map[key].isCurrentAccount) { - return key; - } - } - } - - return null; -} - function getOwnerEmail(map: CollaboratorMap): string { if (map) { for (var key of Object.keys(map)) { @@ -637,41 +469,6 @@ function getOwnerEmail(map: CollaboratorMap): string { return null; } -function getAppId(appName: string): Promise { - return getApp(appName) - .then((app: App): string => { - if (app) { - return app.id; - } - - return null; - }); -} - -function getDeployment(appId: string, deploymentName: string): Promise { - return sdk.getDeployments(appId) - .then((deployments: Deployment[]): Deployment => { - for (var i = 0; i < deployments.length; ++i) { - var deployment: Deployment = deployments[i]; - - if (deployment.name === deploymentName) { - return deployment; - } - } - }); -} - -function getDeploymentId(appId: string, deploymentName: string): Promise { - return getDeployment(appId, deploymentName) - .then((deployment: Deployment): string => { - if (deployment) { - return deployment.id; - } - - return null; - }); -} - function getTotalActiveFromDeploymentMetrics(metrics: DeploymentMetrics): number { var totalActive = 0; Object.keys(metrics).forEach((label: string) => { @@ -722,6 +519,9 @@ function loginWithAccessTokenInternal(serverUrl: string): Promise { var decoded: string = base64.decode(accessToken); var connectionInfo: ILegacyLoginConnectionInfo = JSON.parse(decoded); } catch (error) { + } + + if (!connectionInfo) { throw new Error("Invalid access token."); } @@ -842,7 +642,6 @@ function printCollaboratorsList(format: string, collaborators: CollaboratorMap): function printDeploymentList(command: cli.IDeploymentListCommand, deployments: Deployment[], showPackage: boolean = true): void { if (command.format === "json") { - deployments.forEach((deployment: Deployment) => delete deployment.id); // Temporary until ID's are removed from the REST API printJson(deployments); } else if (command.format === "table") { var headers = ["Name"]; @@ -1002,26 +801,7 @@ function register(command: cli.IRegisterCommand): Promise { } function promote(command: cli.IPromoteCommand): Promise { - var appId: string; - var sourceDeploymentId: string; - var destDeploymentId: string; - - return getAppId(command.appName) - .then((appIdResult: string): Promise => { - throwForInvalidAppId(appIdResult, command.appName); - appId = appIdResult; - return getDeploymentId(appId, command.sourceDeploymentName); - }) - .then((deploymentId: string): Promise => { - throwForInvalidDeploymentId(deploymentId, command.sourceDeploymentName, command.appName); - sourceDeploymentId = deploymentId; - return getDeploymentId(appId, command.destDeploymentName); - }) - .then((deploymentId: string): Promise => { - throwForInvalidDeploymentId(deploymentId, command.destDeploymentName, command.appName); - destDeploymentId = deploymentId; - return sdk.promotePackage(appId, sourceDeploymentId, destDeploymentId); - }) + return sdk.promotePackage(command.appName, command.sourceDeploymentName, command.destDeploymentName) .then((): void => { log("Successfully promoted the \"" + command.sourceDeploymentName + "\" deployment of the \"" + command.appName + "\" app to the \"" + command.destDeploymentName + "\" deployment."); }); @@ -1034,92 +814,80 @@ function release(command: cli.IReleaseCommand): Promise { throw new Error("Please use a semver compliant app store version, for example \"1.0.3\"."); } - return getAppId(command.appName) - .then((appId: string): Promise => { - throwForInvalidAppId(appId, command.appName); + var filePath: string = command.package; + var getPackageFilePromise: Promise; + var isSingleFilePackage: boolean = true; - return getDeploymentId(appId, command.deploymentName) - .then((deploymentId: string): Promise => { - throwForInvalidDeploymentId(deploymentId, command.deploymentName, command.appName); + if (fs.lstatSync(filePath).isDirectory()) { + isSingleFilePackage = false; + getPackageFilePromise = Promise((resolve: (file: IPackageFile) => void, reject: (reason: Error) => void): void => { + var directoryPath: string = filePath; - var filePath: string = command.package; - var getPackageFilePromise: Promise; - var isSingleFilePackage: boolean = true; + recursiveFs.readdirr(directoryPath, (error?: any, directories?: string[], files?: string[]): void => { + if (error) { + reject(error); + return; + } - if (fs.lstatSync(filePath).isDirectory()) { - isSingleFilePackage = false; - getPackageFilePromise = Promise((resolve: (file: IPackageFile) => void, reject: (reason: Error) => void): void => { - var directoryPath: string = filePath; + var baseDirectoryPath = path.dirname(directoryPath); + var fileName: string = generateRandomFilename(15) + ".zip"; + var zipFile = new yazl.ZipFile(); + var writeStream: fs.WriteStream = fs.createWriteStream(fileName); - recursiveFs.readdirr(directoryPath, (error?: any, directories?: string[], files?: string[]): void => { - if (error) { - reject(error); - return; - } + zipFile.outputStream.pipe(writeStream) + .on("error", (error: Error): void => { + reject(error); + }) + .on("close", (): void => { + filePath = path.join(process.cwd(), fileName); - var baseDirectoryPath = path.dirname(directoryPath); - var fileName: string = generateRandomFilename(15) + ".zip"; - var zipFile = new yazl.ZipFile(); - var writeStream: fs.WriteStream = fs.createWriteStream(fileName); + resolve({ isTemporary: true, path: filePath }); + }); - zipFile.outputStream.pipe(writeStream) - .on("error", (error: Error): void => { - reject(error); - }) - .on("close", (): void => { - filePath = path.join(process.cwd(), fileName); + for (var i = 0; i < files.length; ++i) { + var file: string = files[i]; + var relativePath: string = path.relative(baseDirectoryPath, file); - resolve({ isTemporary: true, path: filePath }); - }); + // yazl does not like backslash (\) in the metadata path. + relativePath = slash(relativePath); - for (var i = 0; i < files.length; ++i) { - var file: string = files[i]; - var relativePath: string = path.relative(baseDirectoryPath, file); + zipFile.addFile(file, relativePath); + } - // yazl does not like backslash (\) in the metadata path. - relativePath = slash(relativePath); + zipFile.end(); + }); + }); + } else { + getPackageFilePromise = Q({ isTemporary: false, path: filePath }); + } - zipFile.addFile(file, relativePath); - } + var lastTotalProgress = 0; + var progressBar = new progress("Upload progress:[:bar] :percent :etas", { + complete: "=", + incomplete: " ", + width: 50, + total: 100 + }); - zipFile.end(); - }); - }); - } else { - getPackageFilePromise = Q({ isTemporary: false, path: filePath }); - } + var uploadProgress = (currentProgress: number): void => { + progressBar.tick(currentProgress - lastTotalProgress); + lastTotalProgress = currentProgress; + } - var lastTotalProgress = 0; - var progressBar = new progress("Upload progress:[:bar] :percent :etas", { - complete: "=", - incomplete: " ", - width: 50, - total: 100 - }); + return getPackageFilePromise + .then((file: IPackageFile): Promise => { + return sdk.addPackage(command.appName, command.deploymentName, file.path, command.description, command.appStoreVersion, command.mandatory, uploadProgress) + .then((): void => { + log("Successfully released an update containing the \"" + command.package + "\" " + (isSingleFilePackage ? "file" : "directory") + " to the \"" + command.deploymentName + "\" deployment of the \"" + command.appName + "\" app."); - var uploadProgress = (currentProgress: number): void => { - progressBar.tick(currentProgress - lastTotalProgress); - lastTotalProgress = currentProgress; + if (file.isTemporary) { + fs.unlinkSync(filePath); } - - return getPackageFilePromise - .then((file: IPackageFile): Promise => { - return sdk.addPackage(appId, deploymentId, file.path, command.description, /*label*/ null, command.appStoreVersion, command.mandatory, uploadProgress) - .then((): void => { - log("Successfully released an update containing the \"" + command.package + "\" " + (isSingleFilePackage ? "file" : "directory") + " to the \"" + command.deploymentName + "\" deployment of the \"" + command.appName + "\" app."); - - if (file.isTemporary) { - fs.unlinkSync(filePath); - } - }); - }); }); }); } function rollback(command: cli.IRollbackCommand): Promise { - var appId: string; - return confirm() .then((wasConfirmed: boolean) => { if (!wasConfirmed) { @@ -1127,16 +895,7 @@ function rollback(command: cli.IRollbackCommand): Promise { return; } - return getAppId(command.appName) - .then((appIdResult: string): Promise => { - throwForInvalidAppId(appIdResult, command.appName); - appId = appIdResult; - return getDeploymentId(appId, command.deploymentName); - }) - .then((deploymentId: string): Promise => { - throwForInvalidDeploymentId(deploymentId, command.deploymentName, command.appName); - return sdk.rollbackPackage(appId, deploymentId, command.targetRelease || undefined); - }) + return sdk.rollbackPackage(command.appName, command.deploymentName, command.targetRelease || undefined) .then((): void => { log("Successfully performed a rollback on the \"" + command.deploymentName + "\" deployment of the \"" + command.appName + "\" app."); }); @@ -1180,48 +939,12 @@ function isBinaryOrZip(path: string): boolean { || path.search(/\.ipa$/i) !== -1; } -function throwForMissingCredentials(accessKeyName: string, providerName: string, providerUniqueId: string): void { - if (!accessKeyName) throw new Error("Access key is missing."); - if (!providerName) throw new Error("Provider name is missing."); - if (!providerUniqueId) throw new Error("Provider unique ID is missing."); -} - -function throwForInvalidAccessKeyId(accessKeyId: string, accessKeyName: string): void { - if (!accessKeyId) { - throw new Error("Access key \"" + accessKeyName + "\" does not exist."); - } -} - -function throwForInvalidApp(app: App, appName: string): void { - if (!app) { - throw new Error("App \"" + appName + "\" does not exist."); - } -} - -function throwForInvalidAppId(appId: string, appName: string): void { - if (!appId) { - throw new Error("App \"" + appName + "\" does not exist."); - } -} - function throwForInvalidEmail(email: string): void { if (!emailValidator.validate(email)) { throw new Error("\"" + email + "\" is an invalid e-mail address."); } } -function throwForInvalidDeployment(deployment: Deployment, deploymentName: string, appName: string): void { - if (!deployment) { - throw new Error("Deployment \"" + deploymentName + "\" does not exist for app \"" + appName + "\"."); - } -} - -function throwForInvalidDeploymentId(deploymentId: string, deploymentName: string, appName: string): void { - if (!deploymentId) { - throw new Error("Deployment \"" + deploymentName + "\" does not exist for app \"" + appName + "\"."); - } -} - function throwForInvalidOutputFormat(format: string): void { switch (format) { case "json": diff --git a/cli/test/cli.ts b/cli/test/cli.ts index 24378bea..e5feaaa1 100644 --- a/cli/test/cli.ts +++ b/cli/test/cli.ts @@ -13,9 +13,14 @@ function assertJsonDescribesObject(json: string, object: Object): void { } export class SdkStub { + public getAccountInfo(): Promise { + return Q({ + email: "a@a.com" + }); + } + public addAccessKey(machine: string, description?: string): Promise { return Q({ - id: "accessKeyId", name: "key123", createdTime: new Date().getTime(), createdBy: os.hostname(), @@ -25,7 +30,6 @@ export class SdkStub { public addApp(name: string): Promise { return Q({ - id: "appId", name: name }); } @@ -36,7 +40,6 @@ export class SdkStub { public addDeployment(appId: string, name: string): Promise { return Q({ - id: "deploymentId", name: name, key: "6" }); @@ -44,7 +47,6 @@ export class SdkStub { public getAccessKeys(): Promise { return Q([{ - id: "7", name: "8", createdTime: 0, createdBy: os.hostname(), @@ -54,11 +56,9 @@ export class SdkStub { public getApps(): Promise { return Q([{ - id: "1", name: "a", collaborators: { "a@a.com": { permission: "Owner", isCurrentAccount: true } } }, { - id: "2", name: "b", collaborators: { "a@a.com": { permission: "Owner", isCurrentAccount: true } } }]); @@ -66,11 +66,9 @@ export class SdkStub { public getDeployments(appId: string): Promise { return Q([{ - id: "3", name: "Production", key: "6" }, { - id: "4", name: "Staging", key: "6", package: { @@ -231,7 +229,6 @@ describe("CLI", () => { var actual: string = log.args[0][0]; var expected = [ { - id: "7", name: "8", createdTime: 0, createdBy: os.hostname(), @@ -255,7 +252,7 @@ describe("CLI", () => { cmdexec.execute(command) .done((): void => { sinon.assert.calledOnce(removeAccessKey); - sinon.assert.calledWithExactly(removeAccessKey, "7"); + sinon.assert.calledWithExactly(removeAccessKey, "8"); sinon.assert.calledOnce(log); sinon.assert.calledWithExactly(log, "Successfully removed the \"8\" access key."); @@ -335,7 +332,7 @@ describe("CLI", () => { cmdexec.execute(command) .done((): void => { sinon.assert.calledOnce(removeApp); - sinon.assert.calledWithExactly(removeApp, "1"); + sinon.assert.calledWithExactly(removeApp, "a"); sinon.assert.calledOnce(log); sinon.assert.calledWithExactly(log, "Successfully removed the \"a\" app."); @@ -435,7 +432,7 @@ describe("CLI", () => { var actual: string = log.args[0][0]; var expected = { "collaborators": - { + { "a@a.com": { permission: "Owner", isCurrentAccount: true }, "b@b.com": { permission: "Collaborator", isCurrentAccount: false } } @@ -543,7 +540,7 @@ describe("CLI", () => { cmdexec.execute(command) .done((): void => { sinon.assert.calledOnce(removeDeployment); - sinon.assert.calledWithExactly(removeDeployment, "1", "4"); + sinon.assert.calledWithExactly(removeDeployment, "a", "Staging"); sinon.assert.calledOnce(log); sinon.assert.calledWithExactly(log, "Successfully removed the \"Staging\" deployment from the \"a\" app."); diff --git a/definitions/rest-definitions.d.ts b/definitions/rest-definitions.d.ts index 1f78eb75..4f03fc2a 100644 --- a/definitions/rest-definitions.d.ts +++ b/definitions/rest-definitions.d.ts @@ -1,16 +1,25 @@ declare module "rest-definitions" { + /** + * Annotations for properties on 'inout' interfaces: + * - generated: This property cannot be specified on any input requests (PUT/PATCH/POST). + * As a result, generated properties are always marked as optional. + * - key: This property is the identifier for an object, with certain uniqueness constraints. + */ + + /*inout*/ export interface AccessKey { - /*generated*/ id?: string; - name: string; createdBy: string; createdTime: number; description?: string; + /*key*/ name: string; } + /*out*/ export interface DeploymentMetrics { [packageLabelOrAppVersion: string]: UpdateMetrics } + /*in*/ export interface DeploymentStatusReport { appVersion: string; clientUniqueId?: string; @@ -21,35 +30,40 @@ declare module "rest-definitions" { status?: string; } + /*in*/ export interface DownloadReport { clientUniqueId: string; deploymentKey: string; label: string; } - export interface PackageInfo { - appVersion: string; - description: string; - isMandatory: boolean; + /*inout*/ + interface PackageInfo { + appVersion?: string; + description?: string; + isMandatory?: boolean; /*generated*/ label?: string; - /*generated*/ packageHash: string; + /*generated*/ packageHash?: string; } + /*out*/ export interface UpdateCheckResponse extends PackageInfo { - /*generated*/ downloadURL: string; - /*generated*/ isAvailable: boolean; - /*generated*/ packageSize: number; - /*generated*/ updateAppVersion?: boolean; + downloadURL?: string; + isAvailable: boolean; + packageSize?: number; + updateAppVersion?: boolean; } + /*in*/ export interface UpdateCheckRequest { appVersion: string; deploymentKey: string; - isCompanion: boolean; - label: string; - packageHash: string; + isCompanion?: boolean; + label?: string; + packageHash?: string; } + /*out*/ export interface UpdateMetrics { active: number; downloaded?: number; @@ -57,35 +71,37 @@ declare module "rest-definitions" { installed?: number; } + /*inout*/ export interface Account { - email: string; - /*generated*/ id?: string; + /*key*/ email: string; name: string; - /*const*/ username: string; } + /*out*/ export interface CollaboratorProperties { - /*generated*/ isCurrentAccount?: boolean; + isCurrentAccount?: boolean; permission: string; } + /*out*/ export interface CollaboratorMap { [email: string]: CollaboratorProperties; } + /*inout*/ export interface App { /*generated*/ collaborators?: CollaboratorMap; - /*generated*/ id?: string; - name: string; + /*key*/ name: string; } + /*inout*/ export interface Deployment { - /*generated*/ id?: string; - name: string; /*generated*/ key?: string; - package?: Package + /*key*/ name: string; + /*generated*/ package?: Package } + /*inout*/ export interface Package extends PackageInfo { /*generated*/ blobUrl: string; /*generated*/ diffAgainstPackageHash?: string; diff --git a/sdk/script/account-manager.ts b/sdk/script/account-manager.ts index d303bb94..96e411d8 100644 --- a/sdk/script/account-manager.ts +++ b/sdk/script/account-manager.ts @@ -35,7 +35,6 @@ export interface CodePushError { } interface PackageToUpload { - label: string; description: string; appVersion: string; isMandatory: boolean; @@ -85,22 +84,14 @@ export class AccountManager { public addAccessKey(machine: string, description?: string): Promise { return this.generateAccessKey() .then((newAccessKey: string) => { - var accessKey: AccessKey = { id: null, name: newAccessKey, createdTime: new Date().getTime(), createdBy: machine, description: description }; + var accessKey: AccessKey = { name: newAccessKey, createdTime: new Date().getTime(), createdBy: machine, description: description }; return this.post(`/accessKeys/`, JSON.stringify(accessKey), /*expectResponseBody=*/ false) - .then((res: JsonResponse) => { - var location: string = res.header[`location`]; - if (location && location.lastIndexOf(`/`) !== -1) { - accessKey.id = location.substr(location.lastIndexOf(`/`) + 1); - return accessKey; - } else { - return null; - } - }); + .then(() => accessKey); }); } - public getAccessKey(accessKeyId: string): Promise { - return this.get(`/accessKeys/${accessKeyId}`) + public getAccessKey(accessKey: string): Promise { + return this.get(`/accessKeys/${accessKey}`) .then((res: JsonResponse) => res.body.accessKey); } @@ -109,8 +100,8 @@ export class AccountManager { .then((res: JsonResponse) => res.body.accessKeys); } - public removeAccessKey(accessKeyId: string): Promise { - return this.del(`/accessKeys/${accessKeyId}`) + public removeAccessKey(accessKey: string): Promise { + return this.del(`/accessKeys/${accessKey}`) .then(() => null); } @@ -131,95 +122,84 @@ export class AccountManager { .then((res: JsonResponse) => res.body.apps); } - public getApp(appId: string): Promise { - return this.get(`/apps/${appId}`) + public getApp(appName: string): Promise { + return this.get(`/apps/${appName}`) .then((res: JsonResponse) => res.body.app); } public addApp(appName: string): Promise { var app: App = { name: appName }; return this.post(`/apps/`, JSON.stringify(app), /*expectResponseBody=*/ false) - .then((res: JsonResponse) => { - var location = res.header[`location`]; - if (location && location.lastIndexOf(`/`) !== -1) { - app.id = location.substr(location.lastIndexOf(`/`) + 1); - return app; - } else { - return null; - } - }); + .then(() => app); } - public removeApp(app: App | string): Promise { - var id: string = (typeof app === `string`) ? (app) : (app).id; - return this.del(`/apps/${id}`) + public removeApp(appName: string): Promise { + return this.del(`/apps/${appName}`) .then(() => null); } - public updateApp(infoToChange: App): Promise { - return this.put(`/apps/${infoToChange.id}`, JSON.stringify(infoToChange)) - .then((res: JsonResponse) => null); + public updateApp(appName: string, infoToChange: App): Promise { + return this.put(`/apps/${appName}`, JSON.stringify(infoToChange)) + .then(() => null); } - public transferApp(appId: string, email: string): Promise { - return this.post(`/apps/${appId}/transfer/${email}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) + public transferApp(appName: string, email: string): Promise { + return this.post(`/apps/${appName}/transfer/${email}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) .then(() => null); } // Collaborators - public getCollaboratorsList(appId: string): Promise { - return this.get(`/apps/${appId}/collaborators`) + public getCollaboratorsList(appName: string): Promise { + return this.get(`/apps/${appName}/collaborators`) .then((res: JsonResponse) => res.body.collaborators); } - public addCollaborator(appId: string, email: string): Promise { - return this.post(`/apps/${appId}/collaborators/${email}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) + public addCollaborator(appName: string, email: string): Promise { + return this.post(`/apps/${appName}/collaborators/${email}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) .then(() => null); } - public removeCollaborator(app: App | string, email: string): Promise { - var id: string = (typeof app === "string") ? app : app.id; - return this.del(`/apps/${id}/collaborators/${email}`) + public removeCollaborator(appName: string, email: string): Promise { + return this.del(`/apps/${appName}/collaborators/${email}`) .then(() => null); } // Deployments - public addDeployment(appId: string, name: string): Promise { - var deployment = { name: name }; - return this.post(`/apps/${appId}/deployments/`, JSON.stringify(deployment), /*expectResponseBody=*/ true) + public addDeployment(appName: string, deploymentName: string): Promise { + var deployment = { name: deploymentName }; + return this.post(`/apps/${appName}/deployments/`, JSON.stringify(deployment), /*expectResponseBody=*/ true) .then((res: JsonResponse) => res.body.deployment); } - public getDeployments(appId: string): Promise { - return this.get(`/apps/${appId}/deployments/`) + public getDeployments(appName: string): Promise { + return this.get(`/apps/${appName}/deployments/`) .then((res: JsonResponse) => res.body.deployments); } - public getDeployment(appId: string, deploymentId: string): Promise { - return this.get(`/apps/${appId}/deployments/${deploymentId}`) + public getDeployment(appName: string, deploymentName: string): Promise { + return this.get(`/apps/${appName}/deployments/${deploymentName}`) .then((res: JsonResponse) => res.body.deployment); } - public getDeploymentMetrics(appId: string, deploymentId: string): Promise { - return this.get(`/apps/${appId}/deployments/${deploymentId}/metrics`) + public getDeploymentMetrics(appName: string, deploymentName: string): Promise { + return this.get(`/apps/${appName}/deployments/${deploymentName}/metrics`) .then((res: JsonResponse) => res.body.metrics); } - public updateDeployment(appId: string, infoToChange: Deployment): Promise { - return this.put(`/apps/${appId}/deployments/${infoToChange.id}`, JSON.stringify(infoToChange)) + public updateDeployment(appName: string, deploymentName: string, infoToChange: Deployment): Promise { + return this.put(`/apps/${appName}/deployments/${deploymentName}`, JSON.stringify(infoToChange)) .then(() => null); } - public removeDeployment(appId: string, deployment: Deployment | string): Promise { - var id: string = (typeof deployment === `string`) ? (deployment) : (deployment).id; - return this.del(`/apps/${appId}/deployments/${id}`) + public removeDeployment(appName: string, deploymentName: string): Promise { + return this.del(`/apps/${appName}/deployments/${deploymentName}`) .then(() => null); } - public addPackage(appId: string, deploymentId: string, fileOrPath: File | string, description: string, label: string, appVersion: string, isMandatory: boolean = false, uploadProgressCallback?: (progress: number) => void): Promise { + public addPackage(appName: string, deploymentName: string, fileOrPath: File | string, description: string, appVersion: string, isMandatory: boolean = false, uploadProgressCallback?: (progress: number) => void): Promise { return Promise((resolve, reject, notify) => { - var packageInfo: PackageToUpload = this.generatePackageInfo(description, label, appVersion, isMandatory); - var request: superagent.Request = superagent.put(`${this._serverUrl}/apps/${appId}/deployments/${deploymentId}/package`); + var packageInfo: PackageToUpload = this.generatePackageInfo(description, appVersion, isMandatory); + var request: superagent.Request = superagent.put(`${this._serverUrl}/apps/${appName}/deployments/${deploymentName}/package`); this.attachCredentials(request); var file: any; @@ -261,23 +241,23 @@ export class AccountManager { }); } - public promotePackage(appId: string, sourceDeploymentId: string, destDeploymentId: string): Promise { - return this.post(`/apps/${appId}/deployments/${sourceDeploymentId}/promote/${destDeploymentId}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) + public promotePackage(appName: string, sourceDeploymentName: string, destDeploymentName: string): Promise { + return this.post(`/apps/${appName}/deployments/${sourceDeploymentName}/promote/${destDeploymentName}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) .then(() => null); } - public rollbackPackage(appId: string, deploymentId: string, targetRelease?: string): Promise { - return this.post(`/apps/${appId}/deployments/${deploymentId}/rollback/${targetRelease || ``}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) + public rollbackPackage(appName: string, deploymentName: string, targetRelease?: string): Promise { + return this.post(`/apps/${appName}/deployments/${deploymentName}/rollback/${targetRelease || ``}`, /*requestBody=*/ null, /*expectResponseBody=*/ false) .then(() => null); } - public getPackage(appId: string, deploymentId: string): Promise { - return this.get(`/apps/${appId}/deployments/${deploymentId}/package`) + public getPackage(appName: string, deploymentName: string): Promise { + return this.get(`/apps/${appName}/deployments/${deploymentName}/package`) .then((res: JsonResponse) => res.body.package); } - public getPackageHistory(appId: string, deploymentId: string): Promise { - return this.get(`/apps/${appId}/deployments/${deploymentId}/packageHistory`) + public getPackageHistory(appName: string, deploymentName: string): Promise { + return this.get(`/apps/${appName}/deployments/${deploymentName}/packageHistory`) .then((res: JsonResponse) => res.body.packageHistory); } @@ -345,10 +325,9 @@ export class AccountManager { return response && response.text ? response.text : error.message; } - private generatePackageInfo(description: string, label: string, appVersion: string, isMandatory: boolean): PackageToUpload { + private generatePackageInfo(description: string, appVersion: string, isMandatory: boolean): PackageToUpload { return { description: description, - label: label, appVersion: appVersion, isMandatory: isMandatory }; @@ -367,7 +346,7 @@ export class AccountManager { .toString(`base64`) .replace(/\+/g, `_`) // URL-friendly characters .replace(/\//g, `-`) - .concat(account.id); + .concat(account.email); return accessKey; }); diff --git a/sdk/test/management-sdk.ts b/sdk/test/management-sdk.ts index f2c6405d..a73eb8da 100644 --- a/sdk/test/management-sdk.ts +++ b/sdk/test/management-sdk.ts @@ -24,15 +24,15 @@ describe("Management SDK", () => { var methodsWithErrorHandling: any[] = [ manager.addApp.bind(manager, "appName"), - manager.getApp.bind(manager, "appId"), - manager.updateApp.bind(manager, {}), - manager.removeApp.bind(manager, "appId"), + manager.getApp.bind(manager, "appName"), + manager.updateApp.bind(manager, "appName", {}), + manager.removeApp.bind(manager, "appName"), - manager.addDeployment.bind(manager, "appId", "name"), - manager.getDeployment.bind(manager, "appId", "deploymentId"), - manager.getDeployments.bind(manager, "appId"), - manager.updateDeployment.bind(manager, "appId", { id: "deploymentToChange" }), - manager.removeDeployment.bind(manager, "appId", { id: "deploymentToChange" }), + manager.addDeployment.bind(manager, "appName", "deploymentName"), + manager.getDeployment.bind(manager, "appName", "deploymentName"), + manager.getDeployments.bind(manager, "appName"), + manager.updateDeployment.bind(manager, "appName", "deploymentName", { name: "newDeploymentName" }), + manager.removeDeployment.bind(manager, "appName", "deploymentName"), manager.getPackage.bind(manager, ""), ]; @@ -89,26 +89,25 @@ describe("Management SDK", () => { }); }); - it("addApp handles location header", (done: MochaDone) => { - mockReturn(JSON.stringify({ success: true }), 200, { location: "/appId" }); + it("addApp handles successful response", (done: MochaDone) => { + mockReturn(JSON.stringify({ success: true }), 201, { location: "/appName" }); manager.addApp("appName").done((obj) => { assert.ok(obj); done(); }, rejectHandler); }); - it("addApp handles missing location header", (done: MochaDone) => { - mockReturn(JSON.stringify({ success: true }), 200, {}); + it("addApp handles error response", (done: MochaDone) => { + mockReturn(JSON.stringify({ success: false }), 404, {}); manager.addApp("appName").done((obj) => { - assert.ok(!obj); - done(); - }, rejectHandler); + throw new Error("Call should not complete successfully"); + }, (error: Error) => done()); }); it("getApp handles JSON response", (done: MochaDone) => { mockReturn(JSON.stringify({ app: {} }), 200, {}); - manager.getApp("appId").done((obj: any) => { + manager.getApp("appName").done((obj: any) => { assert.ok(obj); done(); }, rejectHandler); @@ -117,7 +116,7 @@ describe("Management SDK", () => { it("updateApp handles success response", (done: MochaDone) => { mockReturn(JSON.stringify({ apps: [] }), 200, {}); - manager.updateApp({}).done((obj: any) => { + manager.updateApp("appName", {}).done((obj: any) => { assert.ok(!obj); done(); }, rejectHandler); @@ -126,16 +125,16 @@ describe("Management SDK", () => { it("removeApp handles success response", (done: MochaDone) => { mockReturn("", 200, {}); - manager.removeApp("appId").done((obj: any) => { + manager.removeApp("appName").done((obj: any) => { assert.ok(!obj); done(); }, rejectHandler); }); it("addDeployment handles success response", (done: MochaDone) => { - mockReturn(JSON.stringify({ deployment: { name: "name", key: "key" } }), 201, { location: "/deploymentId" }); + mockReturn(JSON.stringify({ deployment: { name: "name", key: "key" } }), 201, { location: "/deploymentName" }); - manager.addDeployment("appId", "name").done((obj: any) => { + manager.addDeployment("appName", "deploymentName").done((obj: any) => { assert.ok(obj); done(); }, rejectHandler); @@ -144,7 +143,7 @@ describe("Management SDK", () => { it("getDeployment handles JSON response", (done: MochaDone) => { mockReturn(JSON.stringify({ deployment: {} }), 200, {}); - manager.getDeployment("appId", "deploymentId").done((obj: any) => { + manager.getDeployment("appName", "deploymentName").done((obj: any) => { assert.ok(obj); done(); }, rejectHandler); @@ -153,7 +152,7 @@ describe("Management SDK", () => { it("getDeployments handles JSON response", (done: MochaDone) => { mockReturn(JSON.stringify({ deployments: [] }), 200, {}); - manager.getDeployments("appId").done((obj: any) => { + manager.getDeployments("appName").done((obj: any) => { assert.ok(obj); done(); }, rejectHandler); @@ -162,7 +161,7 @@ describe("Management SDK", () => { it("updateDeployment handles success response", (done: MochaDone) => { mockReturn(JSON.stringify({ apps: [] }), 200, {}); - manager.updateDeployment("appId", {id: "deploymentId"}).done((obj: any) => { + manager.updateDeployment("appName", "deploymentName", { name: "newDeploymentName" }).done((obj: any) => { assert.ok(!obj); done(); }, rejectHandler); @@ -171,7 +170,7 @@ describe("Management SDK", () => { it("removeDeployment handles success response", (done: MochaDone) => { mockReturn("", 200, {}); - manager.removeDeployment("appId", "deploymentId").done((obj: any) => { + manager.removeDeployment("appName", "deploymentName").done((obj: any) => { assert.ok(!obj); done(); }, rejectHandler); @@ -180,7 +179,7 @@ describe("Management SDK", () => { it("getPackage handles success response", (done: MochaDone) => { mockReturn(JSON.stringify({ package: {} }), 200); - manager.getPackage("appId", "deploymentId").done((obj: any) => { + manager.getPackage("appName", "deploymentName").done((obj: any) => { assert.ok(obj); done(); }, rejectHandler); @@ -189,7 +188,7 @@ describe("Management SDK", () => { it("getPackageHistory handles success response with no packages", (done: MochaDone) => { mockReturn(JSON.stringify({ packageHistory: [] }), 200); - manager.getPackageHistory("appId", "deploymentId").done((obj: any) => { + manager.getPackageHistory("appName", "deploymentName").done((obj: any) => { assert.ok(obj); assert.equal(obj.length, 0); done(); @@ -199,7 +198,7 @@ describe("Management SDK", () => { it("getPackageHistory handles success response with two packages", (done: MochaDone) => { mockReturn(JSON.stringify({ packageHistory: [ { label: "v1" }, { label: "v2" } ] }), 200); - manager.getPackageHistory("appId", "deploymentId").done((obj: any) => { + manager.getPackageHistory("appName", "deploymentName").done((obj: any) => { assert.ok(obj); assert.equal(obj.length, 2); assert.equal(obj[0].label, "v1"); @@ -211,7 +210,7 @@ describe("Management SDK", () => { it("getPackageHistory handles error response", (done: MochaDone) => { mockReturn("", 404); - manager.getPackageHistory("appId", "deploymentId").done((obj: any) => { + manager.getPackageHistory("appName", "deploymentName").done((obj: any) => { throw new Error("Call should not complete successfully"); }, (error: Error) => done()); });