Using the new ESLint for linting errors, and fxing all the linting errors. All tests are passing again.

This commit is contained in:
Dan 2021-08-09 11:44:32 -04:00
parent c5dfe4ab2c
commit 7e09697ee1
41 changed files with 34575 additions and 312 deletions

51
.eslintrc.json Normal file
View File

@ -0,0 +1,51 @@
{
"root": true,
"ignorePatterns": [
"projects/**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"tsconfig.json",
"e2e/tsconfig.json"
],
"createDefaultProgram": true
},
"extends": [
"plugin:@angular-eslint/recommended",
"plugin:@angular-eslint/template/process-inline-templates"
],
"rules": {
"@angular-eslint/component-selector": [
"error",
{
"prefix": "lib",
"style": "kebab-case",
"type": "element"
}
],
"@angular-eslint/directive-selector": [
"error",
{
"prefix": "lib",
"style": "camelCase",
"type": "attribute"
}
]
}
},
{
"files": [
"*.html"
],
"extends": [
"plugin:@angular-eslint/template/recommended"
],
"rules": {}
}
]
}

View File

@ -37,14 +37,11 @@
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"builder": "@angular-eslint/builder:lint",
"options": {
"tsConfig": [
"projects/sartography-workflow-lib/tsconfig.lib.json",
"projects/sartography-workflow-lib/tsconfig.spec.json"
],
"exclude": [
"**/node_modules/**"
"lintFilePatterns": [
"projects/sartography-workflow-lib/**/*.ts",
"projects/sartography-workflow-lib/**/*.html"
]
}
}
@ -53,6 +50,7 @@
},
"defaultProject": "sartography-workflow-lib",
"cli": {
"analytics": "bc6a81f9-c4c5-46ec-a27b-ba6ec643e910"
"analytics": "bc6a81f9-c4c5-46ec-a27b-ba6ec643e910",
"defaultCollection": "@angular-eslint/schematics"
}
}

34341
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -15,8 +15,6 @@
"postinstall": "ngcc"
},
"private": true,
"dependencies": {
},
"peerDependencies": {
"@angular/animations": "^12.1.3",
"@angular/cdk": "^12.1.3",
@ -44,13 +42,22 @@
"zone.js": "~0.11.4"
},
"devDependencies": {
"@angular-devkit/build-angular": "12.1.3",
"@angular-eslint/builder": "12.3.1",
"@angular-eslint/eslint-plugin": "12.3.1",
"@angular-eslint/eslint-plugin-template": "12.3.1",
"@angular-eslint/schematics": "12.3.1",
"@angular-eslint/template-parser": "12.3.1",
"@angular/animations": "^12.1.3",
"@angular/cdk": "^12.1.3",
"@angular/cli": "^12.1.3",
"@angular/common": "^12.1.3",
"@angular/compiler": "^12.1.3",
"@angular/compiler-cli": "^12.1.3",
"@angular/core": "^12.1.3",
"@angular/flex-layout": "^12.0.0-beta.34",
"@angular/forms": "^12.1.3",
"@angular/language-service": "^12.1.3",
"@angular/material": "^12.1.3",
"@angular/platform-browser": "^12.1.3",
"@angular/platform-browser-dynamic": "^12.1.3",
@ -58,25 +65,14 @@
"@ngx-formly/core": "^5.10.22",
"@ngx-formly/material": "^5.10.22",
"@sentry/browser": "^5.30.0",
"@yellowspot/ng-truncate": "^1.7.0",
"highlight.js": "^10.7.2",
"lodash.isequal": "^4.5.0",
"ngx-device-detector": "^2.1.1",
"ngx-file-drop": "^11.1.0",
"ngx-highlightjs": "^4.1.4",
"ngx-markdown": "^12.0.1",
"rxjs": "^6.5.5",
"tslib": "^2.0.0",
"zone.js": "~0.11.4",
"@angular-devkit/build-angular": "12.1.3",
"@angular/cli": "^12.1.3",
"@angular/compiler-cli": "^12.1.3",
"@angular/language-service": "^12.1.3",
"@types/jasmine": "~3.6.0",
"@types/jasminewd2": "^2.0.10",
"@types/node": "^12.20.17",
"codelyzer": "^6.0.0",
"@typescript-eslint/eslint-plugin": "4.28.2",
"@typescript-eslint/parser": "4.28.2",
"@yellowspot/ng-truncate": "^1.7.0",
"eslint": "^7.26.0",
"highlight.js": "^10.7.2",
"jasmine-core": "~3.8.0",
"jasmine-spec-reporter": "~7.0.0",
"karma": "~6.3.4",
@ -84,10 +80,17 @@
"karma-coverage-istanbul-reporter": "~3.0.3",
"karma-jasmine": "~4.0.1",
"karma-jasmine-html-reporter": "^1.7.0",
"lodash.isequal": "^4.5.0",
"ng-packagr": "^12.1.0",
"ngx-device-detector": "^2.1.1",
"ngx-file-drop": "^11.1.0",
"ngx-highlightjs": "^4.1.4",
"ngx-markdown": "^12.0.1",
"protractor": "~7.0.0",
"rxjs": "^6.5.5",
"ts-node": "~8.6.2",
"tslint": "~6.1.0",
"typescript": "~4.3.5"
"tslib": "^2.0.0",
"typescript": "~4.3.5",
"zone.js": "~0.11.4"
}
}
}

View File

@ -0,0 +1,44 @@
{
"extends": "../../.eslintrc.json",
"ignorePatterns": [
"!**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"projects/sartography-workflow-lib/tsconfig.lib.json",
"projects/sartography-workflow-lib/tsconfig.spec.json"
],
"createDefaultProgram": true
},
"rules": {
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "lib",
"style": "camelCase"
}
],
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "lib",
"style": "kebab-case"
}
]
}
},
{
"files": [
"*.html"
],
"rules": {}
}
]
}

View File

@ -50,7 +50,8 @@ describe('ApiErrorsComponent', () => {
});
it('should dismiss', () => {
const dismissSpy = spyOn((component as any)._bottomSheetRef, 'dismiss').and.stub();
console.log("Component is ", component);
const dismissSpy = spyOn((component as any).bottomSheetRef, 'dismiss').and.stub();
component.dismiss(new MouseEvent('click'));
expect(dismissSpy).toHaveBeenCalled();
});

View File

@ -1,4 +1,4 @@
import {Component, Inject, OnInit} from '@angular/core';
import {Component, Inject} from '@angular/core';
import {MAT_BOTTOM_SHEET_DATA, MatBottomSheetRef} from '@angular/material/bottom-sheet';
import {ApiError} from '../../types/api';
import {snakeToSpace} from '../../util/string-clean';
@ -12,24 +12,21 @@ export interface ApiErrorsBottomSheetData {
templateUrl: './api-errors.component.html',
styleUrls: ['./api-errors.component.scss']
})
export class ApiErrorsComponent implements OnInit {
export class ApiErrorsComponent {
apiErrors: ApiError[];
snakeToSpace = snakeToSpace;
constructor(
@Inject(MAT_BOTTOM_SHEET_DATA) public data: ApiErrorsBottomSheetData,
private _bottomSheetRef: MatBottomSheetRef<ApiErrorsComponent>
private bottomSheetRef: MatBottomSheetRef<ApiErrorsComponent>
) {
if (data && data.apiErrors && data.apiErrors.length > 0) {
this.apiErrors = data.apiErrors;
}
}
ngOnInit(): void {
}
dismiss(event: MouseEvent) {
this._bottomSheetRef.dismiss();
this.bottomSheetRef.dismiss();
event.preventDefault();
}

View File

@ -9,11 +9,8 @@ import { Component, OnInit } from '@angular/core';
`,
styles: []
})
export class SartographyWorkflowLibComponent implements OnInit {
export class SartographyWorkflowLibComponent {
constructor() { }
ngOnInit() {
}
}

View File

@ -1,5 +1,5 @@
import {Component, OnDestroy, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {Component, OnDestroy} from '@angular/core';
import {ActivatedRoute} from '@angular/router';
import {Subscription} from 'rxjs';
import {ApiService} from '../../services/api.service';
@ -8,7 +8,7 @@ import {ApiService} from '../../services/api.service';
templateUrl: 'session-redirect.component.html',
styleUrls: ['./session-redirect.component.scss']
})
export class SessionRedirectComponent implements OnInit, OnDestroy {
export class SessionRedirectComponent implements OnDestroy {
token: string;
private sub: Subscription;
@ -30,9 +30,6 @@ export class SessionRedirectComponent implements OnInit, OnDestroy {
});
}
ngOnInit() {
}
ngOnDestroy() {
this.sub.unsubscribe();
}

View File

@ -52,7 +52,7 @@ describe('FileBaseComponent', () => {
},
expressionProperties:
{
doc_code: () => { return ('my_doc_code') } // doc_code should override the field key
doc_code: () => 'my_doc_code' // doc_code should override the field key
}
};
builder.buildForm(form, [field], {hi: 123}, {});

View File

@ -14,11 +14,11 @@ export class FileValueAccessorDirective implements ControlValueAccessor {
@HostListener('change', ['$event.target.files'])
onChange = (files: File[]) => {
};
}
@HostListener('blur', [])
onTouched = () => {
};
}
writeValue(value) {
}

View File

@ -64,7 +64,7 @@ export class FormPrintoutComponent {
if (fType === 'datepicker') {
let displayDate = '';
if(val) {
if (val) {
displayDate = formatDate(val, 'mediumDate', 'en-us');
}
return displayDate;

View File

@ -1,4 +1,4 @@
import {AfterViewInit, Component, OnInit} from '@angular/core';
import {Component, OnInit} from '@angular/core';
import {FieldWrapper} from '@ngx-formly/core';
import {MarkdownService} from 'ngx-markdown';
@ -7,7 +7,7 @@ import {MarkdownService} from 'ngx-markdown';
templateUrl: './markdown-description-wrapper.component.html',
styleUrls: ['./markdown-description-wrapper.component.scss']
})
export class MarkdownDescriptionWrapperComponent extends FieldWrapper implements OnInit, AfterViewInit {
export class MarkdownDescriptionWrapperComponent extends FieldWrapper implements OnInit {
constructor(private markdownService: MarkdownService) {
super();
}
@ -20,7 +20,5 @@ export class MarkdownDescriptionWrapperComponent extends FieldWrapper implements
};
}
ngAfterViewInit(): void {
}
}

View File

@ -26,7 +26,7 @@ const renderComponent = (field: FormlyFieldConfig) => {
ReactiveFormsModule,
],
});
}
};
describe('RadioDataFieldComponent', () => {
it('should render radio buttons with data', () => {

View File

@ -20,7 +20,7 @@ export class RadioDataFieldComponent extends FormlyFieldRadio {
) {
const storedVal = this.model[key].value;
if (this.radioGroup) {
if (this.radioGroup && this.radioGroup._radios) {
const radios = this.radioGroup._radios.toArray();
for (const radio of radios) {
@ -30,7 +30,6 @@ export class RadioDataFieldComponent extends FormlyFieldRadio {
}
}
}
return null;
}

View File

@ -1,4 +1,4 @@
import {Component, OnInit} from '@angular/core';
import {Component} from '@angular/core';
import {MatDialog} from '@angular/material/dialog';
import {FieldArrayType, FormlyFieldConfig, FormlyFormOptions} from '@ngx-formly/core';
import * as createClone_ from 'rfdc';
@ -12,7 +12,7 @@ import {ApiService} from '../../../services/api.service';
templateUrl: './repeat-section.component.html',
styleUrls: ['./repeat-section.component.scss']
})
export class RepeatSectionComponent extends FieldArrayType implements OnInit {
export class RepeatSectionComponent extends FieldArrayType {
constructor(
public dialog: MatDialog,
protected api: ApiService
@ -20,9 +20,6 @@ export class RepeatSectionComponent extends FieldArrayType implements OnInit {
super();
}
ngOnInit(): void {
}
openDialog(i: number, f?: FormlyFieldConfig) {
const isEdit = !!f;
const title = this.field.templateOptions.label || 'Add ' + this.field.templateOptions.buttonLabel;

View File

@ -1,4 +1,4 @@
// tslint:disable-next-line:max-line-length
// eslint-disable-next-line max-len
export const EMAIL_REGEX = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/i;
export default EMAIL_REGEX;

View File

@ -113,7 +113,7 @@ export function AutocompleteValidator(control: FormControl): ValidationErrors {
);
if (isRequired && !control.value) {
return {required: true}
return {required: true};
}
if (control.value) {
@ -135,7 +135,7 @@ export function FileFieldValidator(control: FormControl): ValidationErrors {
fields[0].templateOptions.required &&
!control.value
) {
return {required: true}
return {required: true};
}
return null;
}
@ -154,7 +154,7 @@ export function FileUploadValidator(control: FormControl): ValidationErrors {
fields[0].templateOptions.required &&
!control.value
) {
return {required: true}
return {required: true};
}
return null;
}
@ -173,7 +173,7 @@ export function RepeatSectionValidator(control: FormControl): ValidationErrors {
fields[0].templateOptions.required &&
!control.value
) {
return {required: true}
return {required: true};
}
return null;
}

View File

@ -1,4 +1,4 @@
// tslint:disable-next-line:max-line-length
// eslint-disable-next-line max-len
export const PHONE_REGEX = /^(?:\+?1\s*(?:[.-]\s*)?)?(?:\(\s*([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9])\s*\)|([2-9]1[02-9]|[2-9][02-8]1|[2-9][02-8][02-9]))\s*(?:[.-]\s*)?([2-9]1[02-9]|[2-9][02-9]1|[2-9][02-9]{2})\s*(?:[.-]\s*)?([0-9]{4})(?:\s*(?:#|x\.?|ext\.?|extension)\s*(\d+))?$/i;
export default PHONE_REGEX;

View File

@ -7,7 +7,7 @@ export class CleanUpLdapPropertiesPipe implements PipeTransform {
// Takes a comma-delimited string from LDAP and returns a human-readable string.
transform(value: string, ...args: any[]): any {
return this.itemsFromLdap(value).join(', ')
return this.itemsFromLdap(value).join(', ');
}
// E1:Clinician Physician
@ -36,7 +36,7 @@ export class CleanUpLdapPropertiesPipe implements PipeTransform {
});
items.add(s.trim());
} else {
items.add(item.trim())
items.add(item.trim());
}
}

View File

@ -17,7 +17,7 @@ describe('ToFormlyPipe', () => {
const workflowId = 20;
const studyId = 15;
const workflowSpec = 'PythonWorkflow';
const fileParams = {workflow_id:workflowId, study_id: studyId, workflow_spec_id: workflowSpec};
const fileParams = {workflow_id: workflowId, study_id: studyId, workflow_spec_id: workflowSpec};
beforeEach(async(() => {
TestBed.configureTestingModule({
@ -303,7 +303,7 @@ describe('ToFormlyPipe', () => {
expect(after[0].templateOptions.workflow_id).toEqual(workflowId);
expect(after[0].templateOptions.study_id).toEqual(studyId);
expect(after[0].templateOptions.workflow_spec_id).toEqual(workflowSpec);
expect(after[0].templateOptions.doc_code).not.toBeNull()
expect(after[0].templateOptions.doc_code).not.toBeNull();
});
@ -414,9 +414,9 @@ describe('ToFormlyPipe', () => {
type: 'textarea',
},
];
const _getAutocompleteNumResultsSpy = spyOn((pipe as any), '_getAutocompleteNumResults').and.callThrough();
const getAutocompleteNumResultsSpy = spyOn((pipe as any), '_getAutocompleteNumResults').and.callThrough();
const after = pipe.transform(before, fileParams);
expect(_getAutocompleteNumResultsSpy).toHaveBeenCalledWith(before[1], 5);
expect(getAutocompleteNumResultsSpy).toHaveBeenCalledWith(before[1], 5);
expect(after[1].key).toEqual(before[1].id);
expect(after[1].type).toEqual('autocomplete');
expect(after[1].templateOptions.label).toEqual(before[1].label);

View File

@ -109,7 +109,7 @@ export interface PythonEvaluation {
name: 'toFormly'
})
export class ToFormlyPipe implements PipeTransform {
private defaultFileParams:FileParams = {}
private defaultFileParams: FileParams = {};
constructor(private apiService?: ApiService) {
}
@ -210,7 +210,7 @@ export class ToFormlyPipe implements PipeTransform {
}
resultField.templateOptions = {
datepickerOptions : { datepickerTogglePosition: 'prefix'}
}
};
break;
case 'files':
resultField.type = 'files';
@ -531,8 +531,8 @@ export class ToFormlyPipe implements PipeTransform {
*/
private getPythonEvalFunction(field: BpmnFormJsonField, p: BpmnFormJsonFieldProperty, defaultValue = false, method = null) {
return (model: any, formState: any, fieldConfig: FormlyFieldConfig) => {
if(!formState) {
formState = {}
if (!formState) {
formState = {};
}
// Establish some variables to be added to the form state.
@ -560,7 +560,7 @@ export class ToFormlyPipe implements PipeTransform {
// wrap the assignment to the variable in a promise - so that it gets evaluated as a part
// of angular's next round of DOM updates, so we avoid modifying the state in the middle of a call.
// If there is no error, update the value.
if(!response.hasOwnProperty('error')) {
if (!response.hasOwnProperty('error')) {
Promise.resolve(null).then(() => {
// The last successful evaluation becomes the new default, this keeps things from flickering.
formState[variableKey].default = response.result;
@ -570,7 +570,7 @@ export class ToFormlyPipe implements PipeTransform {
},
(error: ApiError) => {
console.log(`Failed to update field ${field.id} unable to process expression. ${error.message}`);
formState[variableKey] = 'error'
formState[variableKey] = 'error';
}
);
}
@ -578,21 +578,21 @@ export class ToFormlyPipe implements PipeTransform {
if (!(key in formState[variableKey])) {
formState[variableKey][key] = formState[variableKey].default;
let data = model;
if(formState.hasOwnProperty('mainModel')) {
if (formState.hasOwnProperty('mainModel')) {
data = {...formState.mainModel, ...model};
}
formState[variableSubjectKey].next({expression: p.value, data, key});
}
// We immediately return the variable, but it might change due to the above observable.
return formState[variableKey][key]
return formState[variableKey][key];
};
}
private hashCode(str) {
/* tslint:disable:no-bitwise */
/* eslint-disable no-bitwise */
return str.split('').reduce((prevHash, currVal) =>
(((prevHash << 5) - prevHash) + currVal.charCodeAt(0))|0, 0);
/* tslint:enable:no-bitwise */
(((prevHash << 5) - prevHash) + currVal.charCodeAt(0)) | 0, 0);
/* eslint-enable no-bitwise */
}
/** Get num_results property from given field, or return the given default if no num_results property found. */
@ -611,7 +611,7 @@ export class ToFormlyPipe implements PipeTransform {
private _addClassName(field: FormlyFieldConfig, className: string): string {
const newClasses = className ? className.split(' ') : [];
const oldClasses = field.className ? field.className.split(' ') : [];
return Array.from(new Set(oldClasses.concat(newClasses))).join(' ')
return Array.from(new Set(oldClasses.concat(newClasses))).join(' ');
}
// Returns the appropriate className string for the given field, depending on its read-only state

View File

@ -14,7 +14,7 @@ import {MatExpansionModule} from '@angular/material/expansion';
// @dynamic
export function import_json() {
import('highlight.js/lib/languages/json.js')
import('highlight.js/lib/languages/json.js');
}
@NgModule({

View File

@ -438,9 +438,9 @@ export class ApiService {
getTaskEvents(action?: TaskAction, studyId?: number, workflowId?: number): Observable<TaskEvent[]> {
const url = this.apiRoot + this.endpoints.taskEvents;
let httpParams = new HttpParams();
if (action) httpParams = httpParams.set('action', action);
if (studyId) httpParams = httpParams.set('study', studyId.toString());
if (workflowId) httpParams = httpParams.set('workflow', workflowId.toString());
if (action) { httpParams = httpParams.set('action', action); }
if (studyId) { httpParams = httpParams.set('study', studyId.toString()); }
if (workflowId) { httpParams = httpParams.set('workflow', workflowId.toString()); }
return this.httpClient
.get<TaskEvent[]>(url + '?' + httpParams.toString())

View File

@ -58,6 +58,6 @@ export class ErrorInterceptor implements HttpInterceptor {
const errorMessage = err.message || err.statusText;
return throwError(errorMessage);
}))
}));
}
}

View File

@ -68,7 +68,7 @@ describe('ErrorInterceptor', () => {
httpClient.get<Study>(fakeUrl).subscribe(
_ => fail(`should have failed with a ${errorStatus} error`),
error => {
expect(error).toEqual(expectedMessage, 'message')
expect(error).toEqual(expectedMessage, 'message');
}
);
@ -80,7 +80,7 @@ describe('ErrorInterceptor', () => {
});
it('display API error in bottom sheet', () => {
const bottomSheetOpenSpy = spyOn(errorInterceptor.bottomSheet, 'open').and.stub()
const bottomSheetOpenSpy = spyOn(errorInterceptor.bottomSheet, 'open').and.stub();
const logErrorSpy = spyOn((errorInterceptor as any).googleAnalyticsService, 'errorEvent').and.stub();
const errorStatus = 400;
const fakeUrl = 'apiRoot/study/12345';
@ -89,7 +89,7 @@ describe('ErrorInterceptor', () => {
httpClient.get<Study>(fakeUrl).subscribe(
_ => fail(`should have failed with a ${errorStatus} error`),
error => {
expect(error).toEqual(expectedMessage, 'message')
expect(error).toEqual(expectedMessage, 'message');
}
);

View File

@ -22,7 +22,7 @@ export class GoogleAnalyticsService {
}
public authEvent(req: HttpRequest<any>) {
this.event('login', 'authentication', req.url)
this.event('login', 'authentication', req.url);
}
public errorEvent(error: ApiError) {
@ -33,7 +33,7 @@ export class GoogleAnalyticsService {
if (typeof gtag !== 'undefined') {
gtag('set', {user_id: uid}); // Set the user ID using signed-in user_id.
}
this.event('user-id available', 'authentication', uid)
this.event('user-id available', 'authentication', uid);
}
public init(analyticsKey) {

View File

@ -47,20 +47,20 @@ describe('UserService', () => {
userReq.flush(mockUser0);
expect(!!localStorage.getItem( 'admin_view_as')).toBeFalse();
expect((service as any)._realUser.value).toEqual(mockUser0);
expect((service as any)._isAdmin.value).toBeTrue();
expect((service as any)._isImpersonating.value).toBeFalse();
expect((service as any).realUser.value).toEqual(mockUser0);
expect((service as any).isAdmin.value).toBeTrue();
expect((service as any).isImpersonating.value).toBeFalse();
});
it('should impersonate user',() => {
it('should impersonate user', () => {
const userReq = httpMock.expectOne('apiRoot/user');
expect(userReq.request.method).toEqual('GET');
userReq.flush(mockUser0);
// click on the nav link and then verify user is != realUser
(service as any).viewAs('rhh8n')
(service as any).viewAs('rhh8n');
// First step - we get back the main user
const userReq1 = httpMock.expectOne('apiRoot/user');
expect(userReq1.request.method).toEqual('GET');
@ -71,9 +71,9 @@ describe('UserService', () => {
userReq2.flush(mockUser1);
// now we should be impersonating but still admin so we can still switch
expect(localStorage.getItem( 'admin_view_as')).toEqual('rhh8n')
expect((service as any)._isAdmin.value).toBeTrue();
expect((service as any)._isImpersonating.value).toBeTrue();
expect(localStorage.getItem( 'admin_view_as')).toEqual('rhh8n');
expect((service as any).isAdmin.value).toBeTrue();
expect((service as any).isImpersonating.value).toBeTrue();
});

View File

@ -11,34 +11,34 @@ import {BehaviorSubject} from 'rxjs';
})
export class UserService {
@Output() userChanged = new EventEmitter<User>();
private readonly _user = new BehaviorSubject<User>(undefined);
private readonly _isAdmin = new BehaviorSubject(false);
private readonly _isImpersonating = new BehaviorSubject(false);
private readonly _realUser = new BehaviorSubject<User>(undefined);
private readonly user = new BehaviorSubject<User>(undefined);
private readonly isAdmin = new BehaviorSubject(false);
private readonly isImpersonating = new BehaviorSubject(false);
private readonly realUser = new BehaviorSubject<User>(undefined);
// we give the option to use observables
readonly user$ = this._user.asObservable();
readonly realUser$ =this._realUser.asObservable();
readonly isImpersonating$ = this._isImpersonating.asObservable();
readonly isAdmin$ = this._isAdmin.asObservable();
readonly user$ = this.user.asObservable();
readonly realUser$ = this.realUser.asObservable();
readonly isImpersonating$ = this.isImpersonating.asObservable();
readonly isAdmin$ = this.isAdmin.asObservable();
constructor(private api: ApiService,
public googleAnalyticsService: GoogleAnalyticsService) {
this._loadUser()
this._loadUser();
}
private _impersonate() {
const impersonateUid = localStorage.getItem('admin_view_as');
if ((impersonateUid !== null) &&
(impersonateUid !== this._realUser.value.uid) &&
this._isAdmin.value) {
(impersonateUid !== this.realUser.value.uid) &&
this.isAdmin.value) {
this.api.getUser(impersonateUid).subscribe(u => {
this._user.next(u);
this._isImpersonating.next(true);
this._afterUserLoad()
}, () => this._onLoginError())
this.user.next(u);
this.isImpersonating.next(true);
this._afterUserLoad();
}, () => this._onLoginError());
} else {
this._user.next(this._realUser.value);
this._isImpersonating.next(false);
this.user.next(this.realUser.value);
this.isImpersonating.next(false);
this._afterUserLoad();
}
}
@ -46,8 +46,8 @@ export class UserService {
private _loadUser() {
this.api.getUser().subscribe(u => {
if (u) {
this._realUser.next(u);
this._isAdmin.next(u.is_admin);
this.realUser.next(u);
this.isAdmin.next(u.is_admin);
this._impersonate();
} else {
this._onLoginError();
@ -56,14 +56,14 @@ export class UserService {
}
private _afterUserLoad() {
if (this._realUser.value && this._realUser.value.uid) {
this.googleAnalyticsService.setUser(this._realUser.value.uid);
if (this.realUser.value && this.realUser.value.uid) {
this.googleAnalyticsService.setUser(this.realUser.value.uid);
}
this.userChanged.emit(this._user.value);
this.userChanged.emit(this.user.value);
}
viewAs(uid: string) {
if (this._isAdmin.value && (uid !== this._realUser.value.uid)) {
if (this.isAdmin.value && (uid !== this.realUser.value.uid)) {
localStorage.setItem('admin_view_as', uid);
} else {
localStorage.removeItem('admin_view_as');

View File

@ -167,4 +167,4 @@ export const mockDocumentDirectory: DocumentDirectory[] = [
}]
}
]
];

View File

@ -12,7 +12,7 @@ export const mockFormlyFieldModel = {
},
third_field_other: 'Third field other value',
fourth_field: new Date(),
fifth_field: {id:101, name:'bob.txt'},
fifth_field: {id: 101, name: 'bob.txt'},
}
};

View File

@ -3,17 +3,17 @@ import {ScriptInfo} from '../../types/script-info';
export const mockScriptInfo0: ScriptInfo = {
name: 'CompleteTemplate',
description: `Let the paint work. When things happen - enjoy them. They're little gifts. Let's put a touch more of the magic here. A fan brush can be your best friend. Let your imagination be your guide.`,
}
};
export const mockScriptInfo1: ScriptInfo = {
name: 'StudyInfo',
description: `Just go out and talk to a tree. Make friends with it. Let's make some happy little clouds in our world. See there how easy that is.`,
}
};
export const mockScriptInfo2: ScriptInfo = {
name: 'FactService',
description: `Let's just drop a little Evergreen right here. For the lack of a better word I call them hangy downs. So often we avoid running water, and running water is a lot of fun. Steve wants reflections, so let's give him reflections.`,
}
};
export const mockScriptInfos: ScriptInfo[] = [
mockScriptInfo0,

View File

@ -16,7 +16,7 @@ export const mockTaskEvent0: TaskEvent = {
task_state: mockWorkflowTask0.state,
task_lane: 'supervisor',
date: new Date(),
}
};
export const mockTaskEvent1: TaskEvent = {
id: 1,
@ -31,7 +31,7 @@ export const mockTaskEvent1: TaskEvent = {
task_state: mockWorkflowTask1.state,
task_lane: '',
date: new Date(),
}
};
export const mockTaskEvents: TaskEvent[] = [
mockTaskEvent0,

View File

@ -17,7 +17,7 @@ export enum ApprovalStatusLabels {
export interface ApprovalFile {
id: number;
name: string;
content_type: string
content_type: string;
}
export interface ApprovalPerson {

View File

@ -50,7 +50,7 @@ export interface WorkflowMetadata {
status: WorkflowStatus;
state_message?: string; // An optional message explaining the state of a workflow.
display_order?: number;
is_review? : boolean;
is_review?: boolean;
}
@ -65,7 +65,7 @@ export interface Workflow {
next_task?: WorkflowTask;
is_latest_spec?: boolean;
spec_version?: string;
is_review? : boolean;
is_review?: boolean;
title?: string;
redirect?: number;
}
@ -80,7 +80,7 @@ export interface WorkflowNavItem {
backtrack_to?: string;
task_id?: string;
state?: WorkflowTaskState;
children?: WorkflowNavItem[]
children?: WorkflowNavItem[];
}

View File

@ -55,5 +55,5 @@ describe('newFileFromResponse', () => {
headers: mockHeaders
}
))).toEqual(mockFile0);
})
});
});

View File

@ -1,14 +1,14 @@
import {swapArrayElements, moveArrayElementDown, moveArrayElementUp} from './move-array-element';
describe('moveArrayElementTo', () => {
it('should move an array item to the destination index',() => {
it('should move an array item to the destination index', () => {
const arrayBefore = ['a', 'b', 'c', 'd'];
const arrayAfter = ['d', 'b', 'c', 'a'];
swapArrayElements(arrayBefore, 0, 3);
expect(arrayBefore).toEqual(arrayAfter);
});
it('should do nothing if one of the given indexes is invalid',() => {
it('should do nothing if one of the given indexes is invalid', () => {
const arrayBefore = ['a', 'b', 'c', 'd'];
const arrayUnchanged = ['a', 'b', 'c', 'd'];
swapArrayElements(arrayBefore, -1, 3);
@ -18,14 +18,14 @@ describe('moveArrayElementTo', () => {
});
describe('moveArrayElementUp', () => {
it('should move an array item up',() => {
it('should move an array item up', () => {
const arrayBefore = ['a', 'b', 'c', 'd'];
const arrayAfter = ['a', 'c', 'b', 'd'];
moveArrayElementUp(arrayBefore, 2);
expect(arrayBefore).toEqual(arrayAfter);
});
it('should do nothing if the array item is already first',() => {
it('should do nothing if the array item is already first', () => {
const arrayBefore = ['a', 'b', 'c', 'd'];
const arrayUnchanged = ['a', 'b', 'c', 'd'];
moveArrayElementUp(arrayBefore, 0);
@ -34,14 +34,14 @@ describe('moveArrayElementUp', () => {
});
describe('moveArrayElementDown', () => {
it('should move an array item down',() => {
it('should move an array item down', () => {
const arrayBefore = ['a', 'b', 'c', 'd'];
const arrayAfter = ['a', 'c', 'b', 'd'];
moveArrayElementDown(arrayBefore, 1);
expect(arrayBefore).toEqual(arrayAfter);
});
it('should do nothing if the array item is already last',() => {
it('should do nothing if the array item is already last', () => {
const arrayBefore = ['a', 'b', 'c', 'd'];
const arrayUnchanged = ['a', 'b', 'c', 'd'];
moveArrayElementDown(arrayBefore, 3);

View File

@ -8,7 +8,7 @@ export const moveArrayElementUp = (a: Array<any>, i: number) => {
} else {
swapArrayElements(a, i, i - 1);
}
}
};
/**
* Given the array `a`, moves element with index `i` one position down.
@ -20,7 +20,7 @@ export const moveArrayElementDown = (a: Array<any>, i: number) => {
} else {
swapArrayElements(a, i, i + 1);
}
}
};
/**
* Given the array `a`, swaps the positions of elements with indexes `i` and `j`,
@ -30,4 +30,4 @@ export const swapArrayElements = (a: Array<any>, i: number, j: number) => {
if (i >= 0 && j >= 0 && (a.length >= i + 1) && (a.length >= j + 1)) {
[a[i], a[j]] = [a[j], a[i]]; // Swap items using destructuring
}
}
};

View File

@ -16,8 +16,8 @@ export const CameltoSnakeCase = (str: string): string => {
// * https://stackoverflow.com/questions/15369566/putting-space-in-camel-case-string-using-regular-expression
const regex = /(?<!^)([A-Z][a-z]|(?<=[a-z])[A-Z])/g;
return !str ? '' : String(str)
.replace(regex,(match) => {
return '_' + match.toLowerCase();})
.replace(regex, (match) => {
return '_' + match.toLowerCase(); })
.toLowerCase();
};
@ -36,4 +36,4 @@ export const cleanUpFilename = (str: string, extension: FileType|string): string
export const snakeToSpace = (codeString: string): string => {
return codeString.replace(/([^A-Za-z0-9])/g, ' ').toLowerCase();
}
};

View File

@ -1,17 +0,0 @@
{
"extends": "../../tslint.json",
"rules": {
"directive-selector": [
true,
"attribute",
"lib",
"camelCase"
],
"component-selector": [
true,
"element",
"lib",
"kebab-case"
]
}
}

View File

@ -1,143 +0,0 @@
{
"extends": "tslint:recommended",
"rulesDirectory": [
"codelyzer"
],
"rules": {
"align": {
"options": [
"parameters",
"statements"
]
},
"array-type": false,
"arrow-parens": false,
"arrow-return-shorthand": true,
"deprecation": {
"severity": "warning"
},
"import-blacklist": [
true,
"rxjs/Rx"
],
"curly": true,
"interface-name": false,
"max-classes-per-file": false,
"eofline": true,
"max-line-length": [
true,
140
],
"import-spacing": true,
"indent": {
"options": [
"spaces"
]
},
"member-access": false,
"member-ordering": [
true,
{
"order": [
"static-field",
"instance-field",
"static-method",
"instance-method"
]
}
],
"no-consecutive-blank-lines": false,
"no-console": [
true,
"debug",
"info",
"time",
"timeEnd",
"trace"
],
"no-empty": false,
"no-inferrable-types": [
true,
"ignore-params"
],
"no-non-null-assertion": true,
"no-redundant-jsdoc": true,
"no-switch-case-fall-through": true,
"no-var-requires": false,
"object-literal-key-quotes": [
true,
"as-needed"
],
"object-literal-sort-keys": false,
"ordered-imports": false,
"quotemark": [
true,
"single"
],
"trailing-comma": false,
"component-class-suffix": true,
"contextual-lifecycle": true,
"directive-class-suffix": true,
"no-conflicting-lifecycle": true,
"no-host-metadata-property": true,
"no-input-rename": true,
"no-inputs-metadata-property": true,
"no-output-native": true,
"no-output-on-prefix": true,
"no-output-rename": true,
"semicolon": {
"options": [
"always"
]
},
"space-before-function-paren": {
"options": {
"anonymous": "never",
"asyncArrow": "always",
"constructor": "never",
"method": "never",
"named": "never"
}
},
"no-outputs-metadata-property": true,
"template-banana-in-box": true,
"template-no-negated-async": true,
"typedef-whitespace": {
"options": [
{
"call-signature": "nospace",
"index-signature": "nospace",
"parameter": "nospace",
"property-declaration": "nospace",
"variable-declaration": "nospace"
},
{
"call-signature": "onespace",
"index-signature": "onespace",
"parameter": "onespace",
"property-declaration": "onespace",
"variable-declaration": "onespace"
}
]
},
"use-lifecycle-interface": true,
"use-pipe-transform-interface": true,
"variable-name": {
"options": [
"ban-keywords",
"check-format",
"allow-pascal-case"
]
},
"whitespace": {
"options": [
"check-branch",
"check-decl",
"check-operator",
"check-separator",
"check-type",
"check-typecast"
]
}
}
}