88import * as path from 'path' ;
99import * as chalk from 'chalk' ;
1010import { UX } from '@salesforce/command' ;
11- import { Logger , Messages } from '@salesforce/core' ;
12- import { getBoolean , getString , getNumber } from '@salesforce/ts-types' ;
11+ import { Logger , Messages , SfdxError } from '@salesforce/core' ;
12+ import { get , getBoolean , getString , getNumber } from '@salesforce/ts-types' ;
1313import { DeployResult } from '@salesforce/source-deploy-retrieve' ;
1414import {
1515 FileResponse ,
@@ -61,13 +61,13 @@ export class DeployResultFormatter extends ResultFormatter {
6161 * @returns a JSON formatted result matching the provided type.
6262 */
6363 public getJson ( ) : DeployCommandResult | DeployCommandAsyncResult {
64- const json = this . result . response as DeployCommandResult | DeployCommandAsyncResult ;
64+ const json = this . getResponse ( ) as DeployCommandResult | DeployCommandAsyncResult ;
6565 json . deployedSource = this . fileResponses ;
6666 json . outboundFiles = [ ] ; // to match toolbelt version
67- json . deploys = [ Object . assign ( { } , this . result . response ) ] ; // to match toolbelt version
67+ json . deploys = [ Object . assign ( { } , this . getResponse ( ) ) ] ; // to match toolbelt version
6868
6969 if ( this . isAsync ( ) ) {
70- // json = this.result.response ; // <-- TODO: ensure the response matches toolbelt
70+ // json = this.getResponse() ; // <-- TODO: ensure the response matches toolbelt
7171 return json as DeployCommandAsyncResult ;
7272 }
7373
@@ -93,20 +93,24 @@ export class DeployResultFormatter extends ResultFormatter {
9393 }
9494 if ( this . hasStatus ( RequestStatus . Canceled ) ) {
9595 const canceledByName = getString ( this . result , 'response.canceledByName' , 'unknown' ) ;
96- this . ux . log ( `The deployment has been canceled by ${ canceledByName } ` ) ;
97- return ;
96+ throw new SfdxError ( messages . getMessage ( 'deployCanceled' , [ canceledByName ] ) , 'DeployFailed' ) ;
9897 }
9998 this . displaySuccesses ( ) ;
10099 this . displayFailures ( ) ;
101100 this . displayTestResults ( ) ;
101+
102+ // Throw a DeployFailed error unless the deployment was successful.
103+ if ( ! this . isSuccess ( ) ) {
104+ throw new SfdxError ( messages . getMessage ( 'deployFailed' ) , 'DeployFailed' ) ;
105+ }
102106 }
103107
104108 protected hasStatus ( status : RequestStatus ) : boolean {
105109 return getString ( this . result , 'response.status' ) === status ;
106110 }
107111
108112 protected hasComponents ( ) : boolean {
109- return getNumber ( this . result , 'components.size' , 0 ) === 0 ;
113+ return getNumber ( this . result , 'components.size' , 0 ) > 0 ;
110114 }
111115
112116 protected isRunTestsEnabled ( ) : boolean {
@@ -121,6 +125,10 @@ export class DeployResultFormatter extends ResultFormatter {
121125 return getNumber ( this . result , `response.${ field } ` , 0 ) ;
122126 }
123127
128+ protected getResponse ( ) : MetadataApiDeployStatus {
129+ return get ( this . result , 'response' , { } ) as MetadataApiDeployStatus ;
130+ }
131+
124132 protected displaySuccesses ( ) : void {
125133 if ( this . isSuccess ( ) && this . hasComponents ( ) ) {
126134 // sort by type then filename then fullname
@@ -156,21 +164,24 @@ export class DeployResultFormatter extends ResultFormatter {
156164 protected displayFailures ( ) : void {
157165 if ( this . hasStatus ( RequestStatus . Failed ) && this . hasComponents ( ) ) {
158166 // sort by filename then fullname
159- const failures = this . fileResponses . sort ( ( i , j ) => {
160- if ( i . filePath === j . filePath ) {
161- // if they have the same directoryName then sort by fullName
162- return i . fullName < j . fullName ? 1 : - 1 ;
163- }
164- return i . filePath < j . filePath ? 1 : - 1 ;
165- } ) ;
167+ const failures = this . fileResponses
168+ . filter ( ( fileResponse ) => fileResponse . state === 'Failed' )
169+ . sort ( ( i , j ) => {
170+ if ( i . filePath === j . filePath ) {
171+ // if they have the same directoryName then sort by fullName
172+ return i . fullName < j . fullName ? 1 : - 1 ;
173+ }
174+ return i . filePath < j . filePath ? 1 : - 1 ;
175+ } ) ;
166176 this . ux . log ( '' ) ;
167177 this . ux . styledHeader ( chalk . red ( `Component Failures [${ failures . length } ]` ) ) ;
178+ // TODO: do we really need the project path or file path in the table?
179+ // Seems like we can just provide the full name and devs will know.
168180 this . ux . table ( failures , {
169181 columns : [
170- { key : 'componentType' , label : 'Type' } ,
171- { key : 'fileName' , label : 'File' } ,
182+ { key : 'problemType' , label : 'Type' } ,
172183 { key : 'fullName' , label : 'Name' } ,
173- { key : 'problem ' , label : 'Problem' } ,
184+ { key : 'error ' , label : 'Problem' } ,
174185 ] ,
175186 } ) ;
176187 this . ux . log ( '' ) ;
0 commit comments