Skip to content
This repository has been archived by the owner on Jul 21, 2020. It is now read-only.

Commit

Permalink
feat(uploader): handle compilation errors, make log downloadable (fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
connor4312 committed Jun 12, 2018
1 parent 6a2f937 commit ac8dc9e
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<h1 mat-dialog-title>Compilation Error</h1>
<div mat-dialog-content>
<p>We ran into a problem compiling your controls. You can <a class="body-link" (click)="saveConsole()">save the compilation output</a>, which should contain more details about what went wrong.</p>
</div>
<div mat-dialog-actions>
<span class="flex-fill"></span>
<button mat-button (click)="dialogRef.close()" cdkFocusInitial>Close</button>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { Store } from '@ngrx/store';

import { MatDialogRef } from '@angular/material';
import * as fromRoot from '../../bedrock.reducers';
import * as forUploader from '../uploader.actions';

/**
* Presented after uploading has compelted.
*/
@Component({
selector: 'uploader-compilation-error',
templateUrl: './uploader-compilation-error.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
styles: [':host { max-width: 500px; display: block }'],
})
export class UploaderCompilationErrorComponent {
constructor(
private readonly store: Store<fromRoot.IState>,
public readonly dialogRef: MatDialogRef<any>,
) {}

/**
* Saves the data the webpack console emitted.
*/
public saveConsole() {
this.store.dispatch(new forUploader.SaveConsoleOutput());
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<h1 mat-dialog-title>Upload Complete</h1>
<div mat-dialog-content>
<p>Upload complete, your changes will appear on your channel page when you reconnect your game client.
<p>Upload complete, your changes will appear on your channel page when you reconnect your game client. If you're interested, you can <a class="body-link" (click)="saveConsole()">save the compilation output</a>.</p>

<p>You'll be able to see them on your changes on your channel page: <a openExternal="https://mixer.com/{{ username | async }}" class="body-link">https://mixer.com/{{ username | async }}</a>.
<p>You'll be able to see them on your changes on your channel page: <a openExternal="https://mixer.com/{{ username | async }}" class="body-link">https://mixer.com/{{ username | async }}</a>.</p>
</div>
<div mat-dialog-actions>
<span class="flex-fill"></span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { map } from 'rxjs/operators';
import { MatDialogRef } from '@angular/material';
import * as fromAccount from '../../account/account.reducer';
import * as fromRoot from '../../bedrock.reducers';
import * as forUploader from '../uploader.actions';

/**
* Presented after uploading has compelted.
Expand All @@ -13,6 +14,7 @@ import * as fromRoot from '../../bedrock.reducers';
selector: 'uploader-completed',
templateUrl: './uploader-completed.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
styles: [':host { max-width: 500px; display: block }'],
})
export class UploaderCompletedComponent {
/**
Expand All @@ -26,4 +28,11 @@ export class UploaderCompletedComponent {
private readonly store: Store<fromRoot.IState>,
public readonly dialogRef: MatDialogRef<any>,
) {}

/**
* Saves the data the webpack console emitted.
*/
public saveConsole() {
this.store.dispatch(new forUploader.SaveConsoleOutput());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { selectUploadControls, selectUploadSchema } from '../uploader.reducer';
@Component({
selector: 'uploader-confirming',
templateUrl: './uploader-confirming.component.html',
styleUrls: ['./uploader-confirming.component.scss'],
styles: [':host { max-width: 500px; display: block }'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class UploaderConfirmingComponent {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
<uploader-uploading-controls *ngSwitchCase="Screens.UploadingControls"></uploader-uploading-controls>
<uploader-linking-game *ngSwitchCase="Screens.LinkingGame"></uploader-linking-game>
<uploader-rename *ngSwitchCase="Screens.Rename"></uploader-rename>
<uploader-compilation-error *ngSwitchCase="Screens.CompilationError"></uploader-compilation-error>
<uploader-completed *ngSwitchCase="Screens.Completed"></uploader-completed>
</ng-container>
9 changes: 9 additions & 0 deletions src/app/editor/uploader/uploader.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const enum UploaderActionTypes {
SET_UPLOAD_SCHEMA = '[Uploader] Set whether to upload schema',
SET_UPLOAD_CONTROLS = '[Uploader] Set whether to upload controls',
SET_ERROR = '[Uploader] Set Error',
SAVE_CONSOLE = '[Uploader] Save console output',
}

export const enum UploaderMethods {
Expand All @@ -26,6 +27,7 @@ export enum UploaderScreen {
UploadingSchema,
UploadingControls,
LinkingGame,
CompilationError,
Completed,
}

Expand All @@ -45,6 +47,13 @@ export class SetUploadSchema implements Action {
constructor(public readonly shouldUpload: boolean) {}
}

/**
* Prompts the user to download a text file of the webpack console output/
*/
export class SaveConsoleOutput implements Action {
public readonly type = UploaderActionTypes.SAVE_CONSOLE;
}

/**
* Sets whether to upload the control bundle.
*/
Expand Down
28 changes: 25 additions & 3 deletions src/app/editor/uploader/uploader.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Store } from '@ngrx/store';
import { fromPromise } from 'rxjs/observable/fromPromise';
import { merge } from 'rxjs/observable/merge';
import { of } from 'rxjs/observable/of';
import { filter, map, mapTo, switchMap, take, tap } from 'rxjs/operators';
import { delay, filter, map, mapTo, switchMap, take, tap } from 'rxjs/operators';

import * as fromRoot from '../bedrock.reducers';
import { ElectronService, RpcError } from '../electron.service';
Expand All @@ -25,7 +25,7 @@ import {
UploaderMethods,
UploaderScreen,
} from './uploader.actions';
import { selectUploadControls, selectUploadSchema } from './uploader.reducer';
import { selectConsole, selectUploadControls, selectUploadSchema } from './uploader.reducer';

/**
* Effects module for account actions.
Expand All @@ -50,7 +50,7 @@ export class UploaderEffects {
uploadSchema ? UploaderScreen.UploadingSchema : UploaderScreen.LinkingGame,
),
),
catchErrorType(RpcError, err => new SetError(err)),
catchErrorType(RpcError, () => new SetScreen(UploaderScreen.CompilationError)),
),
),
);
Expand Down Expand Up @@ -145,6 +145,28 @@ export class UploaderEffects {
.ofType(UploaderActionTypes.UPLOADER_CLOSED)
.pipe(tap(() => this.console.clear()));

/**
* Resets state when the console is closed.
*/
@Effect({ dispatch: false })
public readonly saveConsoleOutput = this.actions.ofType(UploaderActionTypes.SAVE_CONSOLE).pipe(
toLatestFrom(this.store.select(selectConsole)),
map(output => {
const link = document.createElement('a');
const url = URL.createObjectURL(new Blob([output], { type: 'text' }));
link.href = url;
link.download = `webpack-output-${new Date().toISOString()}.txt`;
document.body.appendChild(link);
link.click();
return link;
}),
delay(1),
tap(link => {
URL.revokeObjectURL(link.href);
document.body.removeChild(link);
}),
);

constructor(
private readonly actions: Actions,
private readonly electron: ElectronService,
Expand Down
2 changes: 2 additions & 0 deletions src/app/editor/uploader/uploader.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { StoreModule } from '@ngrx/store';
import { SharedModule } from '../shared/shared.module';
import { AdvancedToggleModule } from '../ui/advanced-toggle/advanced-toggle.module';
import { ConsoleModule } from '../ui/console-display/console-display.module';
import { UploaderCompilationErrorComponent } from './uploader-compilation-error/uploader-compilation-error.component';
import { UploaderCompletedComponent } from './uploader-completed/uploader-completed.component';
import { UploaderConfirmingComponent } from './uploader-confirming/uploader-confirming.component';
import { UploaderConsoleService } from './uploader-console.service';
Expand Down Expand Up @@ -52,6 +53,7 @@ const errorStateMatcher = { isErrorState: (ctrl: FormControl) => ctrl.invalid };
entryComponents: [UploaderDialogComponent],
providers: [UploaderConsoleService, { provide: ErrorStateMatcher, useValue: errorStateMatcher }],
declarations: [
UploaderCompilationErrorComponent,
UploaderCompletedComponent,
UploaderConfirmingComponent,
UploaderDialogComponent,
Expand Down
11 changes: 10 additions & 1 deletion src/app/editor/uploader/uploader.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface IUploaderState {
screen: UploaderScreen;
uploadSchema: boolean;
uploadControls: boolean;
consoleOutput: string;
error?: RpcError;
}

Expand All @@ -23,6 +24,7 @@ const initialState: IUploaderState = {
screen: UploaderScreen.Confirming,
uploadSchema: true,
uploadControls: true,
consoleOutput: '',
};

export function uploaderReducer(
Expand All @@ -38,12 +40,14 @@ export function uploaderReducer(
return { ...state, uploadSchema: action.shouldUpload };
case UploaderActionTypes.SET_SCREEN:
return { ...state, screen: action.screen };
case UploaderActionTypes.UPDATE_WEBPACK_CONSOLE:
return { ...state, consoleOutput: state.consoleOutput + action.data };
case UploaderActionTypes.SET_ERROR:
return action.error.originalName === BundleNameTakenError.name
? { ...state, screen: UploaderScreen.Rename }
: { ...state, error: action.error };
case UploaderActionTypes.UPLOADER_CLOSED:
return { ...state, screen: UploaderScreen.Confirming, error: undefined };
return { ...state, screen: UploaderScreen.Confirming, consoleOutput: '', error: undefined };
default:
return state;
}
Expand Down Expand Up @@ -76,6 +80,11 @@ export const selectUploadControls = createSelector(uploaderState, s => s.uploadC
*/
export const selectUploadSchema = createSelector(uploaderState, s => s.uploadSchema);

/**
* Selects whether to upload schema.
*/
export const selectConsole = createSelector(uploaderState, s => s.consoleOutput);

/**
* Selects the rpc error that occurred during the process.
*/
Expand Down
11 changes: 10 additions & 1 deletion src/server/webpack-bundler-task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import {
} from '@mixer/cdk-webpack-plugin/dist/src/notifier';
import { ChildProcess } from 'child_process';
import { combineLatest } from 'rxjs/observable/combineLatest';
import { switchMap, take } from 'rxjs/operators';
import { filter, merge, switchMap, take } from 'rxjs/operators';
import { ReplaySubject } from 'rxjs/ReplaySubject';

import { WebpackState } from '../app/editor/controls/controls.actions';
import { WebpackBundlerError } from './errors';
import { spawnPackageScript } from './npm-exec';
import { Uploader } from './publish/uploader';
import { WebpackTask } from './webpack-task';
Expand Down Expand Up @@ -58,6 +59,14 @@ export class WebpackBundleTask extends WebpackTask<Promise<IBundleData>> {
await uploader.upload(bundled.location, metadata);
return { tarball: bundled.location, readme: bundled.readme, metadata };
}),
merge(
this.state.pipe(
filter(s => s === WebpackState.Failed),
switchMap(async () => {
throw new WebpackBundlerError();
}),
),
),
take(1),
)
.toPromise(),
Expand Down

0 comments on commit ac8dc9e

Please sign in to comment.