Add an integration manager test widget

Fixes https://github.com/turt2live/matrix-dimension/issues/244
This commit is contained in:
Travis Ralston 2019-03-24 14:37:51 -06:00
parent 83b2062d87
commit bb874b1fa0
10 changed files with 247 additions and 1 deletions

View file

@ -100,4 +100,10 @@ export class ScalarService {
return {user_id: userId};
}
@GET
@Path("ping")
public async ping(): Promise<any> {
return {}; // 200 OK
}
}

View file

@ -110,6 +110,7 @@ import { AdminSlackBridgeManageSelfhostedComponent } from "./admin/bridges/slack
import { AdminSlackBridgeComponent } from "./admin/bridges/slack/slack.component";
import { AdminSlackApiService } from "./shared/services/admin/admin-slack-api.service";
import { ReauthExampleWidgetWrapperComponent } from "./widget-wrappers/reauth-example/reauth-example.component";
import { ManagerTestWidgetWrapperComponent } from "./widget-wrappers/manager-test/manager-test.component";
@NgModule({
imports: [
@ -200,6 +201,7 @@ import { ReauthExampleWidgetWrapperComponent } from "./widget-wrappers/reauth-ex
AdminSlackBridgeManageSelfhostedComponent,
AdminSlackBridgeComponent,
ReauthExampleWidgetWrapperComponent,
ManagerTestWidgetWrapperComponent,
// Vendor
],

View file

@ -43,6 +43,7 @@ import { AdminCustomBotsComponent } from "./admin/custom-bots/custom-bots.compon
import { AdminSlackBridgeComponent } from "./admin/bridges/slack/slack.component";
import { SlackBridgeConfigComponent } from "./configs/bridge/slack/slack.bridge.component";
import { ReauthExampleWidgetWrapperComponent } from "./widget-wrappers/reauth-example/reauth-example.component";
import { ManagerTestWidgetWrapperComponent } from "./widget-wrappers/manager-test/manager-test.component";
const routes: Routes = [
{path: "", component: HomeComponent},
@ -266,6 +267,7 @@ const routes: Routes = [
{path: "tradingview", component: TradingViewWidgetWrapperComponent},
{path: "spotify", component: SpotifyWidgetWrapperComponent},
{path: "reauth", component: ReauthExampleWidgetWrapperComponent},
{path: "manager-test", component: ManagerTestWidgetWrapperComponent},
]
},
];

View file

@ -13,6 +13,10 @@ export class ScalarServerApiService extends AuthedApi {
super(http)
}
public ping(): Promise<any> {
return this.http.get("/api/v1/scalar/ping").map(res => res.json()).toPromise();
}
public getAccount(): Promise<FE_ScalarAccountResponse> {
return this.authedGet("/api/v1/scalar/account").map(res => res.json()).toPromise();
}

View file

@ -2,16 +2,23 @@ import { OnDestroy, OnInit } from "@angular/core";
import { Subscription } from "rxjs/Subscription";
import { ScalarWidgetApi } from "../shared/services/scalar/scalar-widget.api";
import * as semver from "semver";
import { FE_ScalarOpenIdRequestBody } from "../shared/models/scalar-server-responses";
export const WIDGET_API_VERSION_BASIC = "0.0.1";
export const WIDGET_API_VERSION_OPENID = "0.0.2";
export const WIDGET_API_DIMENSION_VERSIONS = [WIDGET_API_VERSION_BASIC, WIDGET_API_VERSION_OPENID];
export interface OpenIdResponse {
openId: FE_ScalarOpenIdRequestBody;
blocked: boolean;
}
export abstract class CapableWidget implements OnInit, OnDestroy {
private requestSubscription: Subscription;
private responseSubscription: Subscription;
private openIdRequest: { resolve: (a: OpenIdResponse) => void, promise: Promise<OpenIdResponse> } = null;
// The capabilities we support
protected supportsScreenshots = false;
@ -34,12 +41,29 @@ export abstract class CapableWidget implements OnInit, OnDestroy {
this.onCapabilitiesSent();
} else if (request.action === "supported_api_versions") {
ScalarWidgetApi.replySupportedVersions(request, WIDGET_API_DIMENSION_VERSIONS);
} else if (request.action === "openid_credentials" && this.openIdRequest) {
if (request.data.success) {
this.openIdRequest.resolve({openId: request.data, blocked: false});
} else {
this.openIdRequest.resolve({openId: null, blocked: true});
}
this.openIdRequest = null;
}
});
this.responseSubscription = ScalarWidgetApi.replyReceived.subscribe(request => {
if (request.action === "supported_api_versions" && request.response) {
if (!request.response) return;
if (request.action === "supported_api_versions") {
this.clientWidgetApiVersions = request.response.supported_versions || [];
this.onSupportedVersionsFound();
} else if (request.action === "get_openid" && this.openIdRequest) {
if (request.response.state === "allowed") {
this.openIdRequest.resolve({openId: request.response, blocked: false});
this.openIdRequest = null;
} else if (request.response.state === "blocked") {
this.openIdRequest.resolve({openId: null, blocked: true});
this.openIdRequest = null;
}
}
});
}
@ -65,4 +89,13 @@ export abstract class CapableWidget implements OnInit, OnDestroy {
}
return false;
}
protected getOpenIdInfo(): Promise<OpenIdResponse> {
if (this.openIdRequest) return this.openIdRequest.promise;
const promise = new Promise<OpenIdResponse>(((resolve, _reject) => {
this.openIdRequest = {resolve: resolve, promise};
ScalarWidgetApi.requestOpenID();
}));
return promise;
}
}

View file

@ -0,0 +1,33 @@
<div class="wrapper">
<div class="boat">
<div class="prompt">
<div class="diagram">
<div class="circle {{selfState}}">
<i class="fa fa-user fa-2x"></i>
<span class="title">You</span>
</div>
<div class="link {{managerState}}"></div>
<div class="circle {{managerState}}">
<i class="fa fa-cubes fa-2x"></i>
<span class="title">Integrations</span>
</div>
<div class="link {{homeserverState}}"></div>
<div class="circle {{homeserverState}}">
<i class="fa fa-server fa-2x"></i>
<span class="title">Homeserver</span>
</div>
</div>
<p *ngIf="!isSupported">
Your client is too old to use this widget. Try upgrading your client to the latest available version,
or contact the author to try and diagnose the problem. Your client needs to support OpenID information
exchange.
</p>
<div *ngIf="isSupported">
<p>{{message}}</p>
<button type="button" (click)="start()" class="btn btn-primary btn-large" [disabled]="isBusy">
Check connection
</button>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,66 @@
// component styles are encapsulated and only applied to their components
@import "../../../style/themes/themes";
@include themifyComponent() {
.wrapper {
display: table;
position: absolute;
height: 100%;
width: 100%;
background-color: themed(troubleshooterBgColor);
}
.boat {
display: table-cell;
vertical-align: middle;
}
.prompt {
margin-left: auto;
margin-right: auto;
width: 90%;
text-align: center;
}
p {
font-size: 0.9em;
}
.diagram {
margin: 20px;
div {
display: inline-block;
}
.circle {
width: 120px;
height: 120px;
border: 5px solid themed(troubleshooterNeutralColor);
border-radius: 120px;
vertical-align: middle;
padding-top: 22px;
transition: border-color 0.2s;
.title {
display: block;
}
}
.link {
width: 120px;
height: 1px;
border: 2px solid themed(troubleshooterNeutralColor);
margin: 5px;
transition: border-color 0.2s;
}
.circle.error, .link.error {
border-color: themed(troubleshooterErrorColor);
}
.circle.ok, .link.ok {
border-color: themed(troubleshooterOkColor);
}
}
}

View file

@ -0,0 +1,90 @@
import { ChangeDetectorRef, Component, OnDestroy, OnInit } from "@angular/core";
import { ScalarWidgetApi } from "../../shared/services/scalar/scalar-widget.api";
import { CapableWidget, WIDGET_API_VERSION_OPENID } from "../capable-widget";
import { ActivatedRoute } from "@angular/router";
import { ScalarServerApiService } from "../../shared/services/scalar/scalar-server-api.service";
@Component({
selector: "my-reauth-example-widget-wrapper",
templateUrl: "manager-test.component.html",
styleUrls: ["manager-test.component.scss"],
})
export class ManagerTestWidgetWrapperComponent extends CapableWidget implements OnInit, OnDestroy {
public readonly STATE_NEUTRAL = 'neutral';
public readonly STATE_OK = 'ok';
public readonly STATE_ERROR = 'error';
public isBusy = true;
public isSupported = true;
public selfState = this.STATE_NEUTRAL;
public managerState = this.STATE_NEUTRAL;
public homeserverState = this.STATE_NEUTRAL;
public message = "Click the button to test your connection. This may cause your client to ask if it " +
"is okay to share your identity with the widget - this is required to test your connection to your " +
"homeserver.";
constructor(activatedRoute: ActivatedRoute,
private scalarApi: ScalarServerApiService,
private changeDetector: ChangeDetectorRef) {
super();
const params: any = activatedRoute.snapshot.queryParams;
ScalarWidgetApi.widgetId = params.widgetId;
}
protected onSupportedVersionsFound(): void {
super.onSupportedVersionsFound();
this.isSupported = this.doesSupportAtLeastVersion(WIDGET_API_VERSION_OPENID);
this.isBusy = false;
if (!this.isSupported) {
this.selfState = this.STATE_ERROR;
}
this.changeDetector.detectChanges();
}
public async start(): Promise<any> {
this.selfState = this.STATE_NEUTRAL;
this.managerState = this.STATE_NEUTRAL;
this.homeserverState = this.STATE_NEUTRAL;
this.message = "Please accept the prompt to verify your identity.";
this.isBusy = true;
const response = await this.getOpenIdInfo();
if (response.blocked) {
this.isBusy = false;
this.selfState = this.STATE_ERROR;
this.message = "You have blocked this widget from verifying your identity.";
return;
}
this.selfState = this.STATE_OK;
this.message = "Checking connectivity to integration manager...";
try {
await this.scalarApi.ping();
this.managerState = this.STATE_OK;
this.message = "Checking connectivity to homeserver...";
} catch (e) {
console.error(e);
this.isBusy = false;
this.managerState = this.STATE_ERROR;
this.message = "Error checking if the integration manager is alive. This usually means that the manager " +
"which served this widget has gone offline.";
return;
}
try {
await this.scalarApi.register(response.openId);
this.homeserverState = this.STATE_OK;
this.message = "You're all set! Click the button below to re-run the test.";
this.isBusy = false;
} catch (e) {
this.isBusy = false;
this.homeserverState = this.STATE_ERROR;
this.message = "Error contacting homeserver. This usually means your federation setup is incorrect, or " +
"your homeserver is offline. Consult your homeserver's documentation for how to set up federation.";
}
}
}

View file

@ -50,6 +50,11 @@ $theme_dark: (
jitsiWelcomeBgColor: #fff,
troubleshooterBgColor: #2d2d2d,
troubleshooterNeutralColor: rgb(205, 215, 222),
troubleshooterOkColor: #59bb59,
troubleshooterErrorColor: #e84f4f,
genericControlBgColor: #eee,
genericControlFgColor: #222,
widgetBannedSymbolColor: #bd362f,

View file

@ -50,6 +50,11 @@ $theme_light: (
jitsiWelcomeBgColor: #fff,
troubleshooterBgColor: #fff,
troubleshooterNeutralColor: rgb(205, 215, 222),
troubleshooterOkColor: #59bb59,
troubleshooterErrorColor: #e84f4f,
genericControlBgColor: #eee,
genericControlFgColor: #222,
widgetBannedSymbolColor: #bd362f,