Skip to content

Commit

Permalink
Initial web auth
Browse files Browse the repository at this point in the history
  • Loading branch information
newmanw committed Oct 4, 2024
1 parent 9586daa commit b944722
Show file tree
Hide file tree
Showing 14 changed files with 604 additions and 460 deletions.
5 changes: 2 additions & 3 deletions plugins/arcgis/service/src/ArcGISConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export interface FeatureServiceConfig {
* The feature layers.
*/
layers: FeatureLayerConfig[]

}

/**
Expand Down Expand Up @@ -125,7 +124,7 @@ export interface OAuthAuthConfig {
/**
* The expiration date for the temporary token
*/
authTokenExpires?: string
authTokenExpires?: number

/**
* The Refresh token for OAuth
Expand All @@ -135,7 +134,7 @@ export interface OAuthAuthConfig {
/**
* The expiration date for the Refresh token
*/
refreshTokenExpires?: string
refreshTokenExpires?: number
}

/**
Expand Down
248 changes: 131 additions & 117 deletions plugins/arcgis/service/src/index.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<h2 mat-dialog-title>Delete Feature Service?</h2>

<mat-dialog-content class="mat-typography">{{url}}</mat-dialog-content>

<mat-dialog-actions align="end">
<button mat-button mat-dialog-close>Cancel</button>
<button mat-flat-button color="primary" [mat-dialog-close]="true"
>Delete</button>
</mat-dialog-actions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { MatDialogRef } from '@angular/material/dialog';
import { ArcLayerDeleteDialogComponent } from './arc-layer-delete-dialog.component';

describe('Arc Layer Delete Dialog', () => {
let component: ArcLayerDeleteDialogComponent;
let fixture: ComponentFixture<ArcLayerDeleteDialogComponent>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ArcLayerDeleteDialogComponent],
imports: [HttpClientTestingModule],
providers: [{
provide: MatDialogRef,
useValue: {}
},]
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(ArcLayerDeleteDialogComponent);
component = fixture.componentInstance;
});


it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Component, Inject } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';

@Component({
selector: 'arc-layer-delete-dialog',
templateUrl: 'arc-layer-delete-dialog.component.html',
styleUrls: ['./arc-layer-delete-dialog.component.scss']
})
export class ArcLayerDeleteDialogComponent {

url: string

constructor(
public dialogRef: MatDialogRef<ArcLayerDeleteDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: string
) {
this.url = data
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<div class="component" @disableOnEnter>
<div *ngIf="state === State.Validate" class="dialog" @slide>
<form [formGroup]="layerForm" class="dialog-content">
<div mat-dialog-title>ArcGIS Feature Service</div>

<mat-dialog-content>
<div>
<mat-form-field>
<mat-label>URL</mat-label>
<input matInput formControlName="url" required
placeholder="http{s}://{domain}/arcgis/rest/services/{service}/FeatureServer" />
<mat-error *ngIf="layerForm.hasError('required')">URL is required</mat-error>
</mat-form-field>

<mat-form-field appearance="fill">
<mat-label>Authentication</mat-label>
<mat-select formControlName="authenticationType">
<mat-option *ngFor="let authenticationType of authenticationTypes" [value]="authenticationType.value">
{{authenticationType.title}}
</mat-option>
</mat-select>
</mat-form-field>

<ng-container [ngSwitch]="layerForm.controls.authenticationType.value">
<div *ngSwitchCase="'token'" formGroupName="token">
<mat-form-field appearance="fill">
<mat-label>Token</mat-label>
<input matInput formControlName="token" required />
<mat-error>Token is required</mat-error>
</mat-form-field>
</div>
<div *ngSwitchCase="'oauth'" formGroupName="oauth">
<mat-form-field appearance="fill">
<mat-label>Client Id</mat-label>
<input matInput formControlName="clientId" required />
<mat-error>Client Id is required</mat-error>
</mat-form-field>

<mat-form-field appearance="fill">
<mat-label>Client Secret</mat-label>
<input matInput formControlName="clientSecret" required />
<mat-error>Client Secret is required</mat-error>
</mat-form-field>
</div>
<div *ngSwitchCase="'local'" formGroupName="local">
<mat-form-field appearance="fill">
<mat-label>Username</mat-label>
<input matInput formControlName="username" required />
<mat-error>Username Id is required</mat-error>
</mat-form-field>

<mat-form-field appearance="fill">
<mat-label>Password</mat-label>
<input matInput formControlName="password" required />
<mat-error>Password is required</mat-error>
</mat-form-field>
</div>
</ng-container>
</div>
</mat-dialog-content>

<mat-dialog-actions align="end">
<button mat-flat-button color="primary" [disabled]="loading" (click)="onValidate()">Validate</button>
</mat-dialog-actions>

</form>
</div>

<div *ngIf="state === State.Layers" class="dialog" @slide>
<div [formGroup]="layerForm" class="dialog-content">
<div mat-dialog-title>ArcGIS Feature Layers</div>

<mat-dialog-content>
<div>
<mat-selection-list #layerList color="primary">
<mat-list-option *ngFor="let layer of layers">
{{layer}}
</mat-list-option>
</mat-selection-list>
</div>
</mat-dialog-content>

<mat-dialog-actions align="end">
<button mat-flat-button color="primary" [disabled]="loading" (click)="onSave()">Save</button>
</mat-dialog-actions>

</div>
</div>

<div *ngIf="loading" class="dialog-content loading">
<mat-spinner></mat-spinner>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
mat-form-field {
width: 100%;
}

mat-dialog-content {
flex: 1
}

.component {
min-width: 600px;
min-height: 400px;
position: relative;
}

.dialog {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}

.dialog-content {
display: flex;
flex-direction: column;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}

.loading {
display: flex;
align-items: center;
justify-content: center;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { MatDialogRef } from '@angular/material/dialog';
import { ArcLayerDialogComponent } from './arc-layer-dialog.component';

describe('Arc Layer Dialog', () => {
let component: ArcLayerDialogComponent;
let fixture: ComponentFixture<ArcLayerDialogComponent>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ArcLayerDialogComponent],
imports: [HttpClientTestingModule],
providers: [{
provide: MatDialogRef,
useValue: {}
},]
}).compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(ArcLayerDialogComponent);
component = fixture.componentInstance;
});


it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { Component, Inject, ViewChild } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { animate, style, transition, trigger } from '@angular/animations';
import { MatSelectionList } from '@angular/material/list';
import { FeatureServiceConfig } from '../ArcGISConfig';
import { ArcService } from '../arc.service';

enum State { Validate, Layers }

interface AuthenticationType {
title: string
value: string
}

export interface DialogData {
featureService?: FeatureServiceConfig
}

@Component({
selector: 'arc-layer-dialog',
templateUrl: 'arc-layer-dialog.component.html',
styleUrls: ['./arc-layer-dialog.component.scss'],
animations: [
trigger('disableOnEnter', [
transition(':enter', [])
]),
trigger('slide', [
transition(':enter', [
style({ transform: 'translateX(100%)' }),
animate('250ms', style({ transform: 'translateX(0%)' })),
]),
transition(':leave', [
animate('250ms', style({ transform: 'translateX(-100%)' }))
])
])
]
})
export class ArcLayerDialogComponent {
State = State
state: State = State.Validate

loading = false

authenticationTypes: AuthenticationType[] = [{
title: 'OAuth',
value: 'oauth'
},{
title: 'Username/Password',
value: 'local'
},{
title: 'Token',
value: 'token'
}]

layerForm: FormGroup
layers: string[]

@ViewChild(MatSelectionList) layerList: MatSelectionList

constructor(
public dialogRef: MatDialogRef<ArcLayerDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: DialogData,
private arcService: ArcService
) {
const { featureService } = data
// TODO update all fields with info from pass in service
this.layerForm = new FormGroup({
url: new FormControl(featureService?.url || 'https://arcgis.geointnext.com/arcgis/rest/services/Hosted/Billy_Test/FeatureServer', [Validators.required]),
authenticationType: new FormControl('oauth', [Validators.required]),
token: new FormGroup({
token: new FormControl('', [Validators.required])
}),
oauth: new FormGroup({
clientId: new FormControl('', [Validators.required]),
clientSecret: new FormControl('', [Validators.required])
}),
local: new FormGroup({
username: new FormControl('', [Validators.required]),
password: new FormControl('', [Validators.required])
})
})

this.layers = featureService?.layers.map(layer => `${layer.layer}`) || []
}

onValidate(): void {
this.loading = true
const url = this.layerForm.controls.url.value
this.arcService.authenticate(this.layerForm.controls.url.value).subscribe(() => {
console
this.arcService.fetchArcLayers(url).subscribe(layers => {
this.state = State.Layers
this.layers = layers.layers.map(layer => layer.name)
this.loading = false
})
})
}

onSave(): void {
// TODO put the rest of the auth stuff here
const server: FeatureServiceConfig = {
url: this.layerForm.controls.url.value || '',
auth: {
username: '',
password: '',
clientId: '',
clientSecret: ''
},
layers: this.layerList.selectedOptions.selected.map(option => {
return { layer: `${option.value}` }
})
}
this.dialogRef.close(server)
}
}
Loading

0 comments on commit b944722

Please sign in to comment.