add call to validate database endpoint if database requires it
This commit is contained in:
@@ -46,7 +46,7 @@ paths:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/InstallValidateDatabaseRequest'
|
||||
$ref: '#/components/schemas/InstallSetupDatabaseConfiguration'
|
||||
required: true
|
||||
responses:
|
||||
'201':
|
||||
@@ -341,13 +341,6 @@ components:
|
||||
required:
|
||||
- user
|
||||
- telemetryLevel
|
||||
InstallValidateDatabaseRequest:
|
||||
type: object
|
||||
properties:
|
||||
database:
|
||||
$ref: '#/components/schemas/InstallSetupDatabaseConfiguration'
|
||||
required:
|
||||
- database
|
||||
ServerStatus:
|
||||
type: string
|
||||
enum:
|
||||
|
||||
@@ -102,9 +102,6 @@ export interface components {
|
||||
telemetryLevel: components["schemas"]["ConsentLevel"];
|
||||
database?: components["schemas"]["InstallSetupDatabaseConfiguration"];
|
||||
};
|
||||
InstallValidateDatabaseRequest: {
|
||||
database: components["schemas"]["InstallSetupDatabaseConfiguration"];
|
||||
};
|
||||
/** @enum {string} */
|
||||
ServerStatus: "running" | "must-install" | "must-upgrade";
|
||||
StatusResponse: {
|
||||
@@ -184,7 +181,7 @@ export interface operations {
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["InstallValidateDatabaseRequest"];
|
||||
"application/json": components["schemas"]["InstallSetupDatabaseConfiguration"];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { UUIButtonElement } from '@umbraco-ui/uui';
|
||||
import { css, CSSResultGroup, html, LitElement } from 'lit';
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from 'lit';
|
||||
import { customElement, property, query, state } from 'lit/decorators.js';
|
||||
import { Subscription } from 'rxjs';
|
||||
|
||||
import { postInstallSetup, postInstallValidateDatabase } from '../core/api/fetcher';
|
||||
import { UmbContextConsumerMixin } from '../core/context';
|
||||
import { UmbracoInstallerDatabaseModel, UmbracoPerformInstallDatabaseConfiguration } from '../core/models';
|
||||
import { UmbInstallerContext } from './installer-context';
|
||||
@@ -69,6 +70,11 @@ export class UmbInstallerDatabase extends UmbContextConsumerMixin(LitElement) {
|
||||
margin-left: auto;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: var(--uui-color-danger);
|
||||
padding: var(--uui-size-space-2) 0;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@@ -90,6 +96,9 @@ export class UmbInstallerDatabase extends UmbContextConsumerMixin(LitElement) {
|
||||
@state()
|
||||
private _installerStore?: UmbInstallerContext;
|
||||
|
||||
@state()
|
||||
private _validationErrorMessage = '';
|
||||
|
||||
private storeDataSubscription?: Subscription;
|
||||
private storeSettingsSubscription?: Subscription;
|
||||
|
||||
@@ -136,15 +145,19 @@ export class UmbInstallerDatabase extends UmbContextConsumerMixin(LitElement) {
|
||||
this._installerStore?.appendData({ database });
|
||||
}
|
||||
|
||||
private _handleSubmit = (e: SubmitEvent) => {
|
||||
e.preventDefault();
|
||||
private _handleSubmit = async (evt: SubmitEvent) => {
|
||||
evt.preventDefault();
|
||||
|
||||
const form = e.target as HTMLFormElement;
|
||||
const form = evt.target as HTMLFormElement;
|
||||
if (!form) return;
|
||||
|
||||
const isValid = form.checkValidity();
|
||||
if (!isValid) return;
|
||||
|
||||
if (!this._installerStore) return;
|
||||
|
||||
this._installButton.state = 'waiting';
|
||||
|
||||
// Only append the database if it's not pre-configured
|
||||
if (!this._preConfiguredDatabase) {
|
||||
const formData = new FormData(form);
|
||||
@@ -154,6 +167,39 @@ export class UmbInstallerDatabase extends UmbContextConsumerMixin(LitElement) {
|
||||
const server = formData.get('server') as string;
|
||||
const name = formData.get('name') as string;
|
||||
const useIntegratedAuthentication = formData.has('useIntegratedAuthentication');
|
||||
const connectionString = formData.get('connectionString') as string;
|
||||
|
||||
// Validate connection
|
||||
const selectedDatabase = this._databases.find((x) => x.id === id);
|
||||
if (selectedDatabase?.requiresConnectionTest) {
|
||||
try {
|
||||
let databaseDetails: UmbracoPerformInstallDatabaseConfiguration = {};
|
||||
|
||||
if (connectionString) {
|
||||
databaseDetails.connectionString = connectionString;
|
||||
} else {
|
||||
databaseDetails = {
|
||||
id,
|
||||
username,
|
||||
password,
|
||||
server,
|
||||
useIntegratedAuthentication,
|
||||
name,
|
||||
};
|
||||
}
|
||||
await postInstallValidateDatabase(databaseDetails);
|
||||
} catch (e) {
|
||||
if (e instanceof postInstallSetup.Error) {
|
||||
const error = e.getActualType();
|
||||
console.warn('Database validation failed', error.data);
|
||||
this._validationErrorMessage = error.data.detail ?? 'Could not verify database connection';
|
||||
} else {
|
||||
this._validationErrorMessage = 'A server error happened when trying to validate the database';
|
||||
}
|
||||
this._installButton.state = 'failed';
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const database = {
|
||||
...this._installerStore?.getData().database,
|
||||
@@ -163,14 +209,13 @@ export class UmbInstallerDatabase extends UmbContextConsumerMixin(LitElement) {
|
||||
server,
|
||||
name,
|
||||
useIntegratedAuthentication,
|
||||
connectionString,
|
||||
} as UmbracoPerformInstallDatabaseConfiguration;
|
||||
|
||||
this._installerStore?.appendData({ database });
|
||||
}
|
||||
|
||||
this.dispatchEvent(new CustomEvent('submit', { bubbles: true, composed: true }));
|
||||
|
||||
this._installButton.state = 'waiting';
|
||||
};
|
||||
|
||||
private _onBack() {
|
||||
@@ -321,6 +366,7 @@ export class UmbInstallerDatabase extends UmbContextConsumerMixin(LitElement) {
|
||||
${this._preConfiguredDatabase
|
||||
? this._renderPreConfiguredDatabase(this._preConfiguredDatabase)
|
||||
: this._renderDatabaseSelection()}
|
||||
${this._validationErrorMessage ? html` <div class="error">${this._validationErrorMessage}</div> ` : nothing}
|
||||
|
||||
<div id="buttons">
|
||||
<uui-button label="Back" @click=${this._onBack} look="secondary"></uui-button>
|
||||
|
||||
@@ -48,7 +48,7 @@ export const Step3Database: Story = () => html`<umb-installer-database></umb-ins
|
||||
Step3Database.storyName = 'Step 3: Database';
|
||||
Step3Database.parameters = {
|
||||
actions: {
|
||||
handles: ['previous', 'next'],
|
||||
handles: ['previous', 'submit'],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -56,7 +56,7 @@ export const Step3DatabasePreconfigured: Story = () => html`<umb-installer-datab
|
||||
Step3DatabasePreconfigured.storyName = 'Step 3: Database (preconfigured)';
|
||||
Step3DatabasePreconfigured.parameters = {
|
||||
actions: {
|
||||
handles: ['previous', 'next'],
|
||||
handles: ['previous', 'submit'],
|
||||
},
|
||||
msw: {
|
||||
handlers: {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { rest } from 'msw';
|
||||
|
||||
import umbracoPath from '../../core/helpers/umbraco-path';
|
||||
import { PostInstallRequest, ProblemDetails, UmbracoInstaller } from '../../core/models';
|
||||
import {
|
||||
PostInstallRequest,
|
||||
ProblemDetails,
|
||||
UmbracoInstaller,
|
||||
UmbracoPerformInstallDatabaseConfiguration,
|
||||
} from '../../core/models';
|
||||
|
||||
export const handlers = [
|
||||
rest.get(umbracoPath('/install/settings'), (_req, res, ctx) => {
|
||||
@@ -73,6 +78,26 @@ export const handlers = [
|
||||
);
|
||||
}),
|
||||
|
||||
rest.post(umbracoPath('/install/validateDatabase'), async (req, res, ctx) => {
|
||||
const body = await req.json<UmbracoPerformInstallDatabaseConfiguration>();
|
||||
|
||||
if (body.name === 'validate') {
|
||||
return res(
|
||||
ctx.status(400),
|
||||
ctx.json<ProblemDetails>({
|
||||
type: 'connection',
|
||||
status: 400,
|
||||
detail: 'Database connection failed',
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return res(
|
||||
// Respond with a 200 status code
|
||||
ctx.status(201)
|
||||
);
|
||||
}),
|
||||
|
||||
rest.post(umbracoPath('/install/setup'), async (req, res, ctx) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, (Math.random() + 1) * 1000)); // simulate a delay of 1-2 seconds
|
||||
const body = await req.json<PostInstallRequest>();
|
||||
|
||||
@@ -3,103 +3,99 @@ import { body, defaultResponse, endpoint, request, response } from '@airtasker/s
|
||||
import { ProblemDetails } from './models';
|
||||
|
||||
@endpoint({
|
||||
method: 'GET',
|
||||
path: '/install/settings',
|
||||
method: 'GET',
|
||||
path: '/install/settings',
|
||||
})
|
||||
export class GetInstallSettings {
|
||||
@response({ status: 200 })
|
||||
success(@body body: InstallSettingsResponse) {}
|
||||
@response({ status: 200 })
|
||||
success(@body body: InstallSettingsResponse) {}
|
||||
|
||||
@defaultResponse
|
||||
default(@body body: ProblemDetails) {}
|
||||
@defaultResponse
|
||||
default(@body body: ProblemDetails) {}
|
||||
}
|
||||
|
||||
@endpoint({
|
||||
method: 'POST',
|
||||
path: '/install/setup',
|
||||
method: 'POST',
|
||||
path: '/install/setup',
|
||||
})
|
||||
export class PostInstallSetup {
|
||||
@request
|
||||
request(@body body: InstallSetupRequest) {}
|
||||
@request
|
||||
request(@body body: InstallSetupRequest) {}
|
||||
|
||||
@response({ status: 201 })
|
||||
success() {}
|
||||
@response({ status: 201 })
|
||||
success() {}
|
||||
|
||||
@response({ status: 400 })
|
||||
badRequest(@body body: ProblemDetails) {}
|
||||
@response({ status: 400 })
|
||||
badRequest(@body body: ProblemDetails) {}
|
||||
}
|
||||
|
||||
@endpoint({
|
||||
method: 'POST',
|
||||
path: '/install/validateDatabase',
|
||||
method: 'POST',
|
||||
path: '/install/validateDatabase',
|
||||
})
|
||||
export class PostInstallValidateDatabase {
|
||||
@request
|
||||
request(@body body: InstallValidateDatabaseRequest) {}
|
||||
@request
|
||||
request(@body body: InstallSetupDatabaseConfiguration) {}
|
||||
|
||||
@response({ status: 201 })
|
||||
success() {}
|
||||
@response({ status: 201 })
|
||||
success() {}
|
||||
|
||||
@response({ status: 400 })
|
||||
badRequest(@body body: ProblemDetails) {}
|
||||
@response({ status: 400 })
|
||||
badRequest(@body body: ProblemDetails) {}
|
||||
}
|
||||
|
||||
export interface InstallSetupRequest {
|
||||
user: InstallSetupUserConfiguration;
|
||||
telemetryLevel: ConsentLevel;
|
||||
database?: InstallSetupDatabaseConfiguration;
|
||||
}
|
||||
|
||||
export interface InstallValidateDatabaseRequest {
|
||||
database: InstallSetupDatabaseConfiguration;
|
||||
user: InstallSetupUserConfiguration;
|
||||
telemetryLevel: ConsentLevel;
|
||||
database?: InstallSetupDatabaseConfiguration;
|
||||
}
|
||||
|
||||
export interface InstallSettingsResponse {
|
||||
user: InstallUserModel;
|
||||
databases: InstallDatabaseModel[];
|
||||
user: InstallUserModel;
|
||||
databases: InstallDatabaseModel[];
|
||||
}
|
||||
|
||||
export interface InstallUserModel {
|
||||
minCharLength: number;
|
||||
minNonAlphaNumericLength: number;
|
||||
consentLevels: TelemetryModel[];
|
||||
minCharLength: number;
|
||||
minNonAlphaNumericLength: number;
|
||||
consentLevels: TelemetryModel[];
|
||||
}
|
||||
|
||||
export interface InstallSetupUserConfiguration {
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
subscribeToNewsletter: boolean;
|
||||
name: string;
|
||||
email: string;
|
||||
password: string;
|
||||
subscribeToNewsletter: boolean;
|
||||
}
|
||||
|
||||
export interface InstallSetupDatabaseConfiguration {
|
||||
id?: string;
|
||||
server?: string | null;
|
||||
password?: string | null;
|
||||
username?: string | null;
|
||||
name?: string | null;
|
||||
providerName?: string | null;
|
||||
useIntegratedAuthentication?: boolean | null;
|
||||
connectionString?: string | null;
|
||||
id?: string;
|
||||
server?: string | null;
|
||||
password?: string | null;
|
||||
username?: string | null;
|
||||
name?: string | null;
|
||||
providerName?: string | null;
|
||||
useIntegratedAuthentication?: boolean | null;
|
||||
connectionString?: string | null;
|
||||
}
|
||||
|
||||
export interface TelemetryModel {
|
||||
level: ConsentLevel;
|
||||
description: string;
|
||||
level: ConsentLevel;
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface InstallDatabaseModel {
|
||||
id: string;
|
||||
sortOrder: number;
|
||||
displayName: string;
|
||||
defaultDatabaseName: string;
|
||||
providerName: null | string;
|
||||
isConfigured: boolean;
|
||||
requiresServer: boolean;
|
||||
serverPlaceholder: null | string;
|
||||
requiresCredentials: boolean;
|
||||
supportsIntegratedAuthentication: boolean;
|
||||
requiresConnectionTest: boolean;
|
||||
id: string;
|
||||
sortOrder: number;
|
||||
displayName: string;
|
||||
defaultDatabaseName: string;
|
||||
providerName: null | string;
|
||||
isConfigured: boolean;
|
||||
requiresServer: boolean;
|
||||
serverPlaceholder: null | string;
|
||||
requiresCredentials: boolean;
|
||||
supportsIntegratedAuthentication: boolean;
|
||||
requiresConnectionTest: boolean;
|
||||
}
|
||||
|
||||
export type ConsentLevel = 'Minimal' | 'Basic' | 'Detailed';
|
||||
|
||||
Reference in New Issue
Block a user