Merge branch 'main' into feature/published-dashboard
This commit is contained in:
@@ -347,6 +347,58 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
/telemetry/ConsentLevel:
|
||||
get:
|
||||
operationId: GetConsentLevel
|
||||
responses:
|
||||
'200':
|
||||
description: 200 response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ConsentLevelSettings'
|
||||
default:
|
||||
description: default response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
post:
|
||||
operationId: PostConsentLevel
|
||||
parameters: []
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ConsentLevelSettings'
|
||||
required: true
|
||||
responses:
|
||||
'201':
|
||||
description: 201 response
|
||||
'400':
|
||||
description: 400 response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
/telemetry/ConsentLevels:
|
||||
get:
|
||||
operationId: ConsentLevels
|
||||
responses:
|
||||
'200':
|
||||
description: 200 response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
default:
|
||||
description: default response
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/ProblemDetails'
|
||||
components:
|
||||
schemas:
|
||||
ConsentLevel:
|
||||
@@ -1135,3 +1187,10 @@ components:
|
||||
type: string
|
||||
required:
|
||||
- sections
|
||||
ConsentLevelSettings:
|
||||
type: object
|
||||
properties:
|
||||
telemetryLevel:
|
||||
$ref: '#/components/schemas/ConsentLevel'
|
||||
required:
|
||||
- telemetryLevel
|
||||
|
||||
@@ -67,6 +67,13 @@ export interface paths {
|
||||
"/user/sections": {
|
||||
get: operations["GetAllowedSections"];
|
||||
};
|
||||
"/telemetry/ConsentLevel": {
|
||||
get: operations["GetConsentLevel"];
|
||||
post: operations["PostConsentLevel"];
|
||||
};
|
||||
"/telemetry/ConsentLevels": {
|
||||
get: operations["ConsentLevels"];
|
||||
};
|
||||
}
|
||||
|
||||
export interface components {
|
||||
@@ -380,6 +387,9 @@ export interface components {
|
||||
AllowedSectionsResponse: {
|
||||
sections: string[];
|
||||
};
|
||||
ConsentLevelSettings: {
|
||||
telemetryLevel: components["schemas"]["ConsentLevel"];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -723,6 +733,56 @@ export interface operations {
|
||||
};
|
||||
};
|
||||
};
|
||||
GetConsentLevel: {
|
||||
responses: {
|
||||
/** 200 response */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["ConsentLevelSettings"];
|
||||
};
|
||||
};
|
||||
/** default response */
|
||||
default: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["ProblemDetails"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
PostConsentLevel: {
|
||||
parameters: {};
|
||||
responses: {
|
||||
/** 201 response */
|
||||
201: unknown;
|
||||
/** 400 response */
|
||||
400: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["ProblemDetails"];
|
||||
};
|
||||
};
|
||||
};
|
||||
requestBody: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["ConsentLevelSettings"];
|
||||
};
|
||||
};
|
||||
};
|
||||
ConsentLevels: {
|
||||
responses: {
|
||||
/** 200 response */
|
||||
200: {
|
||||
content: {
|
||||
"application/json": string[];
|
||||
};
|
||||
};
|
||||
/** default response */
|
||||
default: {
|
||||
content: {
|
||||
"application/json": components["schemas"]["ProblemDetails"];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface external {}
|
||||
|
||||
@@ -1,15 +1,150 @@
|
||||
import { UUITextStyles } from '@umbraco-ui/uui-css/lib';
|
||||
import { css, html, LitElement } from 'lit';
|
||||
import { customElement } from 'lit/decorators.js';
|
||||
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
||||
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
|
||||
import type { TelemetryModel } from '../../../core/models';
|
||||
import { getConsentLevel, getConsentLevels, postConsentLevel } from '../../../core/api/fetcher';
|
||||
|
||||
export type SettingOption = 'Minimal' | 'Basic' | 'Detailed';
|
||||
|
||||
@customElement('umb-dashboard-telemetry')
|
||||
export class UmbDashboardTelemetryElement extends LitElement {
|
||||
static styles = [UUITextStyles, css``];
|
||||
static styles = [
|
||||
UUITextStyles,
|
||||
css`
|
||||
.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
||||
@state()
|
||||
private _telemetryFormData: TelemetryModel['level'] = 'Basic';
|
||||
|
||||
@state()
|
||||
private _telemetryLevels: TelemetryModel['level'][] = [];
|
||||
|
||||
@state()
|
||||
private _errorMessage = '';
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
this._setup();
|
||||
}
|
||||
|
||||
private async _setup() {
|
||||
try {
|
||||
const consentLevels = await getConsentLevels({});
|
||||
this._telemetryLevels = consentLevels.data as TelemetryModel['level'][];
|
||||
} catch (e) {
|
||||
this._errorMessage;
|
||||
}
|
||||
try {
|
||||
const consentSetting = await getConsentLevel({});
|
||||
this._telemetryFormData = consentSetting.data.telemetryLevel as TelemetryModel['level'];
|
||||
} catch (e) {
|
||||
if (e instanceof getConsentLevel.Error) {
|
||||
this._errorMessage = e.data.detail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _handleSubmit = async (e: CustomEvent<SubmitEvent>) => {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
await postConsentLevel({ telemetryLevel: this._telemetryFormData });
|
||||
} catch (e) {
|
||||
if (e instanceof postConsentLevel.Error) {
|
||||
const error = e.getActualType();
|
||||
if (error.status === 400) {
|
||||
this._errorMessage = error.data.detail || 'Unknown error, please try again';
|
||||
}
|
||||
} else {
|
||||
this._errorMessage = 'Unknown error, please try again';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
}
|
||||
|
||||
private _handleChange(e: InputEvent) {
|
||||
const target = e.target as HTMLInputElement;
|
||||
this._telemetryFormData = this._telemetryLevels[parseInt(target.value) - 1];
|
||||
}
|
||||
|
||||
private get _selectedTelemetryIndex() {
|
||||
return this._telemetryLevels?.findIndex((x) => x === this._telemetryFormData) ?? 0;
|
||||
}
|
||||
|
||||
private get _selectedTelemetry() {
|
||||
return this._telemetryLevels?.find((x) => x === this._telemetryFormData) ?? this._telemetryLevels[0];
|
||||
}
|
||||
|
||||
private get _selectedTelemetryDescription() {
|
||||
switch (this._selectedTelemetry) {
|
||||
case 'Minimal':
|
||||
return 'We will only send an anonymized site ID to let us know that the site exists.';
|
||||
case 'Basic':
|
||||
return 'We will send an anonymized site ID, Umbraco version, and packages installed.';
|
||||
case 'Detailed':
|
||||
return `We will send:<ul>
|
||||
<li>Anonymized site ID, Umbraco version, and packages installed.</li>
|
||||
<li>Number of: Root nodes, Content nodes, Macros, Media, Document Types, Templates, Languages, Domains, User Group, Users, Members, and Property Editors in use.</li>
|
||||
<li>System information: Webserver, server OS, server framework, server OS language, and database provider.</li>
|
||||
<li>Configuration settings: Modelsbuilder mode, if custom Umbraco path exists, ASP environment, and if you are in debug mode.</li>
|
||||
</ul>
|
||||
|
||||
<i>We might change what we send on the Detailed level in the future. If so, it will be listed above.
|
||||
By choosing "Detailed" you agree to current and future anonymized information being collected.</i>`;
|
||||
default:
|
||||
return 'Could not find description for this setting';
|
||||
}
|
||||
}
|
||||
|
||||
private _renderSettingSlider() {
|
||||
if (!this._telemetryLevels || this._telemetryLevels.length < 1) return;
|
||||
|
||||
return html`
|
||||
<uui-slider
|
||||
@input=${this._handleChange}
|
||||
name="telemetryLevel"
|
||||
label="telemetry level"
|
||||
value=${this._selectedTelemetryIndex + 1}
|
||||
min="1"
|
||||
max=${this._telemetryLevels.length}
|
||||
hide-step-values></uui-slider>
|
||||
<h2>${this._selectedTelemetry}</h2>
|
||||
<p>${unsafeHTML(this._selectedTelemetryDescription)}</p>
|
||||
`;
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<uui-box>
|
||||
<h1>Telemetry</h1>
|
||||
<h1>Consent for telemetry data</h1>
|
||||
<div style="max-width:580px">
|
||||
<p>
|
||||
In order to improve Umbraco and add new functionality based on as relevant information as possible, we would
|
||||
like to collect system- and usage information from your installation. Aggregate data will be shared on a
|
||||
regular basis as well as learnings from these metrics. Hopefully, you will help us collect some valuable
|
||||
data.
|
||||
<br /><br />
|
||||
We <strong>WILL NOT</strong> collect any personal data such as content, code, user information, and all data
|
||||
will be fully anonymized.
|
||||
</p>
|
||||
${this._renderSettingSlider()}
|
||||
<uui-button look="primary" color="positive" label="Save telemetry settings" @click="${this._handleSubmit}">
|
||||
Save
|
||||
</uui-button>
|
||||
</div>
|
||||
</uui-box>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { expect, fixture, html } from '@open-wc/testing';
|
||||
import { defaultA11yConfig } from '../../../core/helpers/chai';
|
||||
import { UmbDashboardTelemetryElement } from './dashboard-telemetry.element';
|
||||
|
||||
describe('UmbDashboardTelemetryElement', () => {
|
||||
let element: UmbDashboardTelemetryElement;
|
||||
|
||||
beforeEach(async () => {
|
||||
element = await fixture(html`<umb-dashboard-telemetry></umb-dashboard-telemetry>`);
|
||||
});
|
||||
|
||||
it('is defined with its own instance', () => {
|
||||
expect(element).to.be.instanceOf(UmbDashboardTelemetryElement);
|
||||
});
|
||||
|
||||
it('passes the a11y audit', async () => {
|
||||
await expect(element).to.be.accessible(defaultA11yConfig);
|
||||
});
|
||||
});
|
||||
@@ -21,6 +21,10 @@ export const getUpgradeSettings = fetcher.path('/upgrade/settings').method('get'
|
||||
export const PostUpgradeAuthorize = fetcher.path('/upgrade/authorize').method('post').create();
|
||||
export const getManifests = fetcher.path('/manifests').method('get').create();
|
||||
export const getPackagesInstalled = fetcher.path('/manifests/packages/installed').method('get').create();
|
||||
export const getConsentLevels = fetcher.path('/telemetry/ConsentLevels').method('get').create();
|
||||
export const getConsentLevel = fetcher.path('/telemetry/ConsentLevel').method('get').create();
|
||||
export const postConsentLevel = fetcher.path('/telemetry/ConsentLevel').method('post').create();
|
||||
|
||||
|
||||
// Property Editors
|
||||
export const getPropertyEditorsList = fetcher.path('/property-editors/list').method('get').create();
|
||||
@@ -36,3 +40,4 @@ export const getPublishedCacheStatus = fetcher.path('/published-cache/status').m
|
||||
export const postPublishedCacheReload = fetcher.path('/published-cache/reload').method('post').create();
|
||||
export const postPublishedCacheRebuild = fetcher.path('/published-cache/rebuild').method('post').create();
|
||||
export const getPublishedCacheCollect = fetcher.path('/published-cache/collect').method('get').create();
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ export type ManifestEntrypoint = components['schemas']['IManifestEntrypoint'];
|
||||
export type ManifestCustom = components['schemas']['IManifestCustom'];
|
||||
export type ManifestPackageView = components['schemas']['IManifestPackageView'];
|
||||
export type PackageInstalled = components['schemas']['PackageInstalled'];
|
||||
export type ConsentLevelSettings = components['schemas']['ConsentLevelSettings'];
|
||||
|
||||
// Property Editors
|
||||
export type PropertyEditorsListResponse = components['schemas']['PropertyEditorsListResponse'];
|
||||
|
||||
@@ -8,6 +8,7 @@ import { handlers as publishedStatusHandlers } from './domains/published-status.
|
||||
import * as serverHandlers from './domains/server.handlers';
|
||||
import { handlers as upgradeHandlers } from './domains/upgrade.handlers';
|
||||
import { handlers as userHandlers } from './domains/user.handlers';
|
||||
import { handlers as telemetryHandlers } from './domains/telemetry.handlers';
|
||||
import { handlers as propertyEditorHandlers } from './domains/property-editor.handlers';
|
||||
|
||||
const handlers = [
|
||||
@@ -21,6 +22,7 @@ const handlers = [
|
||||
...treeHandlers,
|
||||
...propertyEditorHandlers,
|
||||
...manifestsHandlers.default,
|
||||
...telemetryHandlers,
|
||||
...publishedStatusHandlers,
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { rest } from 'msw';
|
||||
|
||||
import umbracoPath from '../../core/helpers/umbraco-path';
|
||||
import type { ConsentLevelSettings, TelemetryModel } from '../../core/models';
|
||||
|
||||
let telemetryLevel: TelemetryModel['level'] = 'Basic';
|
||||
|
||||
export const handlers = [
|
||||
rest.get(umbracoPath('/telemetry/ConsentLevel'), (_req, res, ctx) => {
|
||||
return res(
|
||||
// Respond with a 200 status code
|
||||
ctx.status(200),
|
||||
ctx.json<ConsentLevelSettings>({
|
||||
telemetryLevel,
|
||||
})
|
||||
);
|
||||
}),
|
||||
rest.get(umbracoPath('/telemetry/ConsentLevels'), (_req, res, ctx) => {
|
||||
return res(
|
||||
// Respond with a 200 status code
|
||||
ctx.status(200),
|
||||
ctx.json<TelemetryModel['level'][]>(['Minimal', 'Basic', 'Detailed'])
|
||||
);
|
||||
}),
|
||||
|
||||
rest.post<ConsentLevelSettings>(umbracoPath('/telemetry/ConsentLevel'), async (_req, res, ctx) => {
|
||||
telemetryLevel = (await _req.json<ConsentLevelSettings>()).telemetryLevel;
|
||||
return res(
|
||||
// Respond with a 200 status code
|
||||
ctx.status(201)
|
||||
);
|
||||
}),
|
||||
];
|
||||
@@ -7,6 +7,7 @@ import { handlers as publishedStatusHandlers } from './domains/published-status.
|
||||
import * as serverHandlers from './domains/server.handlers';
|
||||
import { handlers as upgradeHandlers } from './domains/upgrade.handlers';
|
||||
import { handlers as userHandlers } from './domains/user.handlers';
|
||||
import { handlers as telemetryHandlers } from './domains/telemetry.handlers';
|
||||
import { handlers as treeHandlers } from './domains/entity.handlers';
|
||||
import { handlers as propertyEditorHandlers } from './domains/property-editor.handlers';
|
||||
|
||||
@@ -21,6 +22,7 @@ export const handlers = [
|
||||
...dataTypeHandlers,
|
||||
...documentTypeHandlers,
|
||||
...manifestsHandlers.default,
|
||||
...telemetryHandlers,
|
||||
...publishedStatusHandlers,
|
||||
...treeHandlers,
|
||||
...propertyEditorHandlers,
|
||||
|
||||
@@ -100,6 +100,19 @@ export const internalManifests: Array<ManifestTypes & { loader: () => Promise<ob
|
||||
weight: 10,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'dashboard',
|
||||
alias: 'Umb.Dashboard.Telemetry',
|
||||
name: 'Telemetry',
|
||||
elementName: 'umb-dashboard-telemetry',
|
||||
loader: () => import('./backoffice/dashboards/telemetry/dashboard-telemetry.element'),
|
||||
meta: {
|
||||
label: 'Telemetry Data',
|
||||
sections: ['Umb.Section.Settings'],
|
||||
pathname: 'telemetry', // TODO: how do we want to support pretty urls?
|
||||
weight: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'dashboard',
|
||||
alias: 'Umb.Dashboard.ExamineManagement',
|
||||
|
||||
@@ -4,6 +4,7 @@ import './publishedstatus';
|
||||
import './server';
|
||||
import './upgrader';
|
||||
import './user';
|
||||
import './telemetry';
|
||||
import './property-editors';
|
||||
|
||||
import { api } from '@airtasker/spot';
|
||||
|
||||
47
src/Umbraco.Web.UI.Client/temp-schema-generator/telemetry.ts
Normal file
47
src/Umbraco.Web.UI.Client/temp-schema-generator/telemetry.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { body, defaultResponse, endpoint, request, response } from '@airtasker/spot';
|
||||
import { TelemetryModel } from './installer';
|
||||
|
||||
import { ProblemDetails } from './models';
|
||||
|
||||
@endpoint({
|
||||
method: 'GET',
|
||||
path: '/telemetry/ConsentLevel',
|
||||
})
|
||||
export class GetConsentLevel {
|
||||
@response({ status: 200 })
|
||||
success(@body body: ConsentLevelSettings) {}
|
||||
|
||||
@defaultResponse
|
||||
default(@body body: ProblemDetails) {}
|
||||
}
|
||||
|
||||
@endpoint({
|
||||
method: 'GET',
|
||||
path: '/telemetry/ConsentLevels',
|
||||
})
|
||||
export class ConsentLevels {
|
||||
@response({ status: 200 })
|
||||
success(@body body: string[]) {}
|
||||
|
||||
@defaultResponse
|
||||
default(@body body: ProblemDetails) {}
|
||||
}
|
||||
|
||||
@endpoint({
|
||||
method: 'POST',
|
||||
path: '/telemetry/ConsentLevel',
|
||||
})
|
||||
export class PostConsentLevel {
|
||||
@request
|
||||
request(@body body: ConsentLevelSettings) {}
|
||||
|
||||
@response({ status: 201 })
|
||||
success() {}
|
||||
|
||||
@response({ status: 400 })
|
||||
badRequest(@body body: ProblemDetails) {}
|
||||
}
|
||||
|
||||
export interface ConsentLevelSettings {
|
||||
telemetryLevel: TelemetryModel['level'];
|
||||
}
|
||||
Reference in New Issue
Block a user