-
JSON Schema
-
+
UI Schema
-
+
Model Output
@@ -44,18 +83,45 @@
{{ errorMessage }}
-
+
-
-
-
-
+
+
+
+
-
+
-
-
diff --git a/src/app/pages/form-builder-page/form-builder-page.component.ts b/src/app/pages/form-builder-page/form-builder-page.component.ts
index 9b3e19474..86fb7a41e 100644
--- a/src/app/pages/form-builder-page/form-builder-page.component.ts
+++ b/src/app/pages/form-builder-page/form-builder-page.component.ts
@@ -1,34 +1,39 @@
-import {Component, ElementRef, OnDestroy, OnInit, ViewChild} from '@angular/core';
-import {UntypedFormGroup} from '@angular/forms';
-import {FormlyFieldConfig, FormlyFormOptions} from '@ngx-formly/core';
-import {KendraioFormService} from '../../_shared/ui-form/services/kendraio.form.service';
-import {FormSubmitHandlerService} from '../../services/form-submit-handler.service';
-import {ShareLinkGeneratorService} from '../../services/share-link-generator.service';
-import {Subject} from 'rxjs';
-import {debounceTime, takeUntil} from 'rxjs/operators';
-import JSONFormatter from 'json-formatter-js';
-import {JSON_SCHEMA} from './jsonschema';
-import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
-import {UI_SCHEMA} from './uischema';
-import {EDITOR_OPTIONS} from './editor-options';
-import {AdapterFormSelectService} from '../../services/adapter-form-select.service';
-import {FormDataService} from '../../services/form-data.service';
-import {clone, get, has} from 'lodash-es';
-import {PageTitleService} from '../../services/page-title.service';
+import {
+ Component,
+ ElementRef,
+ OnDestroy,
+ OnInit,
+ ViewChild,
+} from "@angular/core";
+import { UntypedFormGroup } from "@angular/forms";
+import { FormlyFieldConfig, FormlyFormOptions } from "@ngx-formly/core";
+import { KendraioFormService } from "../../_shared/ui-form/services/kendraio.form.service";
+import { FormSubmitHandlerService } from "../../services/form-submit-handler.service";
+import { ShareLinkGeneratorService } from "../../services/share-link-generator.service";
+import { Subject } from "rxjs";
+import { debounceTime, takeUntil } from "rxjs/operators";
+import JSONFormatter from "json-formatter-js";
+import { JSON_SCHEMA } from "./jsonschema";
+import { MonacoEditorModule } from "ngx-monaco-editor-v2";
+import { UI_SCHEMA } from "./uischema";
+import { EDITOR_OPTIONS } from "./editor-options";
+import { AdapterFormSelectService } from "../../services/adapter-form-select.service";
+import { FormDataService } from "../../services/form-data.service";
+import { clone, get, has } from "lodash-es";
+import { PageTitleService } from "../../services/page-title.service";
@Component({
- selector: 'app-form-builder-page',
- templateUrl: './form-builder-page.component.html',
- styleUrls: ['./form-builder-page.component.scss']
+ selector: "app-form-builder-page",
+ templateUrl: "./form-builder-page.component.html",
+ styleUrls: ["./form-builder-page.component.scss"],
})
export class FormBuilderPageComponent implements OnInit, OnDestroy {
-
editorOptions = EDITOR_OPTIONS;
- JSONSchema = '{}';
+ JSONSchema = "{}";
jsonModel: MonacoEditorModule;
- UISchema = '{}';
+ UISchema = "{}";
uiModel: MonacoEditorModule;
form = new UntypedFormGroup({});
@@ -45,12 +50,12 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
showFormConfig = false;
hasError = false;
- errorMessage = '';
+ errorMessage = "";
isAPIData = false;
- endpoint = '';
+ endpoint = "";
- @ViewChild('modelOutput') modelOutput: ElementRef;
+ @ViewChild("modelOutput") modelOutput: ElementRef;
constructor(
private formService: KendraioFormService,
@@ -58,27 +63,28 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
private shareLinks: ShareLinkGeneratorService,
private formSelect: AdapterFormSelectService,
private formData: FormDataService,
- private readonly pageTitle: PageTitleService
- ) {
- }
+ private readonly pageTitle: PageTitleService,
+ ) {}
ngOnInit() {
- this.pageTitle.setTitle('formBuilder.pageTitle');
- this._schemaChange$.pipe(
- takeUntil(this._destroy$),
- debounceTime(500)
- ).subscribe(() => {
- try {
- this.hasError = false;
- const JSONSchema = JSON.parse(this.JSONSchema);
- this.isDbForm = has(JSONSchema, 'name');
- this.fields = this.formService.schemasToFieldConfig(JSONSchema, JSON.parse(this.UISchema));
- } catch ({message}) {
- this.hasError = true;
- this.errorMessage = message;
- this.fields = [];
- }
- });
+ this.pageTitle.setTitle("formBuilder.pageTitle");
+ this._schemaChange$
+ .pipe(takeUntil(this._destroy$), debounceTime(500))
+ .subscribe(() => {
+ try {
+ this.hasError = false;
+ const JSONSchema = JSON.parse(this.JSONSchema);
+ this.isDbForm = has(JSONSchema, "name");
+ this.fields = this.formService.schemasToFieldConfig(
+ JSONSchema,
+ JSON.parse(this.UISchema),
+ );
+ } catch ({ message }) {
+ this.hasError = true;
+ this.errorMessage = message;
+ this.fields = [];
+ }
+ });
const urlData = this.shareLinks.getData();
if (urlData) {
@@ -88,7 +94,7 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
this.jsonSchemaChange();
}
- setSchemaValues({JSONSchema, UISchema}) {
+ setSchemaValues({ JSONSchema, UISchema }) {
this.JSONSchema = JSON.stringify(JSONSchema, null, 4);
this.UISchema = JSON.stringify(UISchema, null, 4);
}
@@ -101,13 +107,13 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
updateEditorModels() {
this.jsonModel = {
value: this.JSONSchema,
- language: 'json',
- uri: 'a:jsonModel.json'
+ language: "json",
+ uri: "a:jsonModel.json",
};
this.uiModel = {
value: this.UISchema,
- language: 'json',
- uri: 'a:uiModel.json'
+ language: "json",
+ uri: "a:uiModel.json",
};
}
@@ -115,15 +121,18 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
const monaco = (window as any).monaco;
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
- schemas: [{
- fileMatch: ['a:jsonModel.json'],
- uri: 'https://json-schema.org/draft-07/schema',
- schema: JSON_SCHEMA
- }, {
- fileMatch: ['a:uiModel.json'],
- uri: 'https://app.kendra.io/v0/ui-schema',
- schema: UI_SCHEMA
- }]
+ schemas: [
+ {
+ fileMatch: ["a:jsonModel.json"],
+ uri: "https://json-schema.org/draft-07/schema",
+ schema: JSON_SCHEMA,
+ },
+ {
+ fileMatch: ["a:uiModel.json"],
+ uri: "https://app.kendra.io/v0/ui-schema",
+ schema: UI_SCHEMA,
+ },
+ ],
});
}
@@ -134,14 +143,16 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
shareForm() {
const JSONSchema = JSON.parse(this.JSONSchema);
const UISchema = JSON.parse(this.UISchema);
- this.shareLinks.shareFlowLink('form-builder', {JSONSchema, UISchema});
+ this.shareLinks.shareFlowLink("form-builder", { JSONSchema, UISchema });
}
onChange() {
// Replace #modelOutput DIV contents with formatted JSON
const formatter = new JSONFormatter(this.model, Infinity);
while (this.modelOutput.nativeElement.firstChild) {
- this.modelOutput.nativeElement.removeChild(this.modelOutput.nativeElement.firstChild);
+ this.modelOutput.nativeElement.removeChild(
+ this.modelOutput.nativeElement.firstChild,
+ );
}
this.modelOutput.nativeElement.append(formatter.render());
}
@@ -150,8 +161,8 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
// Send form submit event to the form handler service
this.formSubmitHandler.handle({
form: `formBuilderForm`,
- action: 'submit',
- payload: this.form.getRawValue()
+ action: "submit",
+ payload: this.form.getRawValue(),
});
}
@@ -160,12 +171,13 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
if (this.isDbForm) {
const JSONSchema = JSON.parse(this.JSONSchema);
this.saveOriginalDbValues(this.model);
- this.formData.saveData(get(JSONSchema, 'name'), this.model)
- .subscribe(({ok, id, rev}) => {
+ this.formData
+ .saveData(get(JSONSchema, "name"), this.model)
+ .subscribe(({ ok, id, rev }) => {
// TODO: This logic is specific to DB and should not be here
if (ok) {
- this.model['_rev'] = rev;
- this.model['_id'] = id;
+ this.model["_rev"] = rev;
+ this.model["_id"] = id;
this.onChange();
}
});
@@ -173,7 +185,7 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
}
loadFromAdapter() {
- this.formSelect.selectForm().subscribe(values => {
+ this.formSelect.selectForm().subscribe((values) => {
if (!!values) {
this.setSchemaValues(values);
this.updateEditorModels();
@@ -183,7 +195,7 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
}
loadFromSwagger() {
- this.formSelect.selectSwagger().subscribe(values => {
+ this.formSelect.selectSwagger().subscribe((values) => {
if (!!values) {
this.setSchemaValues(values);
this.updateEditorModels();
@@ -194,13 +206,13 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
saveOriginalDbValues(values) {
// TODO: Deep copy to save original values?
- this.originalDbValues = {...values};
+ this.originalDbValues = { ...values };
}
loadFromDatabase() {
const data = JSON.parse(this.JSONSchema);
- if (data && has(data, 'name')) {
- this.formData.loadData(get(data, 'name')).subscribe(values => {
+ if (data && has(data, "name")) {
+ this.formData.loadData(get(data, "name")).subscribe((values) => {
if (!!values) {
this.isAPIData = false;
this.saveOriginalDbValues(values);
@@ -227,21 +239,24 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
}
loadDataFromAPI() {
- this.formData.loadDataFromAPI().subscribe(({ status, endpoint, values}) => {
- if (!!status) {
- // console.log({ values });
- this.saveOriginalDbValues(values);
- this.model = values;
- this.onChange();
- this.isAPIData = true;
- this.endpoint = endpoint;
- }
- });
+ this.formData
+ .loadDataFromAPI()
+ .subscribe(({ status, endpoint, values }) => {
+ if (!!status) {
+ // console.log({ values });
+ this.saveOriginalDbValues(values);
+ this.model = values;
+ this.onChange();
+ this.isAPIData = true;
+ this.endpoint = endpoint;
+ }
+ });
}
onSaveAPI() {
if (this.isAPIData) {
- this.formData.saveAPIData(this.endpoint, this.model)
+ this.formData
+ .saveAPIData(this.endpoint, this.model)
.subscribe((result) => {
// TODO: Data update success
// console.log({ result });
@@ -249,4 +264,3 @@ export class FormBuilderPageComponent implements OnInit, OnDestroy {
}
}
}
-
diff --git a/src/app/pages/kql-builder/kql-builder.component.html b/src/app/pages/kql-builder/kql-builder.component.html
index d0c6eccd3..3fec0b17f 100644
--- a/src/app/pages/kql-builder/kql-builder.component.html
+++ b/src/app/pages/kql-builder/kql-builder.component.html
@@ -1,15 +1,21 @@
-
+
-
- {{errorMessage}}
+
+ {{ errorMessage }}
-
+
-
+
@@ -19,7 +25,9 @@
-
+
diff --git a/src/app/pages/kql-builder/kql-builder.component.ts b/src/app/pages/kql-builder/kql-builder.component.ts
index 72a7450ff..50eab860a 100644
--- a/src/app/pages/kql-builder/kql-builder.component.ts
+++ b/src/app/pages/kql-builder/kql-builder.component.ts
@@ -1,81 +1,83 @@
-import {Component, NgZone, OnInit} from '@angular/core';
-import {JSON_EDITOR_OPTIONS} from './json-editor-options';
-import {KQL_EDITOR_OPTIONS} from './kql-editor-options';
-import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
-import {mappingUtility} from '../../blocks/mapping-block/mapping-util';
-import {HttpClient} from '@angular/common/http';
-import {map, take} from 'rxjs/operators';
-import {camelCase, get, sortBy, set} from 'lodash-es';
-import {WorkflowService} from '../../services/workflow.service';
-import {SaveWorkflowDialogComponent} from '../../dialogs/save-workflow-dialog/save-workflow-dialog.component';
-import {MatDialog} from '@angular/material/dialog';
-import {ActivatedRoute, Router} from '@angular/router';
+import { Component, NgZone, OnInit } from "@angular/core";
+import { JSON_EDITOR_OPTIONS } from "./json-editor-options";
+import { KQL_EDITOR_OPTIONS } from "./kql-editor-options";
+import { MonacoEditorModule } from "ngx-monaco-editor-v2";
+import { mappingUtility } from "../../blocks/mapping-block/mapping-util";
+import { HttpClient } from "@angular/common/http";
+import { map, take } from "rxjs/operators";
+import { camelCase, get, sortBy, set } from "lodash-es";
+import { WorkflowService } from "../../services/workflow.service";
+import { SaveWorkflowDialogComponent } from "../../dialogs/save-workflow-dialog/save-workflow-dialog.component";
+import { MatDialog } from "@angular/material/dialog";
+import { ActivatedRoute, Router } from "@angular/router";
@Component({
- selector: 'app-kql-builder',
- templateUrl: './kql-builder.component.html',
- styleUrls: ['./kql-builder.component.scss']
+ selector: "app-kql-builder",
+ templateUrl: "./kql-builder.component.html",
+ styleUrls: ["./kql-builder.component.scss"],
})
export class KqlBuilderComponent implements OnInit {
-
jsonEditorOptions = JSON_EDITOR_OPTIONS;
kqlEditorOptions = KQL_EDITOR_OPTIONS;
- dataInTxt = JSON.stringify({
- 'employees': {
- 'jd101': {
- 'firstname': 'Jane',
- 'surname': 'Doe',
- 'department': 'IT',
- 'manager': 'kp102'
- },
- 'kp102': {
- 'firstname': 'Kitty',
- 'surname': 'Pride',
- 'department': 'IT',
- 'manager': 'jh104'
+ dataInTxt = JSON.stringify(
+ {
+ employees: {
+ jd101: {
+ firstname: "Jane",
+ surname: "Doe",
+ department: "IT",
+ manager: "kp102",
+ },
+ kp102: {
+ firstname: "Kitty",
+ surname: "Pride",
+ department: "IT",
+ manager: "jh104",
+ },
+ cx103: {
+ firstname: "Charles",
+ surname: "Xavier",
+ department: "Management",
+ },
+ jh104: {
+ firstname: "James",
+ surname: "Howlett",
+ department: "Security",
+ manager: "cx103",
+ },
},
- 'cx103': {
- 'firstname': 'Charles',
- 'surname': 'Xavier',
- 'department': 'Management'
- },
- 'jh104': {
- 'firstname': 'James',
- 'surname': 'Howlett',
- 'department': 'Security',
- 'manager': 'cx103'
- }
- }
- }, null, 2);
+ },
+ null,
+ 2,
+ );
dataInModel: MonacoEditorModule;
dataOut = {};
- queryTxt = 'data';
+ queryTxt = "data";
queryModel: MonacoEditorModule;
- errorMessage = '';
+ errorMessage = "";
flows = [];
selectedFlow;
blockOptions = [];
- blockLimit = '0';
+ blockLimit = "0";
blockExec = [];
startModels;
context = {};
flowConfig = {};
selectedBlock = {};
- selectedBlockType = '';
+ selectedBlockType = "";
constructor(
private readonly http: HttpClient,
private readonly zone: NgZone,
private readonly dialog: MatDialog,
- private readonly route: ActivatedRoute
- ) {
- }
+ private readonly route: ActivatedRoute,
+ ) {}
ngOnInit() {
this.getFlows();
@@ -84,27 +86,27 @@ export class KqlBuilderComponent implements OnInit {
}
async createDummyContext() {
- this.route.queryParams.pipe(
- take(1)
- ).subscribe(queryParams => {
- this.context = {...this.context, queryParams};
+ this.route.queryParams.pipe(take(1)).subscribe((queryParams) => {
+ this.context = { ...this.context, queryParams };
});
}
async getFlows() {
- const url = 'https://app.kendra.io/api';
- this.flows = await this.http.get
(url)
- .pipe(
- map(x => sortBy(x, 'title'))
- )
+ const url = "https://app.kendra.io/api";
+ this.flows = await this.http
+ .get(url)
+ .pipe(map((x) => sortBy(x, "title")))
.toPromise();
}
runMapping() {
- this.errorMessage = '';
+ this.errorMessage = "";
try {
const data = JSON.parse(this.dataInTxt);
- this.dataOut = mappingUtility({data, context: this.context }, this.queryTxt);
+ this.dataOut = mappingUtility(
+ { data, context: this.context },
+ this.queryTxt,
+ );
} catch (e) {
this.errorMessage = e.message;
}
@@ -112,14 +114,15 @@ export class KqlBuilderComponent implements OnInit {
async updateBlockOptions(v) {
this.blockOptions = [];
- this.blockLimit = '0';
- const {adapterName, workflowId} = this.flows[+v];
- const {blocks, ...flowConfig} = await this.http.get(`https://app.kendra.io/api/${adapterName}/${workflowId}`)
+ this.blockLimit = "0";
+ const { adapterName, workflowId } = this.flows[+v];
+ const { blocks, ...flowConfig } = await this.http
+ .get(`https://app.kendra.io/api/${adapterName}/${workflowId}`)
.toPromise();
this.blockOptions = blocks;
this.flowConfig = flowConfig;
this.context = {
- app: {adapterName, workflowId}
+ app: { adapterName, workflowId },
};
await this.createDummyContext();
}
@@ -128,13 +131,19 @@ export class KqlBuilderComponent implements OnInit {
setTimeout(() => {
this.zone.run(() => {
this.selectedBlock = this.blockOptions[parseInt(this.blockLimit, 10)];
- this.selectedBlockType = get(this.selectedBlock, 'type', '');
- if (this.selectedBlockType === 'mapping') {
- this.queryTxt = get(this.selectedBlock, 'mapping', 'data');
- this.blockExec = this.blockOptions.slice(0, parseInt(this.blockLimit, 10));
+ this.selectedBlockType = get(this.selectedBlock, "type", "");
+ if (this.selectedBlockType === "mapping") {
+ this.queryTxt = get(this.selectedBlock, "mapping", "data");
+ this.blockExec = this.blockOptions.slice(
+ 0,
+ parseInt(this.blockLimit, 10),
+ );
this.startModels = this.blockExec.map(() => ({}));
} else {
- this.blockExec = this.blockOptions.slice(0, 1 + parseInt(this.blockLimit, 10));
+ this.blockExec = this.blockOptions.slice(
+ 0,
+ 1 + parseInt(this.blockLimit, 10),
+ );
this.startModels = this.blockExec.map(() => ({}));
}
});
@@ -144,17 +153,20 @@ export class KqlBuilderComponent implements OnInit {
saveBlocks() {
// console.log(this.blockOptions);
// const prevMapping = get(this.selectedBlock, 'mapping', 'data');
- set(this.blockOptions[parseInt(this.blockLimit, 10)], 'mapping', this.queryTxt);
+ set(
+ this.blockOptions[parseInt(this.blockLimit, 10)],
+ "mapping",
+ this.queryTxt,
+ );
const dialogRef = this.dialog.open(SaveWorkflowDialogComponent, {
disableClose: true,
- data: {...this.flowConfig, blocks: this.blockOptions}
+ data: { ...this.flowConfig, blocks: this.blockOptions },
});
- dialogRef.afterClosed().subscribe(values => {
+ dialogRef.afterClosed().subscribe((values) => {
if (!!values) {
- console.log('workflow saved', {values});
+ console.log("workflow saved", { values });
}
});
-
}
loadNewData(v) {
@@ -179,8 +191,8 @@ export class KqlBuilderComponent implements OnInit {
updateDataInModel() {
this.dataInModel = {
value: this.dataInTxt,
- language: 'json',
- uri: 'a:dataModel.json'
+ language: "json",
+ uri: "a:dataModel.json",
};
}
}
diff --git a/src/app/pages/query-builder-page/query-builder-page.component.html b/src/app/pages/query-builder-page/query-builder-page.component.html
index 315dbf900..816fcabd8 100644
--- a/src/app/pages/query-builder-page/query-builder-page.component.html
+++ b/src/app/pages/query-builder-page/query-builder-page.component.html
@@ -1,41 +1,60 @@
-/**
-* DEPRECATED
-* All functionality has been moved to the blocks builder
-*/
+/** * DEPRECATED * All functionality has been moved to the blocks builder */
-
-
Query Schema
-
+
-
+
{{ errorMessage }}
-
+
Result output
-
+
Mapping output
@@ -46,14 +65,21 @@
0">{{ title }}
0">{{ description }}
-
-
+
+
-
diff --git a/src/app/pages/query-builder-page/query-builder-page.component.ts b/src/app/pages/query-builder-page/query-builder-page.component.ts
index ef91549ff..692ac198e 100644
--- a/src/app/pages/query-builder-page/query-builder-page.component.ts
+++ b/src/app/pages/query-builder-page/query-builder-page.component.ts
@@ -2,32 +2,37 @@
* DEPRECATED
* All functionality has been moved to the blocks builder
*/
-import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';
-import { EDITOR_OPTIONS } from './editor-options';
-import JSONFormatter from 'json-formatter-js';
-import {get, has, isString} from 'lodash-es';
-import {BehaviorSubject} from 'rxjs';
-import '@angular/material/tooltip';
-import { HttpClient, HttpHeaders } from '@angular/common/http';
-import {AdapterQuerySelectDialogComponent} from '../../dialogs/adapter-query-select-dialog/adapter-query-select-dialog.component';
-import {ShareLinkGeneratorService} from '../../services/share-link-generator.service';
-import { search } from 'jmespath';
-import {DocumentRepositoryService} from '../../services/document-repository.service';
-import {QUERY_SCHEMA} from './query.schema';
-import {map, tap} from 'rxjs/operators';
-import {ContextDataService} from '../../services/context-data.service';
-import {MatDialog} from '@angular/material/dialog';
+import {
+ AfterViewInit,
+ Component,
+ ElementRef,
+ OnInit,
+ ViewChild,
+} from "@angular/core";
+import { EDITOR_OPTIONS } from "./editor-options";
+import JSONFormatter from "json-formatter-js";
+import { get, has, isString } from "lodash-es";
+import { BehaviorSubject } from "rxjs";
+import "@angular/material/tooltip";
+import { HttpClient, HttpHeaders } from "@angular/common/http";
+import { AdapterQuerySelectDialogComponent } from "../../dialogs/adapter-query-select-dialog/adapter-query-select-dialog.component";
+import { ShareLinkGeneratorService } from "../../services/share-link-generator.service";
+import { search } from "jmespath";
+import { DocumentRepositoryService } from "../../services/document-repository.service";
+import { QUERY_SCHEMA } from "./query.schema";
+import { map, tap } from "rxjs/operators";
+import { ContextDataService } from "../../services/context-data.service";
+import { MatDialog } from "@angular/material/dialog";
@Component({
- selector: 'app-query-builder-page',
- templateUrl: './query-builder-page.component.html',
- styleUrls: ['./query-builder-page.component.scss']
+ selector: "app-query-builder-page",
+ templateUrl: "./query-builder-page.component.html",
+ styleUrls: ["./query-builder-page.component.scss"],
})
export class QueryBuilderPageComponent implements OnInit, AfterViewInit {
-
showFormConfig = false;
- title = '';
- description = '';
+ title = "";
+ description = "";
editorOptions = EDITOR_OPTIONS;
@@ -35,54 +40,54 @@ export class QueryBuilderPageComponent implements OnInit, AfterViewInit {
queryModel;
output;
- @ViewChild('modelOutput') modelOutput: ElementRef;
+ @ViewChild("modelOutput") modelOutput: ElementRef;
showMappingResult = false;
- @ViewChild('mappingOutput') mappingOutput: ElementRef;
+ @ViewChild("mappingOutput") mappingOutput: ElementRef;
- @ViewChild('gridAngular') gridAngular;
+ @ViewChild("gridAngular") gridAngular;
defaultColDef = {
- resizable: true
+ resizable: true,
};
columnDefs = [];
- outputMapping = '';
+ outputMapping = "";
- outputType = 'grid';
+ outputType = "grid";
outputConfig;
_rowData = new BehaviorSubject
>([]);
rowData = this._rowData.pipe(
- map(values => {
+ map((values) => {
if (!!this.outputMapping && this.outputMapping.length > 0) {
try {
const mappingResult = search(values, this.outputMapping);
this.updateMappingResult(mappingResult);
- setTimeout(() => this.showMappingResult = true, 0);
+ setTimeout(() => (this.showMappingResult = true), 0);
return mappingResult;
} catch (e) {
- console.log('JMESPath Error', e.message);
+ console.log("JMESPath Error", e.message);
}
}
- setTimeout(() => this.showMappingResult = false, 0);
+ setTimeout(() => (this.showMappingResult = false), 0);
return values;
}),
tap(() => {
if (!!this.gridAngular) {
this.gridAngular.api.sizeColumnsToFit();
}
- })
+ }),
);
hasError = false;
- errorMessage = '';
+ errorMessage = "";
constructor(
private readonly http: HttpClient,
private shareLinks: ShareLinkGeneratorService,
private readonly dialog: MatDialog,
private readonly docRepo: DocumentRepositoryService,
- private readonly contextData: ContextDataService
- ) { }
+ private readonly contextData: ContextDataService,
+ ) {}
ngOnInit() {
const urlData = this.shareLinks.getData();
@@ -91,8 +96,8 @@ export class QueryBuilderPageComponent implements OnInit, AfterViewInit {
this.queryModelText = JSON.stringify(Q, null, 4);
this.queryModel = {
value: this.queryModelText,
- language: 'json',
- uri: 'a:queryModel.json'
+ language: "json",
+ uri: "a:queryModel.json",
};
}
this.runQuery();
@@ -106,26 +111,34 @@ export class QueryBuilderPageComponent implements OnInit, AfterViewInit {
const monaco = (window as any).monaco;
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
validate: true,
- schemas: [{
- fileMatch: ['a:queryModel.json'],
- uri: 'https://app.kendra.io/v0/query-schema',
- schema: QUERY_SCHEMA
- }]
+ schemas: [
+ {
+ fileMatch: ["a:queryModel.json"],
+ uri: "https://app.kendra.io/v0/query-schema",
+ schema: QUERY_SCHEMA,
+ },
+ ],
});
}
constructEndpointUrl(dataSource) {
- if (isString(get(dataSource, 'endpoint', ''))) {
+ if (isString(get(dataSource, "endpoint", ""))) {
return dataSource.endpoint;
}
const demoModel = {};
- const endpoint = this.contextData.getFromContextWithModel(dataSource.endpoint, demoModel);
+ const endpoint = this.contextData.getFromContextWithModel(
+ dataSource.endpoint,
+ demoModel,
+ );
// console.log({ endpoint });
- const protocol = get(endpoint, 'protocol', 'https:');
- const host = get(endpoint, 'host', '');
- const pathname = get(endpoint, 'pathname', '/');
- const query = get(endpoint, 'query', []);
- const reduceQuery = _q => Object.keys(_q).map(key => `${key}=${_q[key]}`, []).join('&');
+ const protocol = get(endpoint, "protocol", "https:");
+ const host = get(endpoint, "host", "");
+ const pathname = get(endpoint, "pathname", "/");
+ const query = get(endpoint, "query", []);
+ const reduceQuery = (_q) =>
+ Object.keys(_q)
+ .map((key) => `${key}=${_q[key]}`, [])
+ .join("&");
return `${protocol}//${host}${pathname}?${reduceQuery(query)}`;
}
@@ -134,58 +147,71 @@ export class QueryBuilderPageComponent implements OnInit, AfterViewInit {
try {
// Parse and validate model
const query = JSON.parse(this.queryModelText);
- this.outputConfig = get(query, 'output');
- this.outputType = get(query, 'output.type', 'grid');
- this.columnDefs = this.preprocessColumnDefinition(get(query, 'output.columnDefs', []));
- this.outputMapping = get(query, 'mapping', '');
- const { type, ...dataSource } = get(query, 'dataSource', { type: false });
- this.title = get(query, 'title', '');
- this.description = get(query, 'description', '');
+ this.outputConfig = get(query, "output");
+ this.outputType = get(query, "output.type", "grid");
+ this.columnDefs = this.preprocessColumnDefinition(
+ get(query, "output.columnDefs", []),
+ );
+ this.outputMapping = get(query, "mapping", "");
+ const { type, ...dataSource } = get(query, "dataSource", { type: false });
+ this.title = get(query, "title", "");
+ this.description = get(query, "description", "");
// Run query
switch (type) {
- case 'local':
+ case "local":
const { schema } = dataSource;
- this.docRepo.listAllOfType(schema).subscribe(values => {
+ this.docRepo.listAllOfType(schema).subscribe((values) => {
// console.log({ values });
this.output = values;
this.updateOutputDisplay();
this._rowData.next(values || []);
});
break;
- case 'remote':
+ case "remote":
const endpoint = this.constructEndpointUrl(dataSource);
// console.log({ endpoint });
let headers = new HttpHeaders();
- if (has(dataSource, 'authentication.type')) {
- switch (get(dataSource, 'authentication.type')) {
- case 'basic-auth':
- const valueGetters = get(dataSource, 'authentication.valueGetters', {});
- const context = { ...dataSource.authentication, ...this.contextData.getGlobalContext(valueGetters, {}) };
- if (has(context, 'username') && has(context, 'password')) {
+ if (has(dataSource, "authentication.type")) {
+ switch (get(dataSource, "authentication.type")) {
+ case "basic-auth":
+ const valueGetters = get(
+ dataSource,
+ "authentication.valueGetters",
+ {},
+ );
+ const context = {
+ ...dataSource.authentication,
+ ...this.contextData.getGlobalContext(valueGetters, {}),
+ };
+ if (has(context, "username") && has(context, "password")) {
const { username, password } = context;
- headers = headers.append('Authorization', 'Basic ' + btoa(`${username}:${password}`));
+ headers = headers.append(
+ "Authorization",
+ "Basic " + btoa(`${username}:${password}`),
+ );
}
break;
- case 'bearer':
+ case "bearer":
break;
default:
- console.log('Unknown authentication type');
+ console.log("Unknown authentication type");
}
}
- this.http.get>(endpoint, { headers }).subscribe(values => {
- this.output = values;
- this.updateOutputDisplay();
- this._rowData.next(values);
- });
+ this.http
+ .get>(endpoint, { headers })
+ .subscribe((values) => {
+ this.output = values;
+ this.updateOutputDisplay();
+ this._rowData.next(values);
+ });
break;
default:
this.hasError = true;
- this.errorMessage = 'Unknown data source type';
+ this.errorMessage = "Unknown data source type";
this._rowData.next([]);
}
// Update display
-
} catch ({ message }) {
this.hasError = true;
this.errorMessage = message;
@@ -193,25 +219,31 @@ export class QueryBuilderPageComponent implements OnInit, AfterViewInit {
}
preprocessColumnDefinition(def: Array) {
- return def.map(item => ({
+ return def.map((item) => ({
...item,
- ...has(item, 'valueGetter') ? { valueGetter: ({ data }) => {
- // console.log({ data, item });
- try {
- return search(data, item['valueGetter']);
- } catch (e) {
- return e.message;
- }
- }} : {}
+ ...(has(item, "valueGetter")
+ ? {
+ valueGetter: ({ data }) => {
+ // console.log({ data, item });
+ try {
+ return search(data, item["valueGetter"]);
+ } catch (e) {
+ return e.message;
+ }
+ },
+ }
+ : {}),
}));
}
updateOutputDisplay() {
if (!!this.modelOutput) {
// Replace #modelOutput DIV contents with formatted JSON
- const formatter = new JSONFormatter(this.output, 0, {theme: 'dark'});
+ const formatter = new JSONFormatter(this.output, 0, { theme: "dark" });
while (this.modelOutput.nativeElement.firstChild) {
- this.modelOutput.nativeElement.removeChild(this.modelOutput.nativeElement.firstChild);
+ this.modelOutput.nativeElement.removeChild(
+ this.modelOutput.nativeElement.firstChild,
+ );
}
this.modelOutput.nativeElement.append(formatter.render());
}
@@ -219,9 +251,11 @@ export class QueryBuilderPageComponent implements OnInit, AfterViewInit {
updateMappingResult(mappingResult) {
if (!!this.mappingOutput && this.showMappingResult) {
- const formatter = new JSONFormatter(mappingResult, 0, {theme: 'dark'});
+ const formatter = new JSONFormatter(mappingResult, 0, { theme: "dark" });
while (this.mappingOutput.nativeElement.firstChild) {
- this.mappingOutput.nativeElement.removeChild(this.mappingOutput.nativeElement.firstChild);
+ this.mappingOutput.nativeElement.removeChild(
+ this.mappingOutput.nativeElement.firstChild,
+ );
}
this.mappingOutput.nativeElement.append(formatter.render());
}
@@ -233,13 +267,13 @@ export class QueryBuilderPageComponent implements OnInit, AfterViewInit {
loadQueryFromAdapter() {
const dialogRef = this.dialog.open(AdapterQuerySelectDialogComponent, {});
- dialogRef.afterClosed().subscribe(data => {
+ dialogRef.afterClosed().subscribe((data) => {
if (!!data) {
this.queryModelText = JSON.stringify(data, null, 4);
this.queryModel = {
value: this.queryModelText,
- language: 'json',
- uri: 'a:queryModel.json'
+ language: "json",
+ uri: "a:queryModel.json",
};
this.runQuery();
}
@@ -248,6 +282,6 @@ export class QueryBuilderPageComponent implements OnInit, AfterViewInit {
shareQuery() {
const Q = JSON.parse(this.queryModelText);
- this.shareLinks.shareFlowLink('query-builder', {Q});
+ this.shareLinks.shareFlowLink("query-builder", { Q });
}
}
diff --git a/src/app/pages/settings-page/settings-page.component.ts b/src/app/pages/settings-page/settings-page.component.ts
index 51ac421f8..0f6c64d29 100644
--- a/src/app/pages/settings-page/settings-page.component.ts
+++ b/src/app/pages/settings-page/settings-page.component.ts
@@ -1,19 +1,18 @@
-import { Component, OnInit } from '@angular/core';
-import { Router } from '@angular/router';
-import { MatDialog } from '@angular/material/dialog';
-import { ConfirmAppResetDialogComponent } from '../../dialogs/confirm-app-reset-dialog/confirm-app-reset-dialog.component';
-import { PageTitleService } from '../../services/page-title.service';
-import { AdaptersService } from '../../services/adapters.service';
-import { DocumentRepositoryService } from '../../services/document-repository.service';
-import {AppSettingsService} from '../../services/app-settings.service';
+import { Component, OnInit } from "@angular/core";
+import { Router } from "@angular/router";
+import { MatDialog } from "@angular/material/dialog";
+import { ConfirmAppResetDialogComponent } from "../../dialogs/confirm-app-reset-dialog/confirm-app-reset-dialog.component";
+import { PageTitleService } from "../../services/page-title.service";
+import { AdaptersService } from "../../services/adapters.service";
+import { DocumentRepositoryService } from "../../services/document-repository.service";
+import { AppSettingsService } from "../../services/app-settings.service";
@Component({
- selector: 'app-settings-page',
- templateUrl: './settings-page.component.html',
- styleUrls: ['./settings-page.component.scss']
+ selector: "app-settings-page",
+ templateUrl: "./settings-page.component.html",
+ styleUrls: ["./settings-page.component.scss"],
})
export class SettingsPageComponent implements OnInit {
-
isDebug = false;
showHelp = true;
@@ -23,37 +22,36 @@ export class SettingsPageComponent implements OnInit {
private readonly pageTitle: PageTitleService,
private readonly adapters: AdaptersService,
private readonly database: DocumentRepositoryService,
- private readonly settings: AppSettingsService
- ) { }
+ private readonly settings: AppSettingsService,
+ ) {}
ngOnInit() {
- this.pageTitle.setTitle('App settings');
- this.isDebug = this.settings.get('debugMode', false);
- this.showHelp = this.settings.getTmp('showHelp', true);
+ this.pageTitle.setTitle("App settings");
+ this.isDebug = this.settings.get("debugMode", false);
+ this.showHelp = this.settings.getTmp("showHelp", true);
}
// TODO: Remove any other data added to localStorage
resetApp() {
const dialogRef = this.dialog.open(ConfirmAppResetDialogComponent, {
- width: '300px'
+ width: "300px",
});
- dialogRef.afterClosed().subscribe(result => {
+ dialogRef.afterClosed().subscribe((result) => {
if (result) {
this.adapters.resetApp();
this.database.resetApp();
- this.router.navigate(['/']);
+ this.router.navigate(["/"]);
}
});
}
toggleDebugMode() {
this.isDebug = !this.isDebug;
- this.settings.set('debugMode', this.isDebug);
+ this.settings.set("debugMode", this.isDebug);
}
toggleShowHelp() {
this.showHelp = !this.showHelp;
- this.settings.set('showHelp', this.showHelp);
+ this.settings.set("showHelp", this.showHelp);
}
-
}
diff --git a/src/app/pages/test-api-page/test-api-page.component.ts b/src/app/pages/test-api-page/test-api-page.component.ts
index d360c16c3..c9a43ff06 100644
--- a/src/app/pages/test-api-page/test-api-page.component.ts
+++ b/src/app/pages/test-api-page/test-api-page.component.ts
@@ -1,20 +1,27 @@
-import {Component, OnInit} from '@angular/core';
-import {TestDataService} from '../../services/test-data.service';
-import {forkJoin, Observable, of, Subject} from 'rxjs';
-import {catchError, map, mergeMap, switchMap, take, tap, withLatestFrom} from 'rxjs/operators';
-import {ImportProgressDialogComponent} from '../../dialogs/import-progress-dialog/import-progress-dialog.component';
-import {Router} from '@angular/router';
-import {PageTitleService} from '../../services/page-title.service';
-import { MatDialog } from '@angular/material/dialog';
-import {TestImportDialogComponent} from '../../dialogs/test-import-dialog/test-import-dialog.component';
+import { Component, OnInit } from "@angular/core";
+import { TestDataService } from "../../services/test-data.service";
+import { forkJoin, Observable, of, Subject } from "rxjs";
+import {
+ catchError,
+ map,
+ mergeMap,
+ switchMap,
+ take,
+ tap,
+ withLatestFrom,
+} from "rxjs/operators";
+import { ImportProgressDialogComponent } from "../../dialogs/import-progress-dialog/import-progress-dialog.component";
+import { Router } from "@angular/router";
+import { PageTitleService } from "../../services/page-title.service";
+import { MatDialog } from "@angular/material/dialog";
+import { TestImportDialogComponent } from "../../dialogs/test-import-dialog/test-import-dialog.component";
@Component({
- selector: 'app-test-api-page',
- templateUrl: './test-api-page.component.html',
- styleUrls: ['./test-api-page.component.scss']
+ selector: "app-test-api-page",
+ templateUrl: "./test-api-page.component.html",
+ styleUrls: ["./test-api-page.component.scss"],
})
export class TestApiPageComponent implements OnInit {
-
entityTypes$;
selectedType;
@@ -31,19 +38,20 @@ export class TestApiPageComponent implements OnInit {
private readonly router: Router,
private readonly pageTitle: PageTitleService,
private readonly dialog: MatDialog,
- ) {
- }
+ ) {}
ngOnInit() {
this.entityTypes$ = this.testData.listEntityTypes({ live: true });
this.entityList$ = new Subject().pipe(
- switchMap(type => this.testData.listEntities(type, { live: true }))
+ switchMap((type) => this.testData.listEntities(type, { live: true })),
);
this.selectedEntity$ = new Subject().pipe(
- switchMap(id => this.testData.getEntity(this.selectedType, id, { live: true }))
+ switchMap((id) =>
+ this.testData.getEntity(this.selectedType, id, { live: true }),
+ ),
);
this.listAll$ = new Subject().pipe(
- switchMap(type => this.testData.listAll(type, { live: true }))
+ switchMap((type) => this.testData.listAll(type, { live: true })),
);
}
@@ -65,18 +73,24 @@ export class TestApiPageComponent implements OnInit {
(this.testData.listEntityTypes({ live: true }) as Observable)
.pipe(
take(1),
- map(types => types.filter(type => type !== 'video' && type !== 'photo')),
- mergeMap(types => forkJoin(types.map(type => {
- return this.testData.listAll(type, { live: true }).pipe(
- take(1),
- catchError(err => of([])),
- map(records => ({ type, records }))
- );
- })))
+ map((types) =>
+ types.filter((type) => type !== "video" && type !== "photo"),
+ ),
+ mergeMap((types) =>
+ forkJoin(
+ types.map((type) => {
+ return this.testData.listAll(type, { live: true }).pipe(
+ take(1),
+ catchError((err) => of([])),
+ map((records) => ({ type, records })),
+ );
+ }),
+ ),
+ ),
)
- .subscribe(allItems => {
+ .subscribe((allItems) => {
this.isLoadingAll = false;
- allItems.map(content => {
+ allItems.map((content) => {
this.doImport(content);
});
});
@@ -84,20 +98,23 @@ export class TestApiPageComponent implements OnInit {
importAll() {
const type = this.selectedType;
- this.testData.listAll(type, { live: true }).pipe(take(1)).subscribe(records => {
- if (records) {
- this.doImport({ type, records });
- }
- });
+ this.testData
+ .listAll(type, { live: true })
+ .pipe(take(1))
+ .subscribe((records) => {
+ if (records) {
+ this.doImport({ type, records });
+ }
+ });
}
doImport(content) {
const dialogRef = this.dialog.open(TestImportDialogComponent, {
disableClose: true,
- data: {content}
+ data: { content },
});
dialogRef.afterClosed().subscribe(() => {
- this.router.navigate(['/docs']);
+ this.router.navigate(["/docs"]);
});
}
}
diff --git a/src/app/pages/upload-page/upload-page.component.ts b/src/app/pages/upload-page/upload-page.component.ts
index 356fff0cf..e41d00586 100644
--- a/src/app/pages/upload-page/upload-page.component.ts
+++ b/src/app/pages/upload-page/upload-page.component.ts
@@ -1,17 +1,16 @@
-import { Component, OnInit } from '@angular/core';
-import { PageTitleService } from '../../services/page-title.service';
-import {from, interval} from 'rxjs';
-import { take, tap } from 'rxjs/operators';
-import { forkJoin } from 'rxjs';
-import { MatSnackBar } from '@angular/material/snack-bar';
+import { Component, OnInit } from "@angular/core";
+import { PageTitleService } from "../../services/page-title.service";
+import { from, interval } from "rxjs";
+import { take, tap } from "rxjs/operators";
+import { forkJoin } from "rxjs";
+import { MatSnackBar } from "@angular/material/snack-bar";
@Component({
- selector: 'app-upload-page',
- templateUrl: './upload-page.component.html',
- styleUrls: ['./upload-page.component.scss']
+ selector: "app-upload-page",
+ templateUrl: "./upload-page.component.html",
+ styleUrls: ["./upload-page.component.scss"],
})
export class UploadPageComponent implements OnInit {
-
adapters$;
uploadStatus = {};
uploadEnabled = {};
@@ -21,962 +20,989 @@ export class UploadPageComponent implements OnInit {
bloomen: true,
mixcloud: true,
ddex: true,
- 'copyright-hub': true,
+ "copyright-hub": true,
unsplash: true,
dotbc: true,
chant: true,
- 'apple-music': true,
+ "apple-music": true,
youtube: true,
teosto: true,
spotify: true,
schema: true,
- 'ppl-ipn': true,
+ "ppl-ipn": true,
bandcamp: true,
- 'm-rin': true,
+ "m-rin": true,
example: true,
resonate: true,
tidal: true,
omi: true,
kendraio: true,
- soundcloud: true
+ soundcloud: true,
};
constructor(
private readonly pageTitle: PageTitleService,
- private readonly snackBar: MatSnackBar
- ) { }
+ private readonly snackBar: MatSnackBar,
+ ) {}
ngOnInit() {
- this.pageTitle.setTitle('Upload');
+ this.pageTitle.setTitle("Upload");
this.adapters$ = from([
{
- 'bloomen': {
- 'adapter': {
- 'name': 'bloomen',
- 'title': 'Bloomen',
- 'description': 'Integration with the Bloomen project',
- 'infoUrl': 'http://bloomen.io/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': true,
- 'swagger': 'extra/bloomen/api-docs.json',
- 'database': [
- 'extra/bloomen/schemas/user.schema.json',
- 'extra/bloomen/schemas/photo.schema.json'
+ bloomen: {
+ adapter: {
+ name: "bloomen",
+ title: "Bloomen",
+ description: "Integration with the Bloomen project",
+ infoUrl: "http://bloomen.io/",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: true,
+ swagger: "extra/bloomen/api-docs.json",
+ database: [
+ "extra/bloomen/schemas/user.schema.json",
+ "extra/bloomen/schemas/photo.schema.json",
],
- 'forms': {
- 'musicalWork': {
- 'jsonSchema': 'extra/bloomen/schemas/musicalWork.schema.json',
- 'uiSchema': 'extra/bloomen/schemas/musicalWork.ui.json'
- },
- 'soundRecording': {
- 'jsonSchema': 'extra/bloomen/schemas/soundRecording.schema.json',
- 'uiSchema': 'extra/bloomen/schemas/soundRecording.ui.json'
- },
- 'user': {
- 'jsonSchema': 'extra/bloomen/schemas/user.schema.json',
- 'uiSchema': 'extra/bloomen/schemas/user.ui.json'
- },
- 'photo': {
- 'jsonSchema': 'extra/bloomen/schemas/photo.schema.json',
- 'uiSchema': 'extra/bloomen/schemas/photo.ui.json'
- }
+ forms: {
+ musicalWork: {
+ jsonSchema: "extra/bloomen/schemas/musicalWork.schema.json",
+ uiSchema: "extra/bloomen/schemas/musicalWork.ui.json",
+ },
+ soundRecording: {
+ jsonSchema: "extra/bloomen/schemas/soundRecording.schema.json",
+ uiSchema: "extra/bloomen/schemas/soundRecording.ui.json",
+ },
+ user: {
+ jsonSchema: "extra/bloomen/schemas/user.schema.json",
+ uiSchema: "extra/bloomen/schemas/user.ui.json",
+ },
+ photo: {
+ jsonSchema: "extra/bloomen/schemas/photo.schema.json",
+ uiSchema: "extra/bloomen/schemas/photo.ui.json",
+ },
},
- 'queries': [
- 'extra/bloomen/queries/listSoundRecordings.json',
- 'extra/bloomen/queries/listMusicalWorks.json',
- 'extra/bloomen/queries/chartReleases.json'
+ queries: [
+ "extra/bloomen/queries/listSoundRecordings.json",
+ "extra/bloomen/queries/listMusicalWorks.json",
+ "extra/bloomen/queries/chartReleases.json",
+ ],
+ configs: [
+ "extra/bloomen/configs/listSoundRecordings.json",
+ "extra/bloomen/configs/listMusicalWorks.json",
+ "extra/bloomen/configs/chartReleases.json",
+ "extra/bloomen/configs/editSoundRecording.json",
+ "extra/bloomen/configs/messageExample.json",
+ "extra/bloomen/configs/templateExample.json",
+ "extra/bloomen/configs/dialogExample.json",
+ "extra/bloomen/configs/bloomenApiJwt-musicalWorks.json",
+ "extra/bloomen/configs/bloomenApiJwt-soundRecordings.json",
+ "extra/bloomen/configs/bloomenApiJwt-exportMusicalWorks.json",
+ "extra/bloomen/configs/photo-search-example.json",
+ "extra/bloomen/configs/importMusicalWorks.json",
+ "extra/bloomen/configs/listSoundRecordings-view-edit.json",
],
- 'configs': [
- 'extra/bloomen/configs/listSoundRecordings.json',
- 'extra/bloomen/configs/listMusicalWorks.json',
- 'extra/bloomen/configs/chartReleases.json',
- 'extra/bloomen/configs/editSoundRecording.json',
- 'extra/bloomen/configs/messageExample.json',
- 'extra/bloomen/configs/templateExample.json',
- 'extra/bloomen/configs/dialogExample.json',
- 'extra/bloomen/configs/bloomenApiJwt-musicalWorks.json',
- 'extra/bloomen/configs/bloomenApiJwt-soundRecordings.json',
- 'extra/bloomen/configs/bloomenApiJwt-exportMusicalWorks.json',
- 'extra/bloomen/configs/photo-search-example.json',
- 'extra/bloomen/configs/importMusicalWorks.json',
- 'extra/bloomen/configs/listSoundRecordings-view-edit.json'
- ]
- }
+ },
},
- 'mixcloud': {
- 'adapter': {
- 'name': 'mixcloud',
- 'title': 'MixCloud',
- 'description': 'Upload your mix, radio show or Podcast to Mixcloud for free',
- 'infoUrl': 'https://www.mixcloud.com/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': true
- }
+ mixcloud: {
+ adapter: {
+ name: "mixcloud",
+ title: "MixCloud",
+ description:
+ "Upload your mix, radio show or Podcast to Mixcloud for free",
+ infoUrl: "https://www.mixcloud.com/",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: true,
+ },
},
- 'ddex': {
- 'adapter': {
- 'name': 'ddex',
- 'title': 'DDEX',
- 'description': 'Implementation of schemas for DDEX messages',
- 'infoUrl': 'https://ddex.net/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'https://kendra.io',
- 'uploads': false,
- 'configs': [
- 'extra/ddex/ern4-import.json'
- ]
- }
+ ddex: {
+ adapter: {
+ name: "ddex",
+ title: "DDEX",
+ description: "Implementation of schemas for DDEX messages",
+ infoUrl: "https://ddex.net/",
+ maintainer: "Kendraio",
+ supportUrl: "https://kendra.io",
+ uploads: false,
+ configs: ["extra/ddex/ern4-import.json"],
+ },
},
- 'copyright-hub': {
- 'adapter': {
- 'name': 'copyright-hub',
- 'title': 'Copyright Hub',
+ "copyright-hub": {
+ adapter: {
+ name: "copyright-hub",
+ title: "Copyright Hub",
// tslint:disable-next-line:max-line-length
- 'description': 'The Copyright Hub aims to help copyright work the way the internet works by making the process of giving and getting permission – the basic building block of the copyright process – fit for purpose in the age of the Internet.',
- 'infoUrl': 'http://www.copyrighthub.org',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': true
- }
+ description:
+ "The Copyright Hub aims to help copyright work the way the internet works by making the process of giving and getting permission – the basic building block of the copyright process – fit for purpose in the age of the Internet.",
+ infoUrl: "http://www.copyrighthub.org",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: true,
+ },
},
- 'unsplash': {
- 'adapter': {
- 'name': 'unsplash',
- 'title': 'Unsplash',
+ unsplash: {
+ adapter: {
+ name: "unsplash",
+ title: "Unsplash",
// tslint:disable-next-line:max-line-length
- 'description': 'Beautiful high quality free images and photos you can download and use for any project. No attribution required.',
- 'infoUrl': 'https://unsplash.com/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': true,
- 'swagger': 'extra/unsplash/swagger.json'
- }
+ description:
+ "Beautiful high quality free images and photos you can download and use for any project. No attribution required.",
+ infoUrl: "https://unsplash.com/",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: true,
+ swagger: "extra/unsplash/swagger.json",
+ },
},
- 'dotbc': {
- 'adapter': {
- 'name': 'dotbc',
- 'title': 'dotBC',
- 'description': 'dotBC is a new dynamic file format and supporting technology architecture, ' +
- 'designed to modernize rights management of media files globally for all participants',
- 'infoUrl': 'https://dotblockchainmusic.com/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': true
- }
+ dotbc: {
+ adapter: {
+ name: "dotbc",
+ title: "dotBC",
+ description:
+ "dotBC is a new dynamic file format and supporting technology architecture, " +
+ "designed to modernize rights management of media files globally for all participants",
+ infoUrl: "https://dotblockchainmusic.com/",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: true,
+ },
},
- 'chant': {
- 'adapter': {
- 'name': 'chant',
- 'title': 'Chant',
- 'description': 'An adapter for Chant specific features',
- 'infoUrl': 'https://chant.world/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'https://www.kendra.io',
- 'uploads': true,
- 'forms': {
- 'gig': {
- 'jsonSchema': 'extra/chant/schemas/gig.json',
- 'uiSchema': 'extra/chant/schemas/gig-ui.json'
- },
- 'chant': {
- 'jsonSchema': 'extra/chant/schemas/chant.json',
- 'uiSchema': 'extra/chant/schemas/chant-ui.json'
- }
+ chant: {
+ adapter: {
+ name: "chant",
+ title: "Chant",
+ description: "An adapter for Chant specific features",
+ infoUrl: "https://chant.world/",
+ maintainer: "Kendraio",
+ supportUrl: "https://www.kendra.io",
+ uploads: true,
+ forms: {
+ gig: {
+ jsonSchema: "extra/chant/schemas/gig.json",
+ uiSchema: "extra/chant/schemas/gig-ui.json",
+ },
+ chant: {
+ jsonSchema: "extra/chant/schemas/chant.json",
+ uiSchema: "extra/chant/schemas/chant-ui.json",
+ },
},
- 'actions': [
+ actions: [
{
- 'title': 'Add/Edit Gig',
- 'path': 'extra/chant/workflow/edit-gig.txt'
+ title: "Add/Edit Gig",
+ path: "extra/chant/workflow/edit-gig.txt",
},
{
- 'title': 'Upload Chant',
- 'path': 'extra/chant/workflow/upload-chant.txt'
- }
- ]
- }
+ title: "Upload Chant",
+ path: "extra/chant/workflow/upload-chant.txt",
+ },
+ ],
+ },
},
- 'apple-music': {
- 'adapter': {
- 'name': 'apple-music',
- 'title': 'Apple Music',
- 'description': 'Stream 45 million songs, ad-free on Apple Music. Shop HomePod, ' +
- 'AirPods and headphones. And build your entertainment collection with iPod and iTunes.',
- 'infoUrl': 'https://www.apple.com/uk/music/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': true
- }
+ "apple-music": {
+ adapter: {
+ name: "apple-music",
+ title: "Apple Music",
+ description:
+ "Stream 45 million songs, ad-free on Apple Music. Shop HomePod, " +
+ "AirPods and headphones. And build your entertainment collection with iPod and iTunes.",
+ infoUrl: "https://www.apple.com/uk/music/",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: true,
+ },
},
- 'youtube': {
- 'adapter': {
- 'name': 'youtube',
- 'title': 'YouTube',
- 'description': 'Enjoy the videos and music you love, upload original content,' +
- ' and share it all with friends, family, and the world on YouTube.',
- 'infoUrl': 'https://www.youtube.com/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': true,
- 'forms': {
- 'basic': {
- 'jsonSchema': 'extra/youtube/schemas/youtube-edit-video-basic.json',
- 'uiSchema': 'extra/youtube/schemas/youtube-edit-video-basic-ui.json'
- },
- 'advanced': {
- 'jsonSchema': 'extra/youtube/schemas/youtube-edit-video-advanced.schema.json',
- 'uiSchema': 'extra/youtube/schemas/youtube-edit-video-advanced.ui.json'
- }
- }
- }
+ youtube: {
+ adapter: {
+ name: "youtube",
+ title: "YouTube",
+ description:
+ "Enjoy the videos and music you love, upload original content," +
+ " and share it all with friends, family, and the world on YouTube.",
+ infoUrl: "https://www.youtube.com/",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: true,
+ forms: {
+ basic: {
+ jsonSchema:
+ "extra/youtube/schemas/youtube-edit-video-basic.json",
+ uiSchema:
+ "extra/youtube/schemas/youtube-edit-video-basic-ui.json",
+ },
+ advanced: {
+ jsonSchema:
+ "extra/youtube/schemas/youtube-edit-video-advanced.schema.json",
+ uiSchema:
+ "extra/youtube/schemas/youtube-edit-video-advanced.ui.json",
+ },
+ },
+ },
},
- 'teosto': {
- 'adapter': {
- 'name': 'teosto',
- 'title': 'Teosto',
- 'description': 'Teosto is a non-profit performance rights organization that collects' +
- ' royalties on behalf of songwriters and composers in Finland.',
- 'infoUrl': 'https://www.teosto.fi/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': false,
- 'configs': [
- 'extra/teosto/configs/listWorks.json',
- 'extra/teosto/configs/authorsWorksByName.json',
- 'extra/teosto/configs/authorsWorksFromIPI.json',
- 'extra/teosto/configs/getRightsHolderForISWC.json',
- 'extra/teosto/configs/listPublishersWorks.json',
- 'extra/teosto/configs/listWorksSelectionExample.json'
- ]
- }
+ teosto: {
+ adapter: {
+ name: "teosto",
+ title: "Teosto",
+ description:
+ "Teosto is a non-profit performance rights organization that collects" +
+ " royalties on behalf of songwriters and composers in Finland.",
+ infoUrl: "https://www.teosto.fi/",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: false,
+ configs: [
+ "extra/teosto/configs/listWorks.json",
+ "extra/teosto/configs/authorsWorksByName.json",
+ "extra/teosto/configs/authorsWorksFromIPI.json",
+ "extra/teosto/configs/getRightsHolderForISWC.json",
+ "extra/teosto/configs/listPublishersWorks.json",
+ "extra/teosto/configs/listWorksSelectionExample.json",
+ ],
+ },
},
- 'spotify': {
- 'adapter': {
- 'name': 'spotify',
- 'title': 'Spotify',
- 'description': 'Spotify is a digital music service that gives you access to millions of songs.',
- 'infoUrl': 'https://www.spotify.com/uk/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': true
- }
+ spotify: {
+ adapter: {
+ name: "spotify",
+ title: "Spotify",
+ description:
+ "Spotify is a digital music service that gives you access to millions of songs.",
+ infoUrl: "https://www.spotify.com/uk/",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: true,
+ },
},
- 'schema': {
- 'adapter': {
- 'name': 'schema',
- 'title': 'schema.org',
- 'description': 'Schema.org is a collaborative, community activity with a mission to create, ' +
- 'maintain, and promote schemas for structured data on the Internet, on web pages, in email messages, and beyond.',
- 'infoUrl': 'https://schema.org/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'https://www.kendra.io',
- 'uploads': false,
- 'forms': {
- 'addImage': {
- 'jsonSchema': 'extra/schema.org/schemas/imageObject.json',
- 'uiSchema': 'extra/schema.org/schemas/addImage-ui.json'
- },
- 'editImage': {
- 'jsonSchema': 'extra/schema.org/schemas/imageObject.json',
- 'uiSchema': 'extra/schema.org/schemas/editImage-ui.json'
- }
+ schema: {
+ adapter: {
+ name: "schema",
+ title: "schema.org",
+ description:
+ "Schema.org is a collaborative, community activity with a mission to create, " +
+ "maintain, and promote schemas for structured data on the Internet, on web pages, in email messages, and beyond.",
+ infoUrl: "https://schema.org/",
+ maintainer: "Kendraio",
+ supportUrl: "https://www.kendra.io",
+ uploads: false,
+ forms: {
+ addImage: {
+ jsonSchema: "extra/schema.org/schemas/imageObject.json",
+ uiSchema: "extra/schema.org/schemas/addImage-ui.json",
+ },
+ editImage: {
+ jsonSchema: "extra/schema.org/schemas/imageObject.json",
+ uiSchema: "extra/schema.org/schemas/editImage-ui.json",
+ },
},
- 'rootNodeTypes': [],
- 'nodeTypes': [
- 'Person',
- 'ImageObject'
- ],
- 'linkTypes': []
+ rootNodeTypes: [],
+ nodeTypes: ["Person", "ImageObject"],
+ linkTypes: [],
},
- 'schemas': {
- 'Person': {
- 'context': 'http://schema.org',
- 'type': 'Person',
- 'label': 'Person',
- 'icon': 'person',
- 'fields': [
+ schemas: {
+ Person: {
+ context: "http://schema.org",
+ type: "Person",
+ label: "Person",
+ icon: "person",
+ fields: [
{
- 'name': 'name',
- 'label': 'Name',
- 'type': 'string',
- 'widget': 'textfield'
- }
- ]
+ name: "name",
+ label: "Name",
+ type: "string",
+ widget: "textfield",
+ },
+ ],
},
- 'ImageObject': {
- 'context': 'http://schema.org',
- 'type': 'ImageObject',
- 'label': 'Image',
- 'icon': 'photo_camera',
- 'labelField': 'contentUrl',
- 'fields': [
+ ImageObject: {
+ context: "http://schema.org",
+ type: "ImageObject",
+ label: "Image",
+ icon: "photo_camera",
+ labelField: "contentUrl",
+ fields: [
{
- 'name': 'contentUrl',
- 'label': 'Image URL',
- 'type': 'string',
- 'widget': 'imageTagger',
- 'required': true,
- 'widthField': 'width',
- 'heightField': 'height'
+ name: "contentUrl",
+ label: "Image URL",
+ type: "string",
+ widget: "imageTagger",
+ required: true,
+ widthField: "width",
+ heightField: "height",
},
{
- 'name': 'name',
- 'label': 'Filename',
- 'type': 'string',
- 'widget': 'textfield'
+ name: "name",
+ label: "Filename",
+ type: "string",
+ widget: "textfield",
},
{
- 'name': 'description',
- 'label': 'Description',
- 'widget': 'textarea'
+ name: "description",
+ label: "Description",
+ widget: "textarea",
},
{
- 'name': 'url',
- 'label': 'Source URL',
- 'type': 'string',
- 'widget': 'textfield',
- 'required': true
+ name: "url",
+ label: "Source URL",
+ type: "string",
+ widget: "textfield",
+ required: true,
},
{
- 'name': 'width',
- 'label': 'Width',
- 'type': 'integer',
- 'widget': 'hidden'
+ name: "width",
+ label: "Width",
+ type: "integer",
+ widget: "hidden",
},
{
- 'name': 'height',
- 'label': 'Height',
- 'type': 'integer',
- 'widget': 'hidden'
- }
- ]
- }
- }
+ name: "height",
+ label: "Height",
+ type: "integer",
+ widget: "hidden",
+ },
+ ],
+ },
+ },
},
- 'ppl-ipn': {
- 'adapter': {
- 'name': 'ppl-ipn',
- 'title': 'PPL IPN',
- 'description': 'Verification of International Performer Number (IPN) for PPL members',
- 'infoUrl': 'https://www.ppluk.com/membership/more-information/ipn/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'https://www.kendra.io',
- 'uploads': false,
- 'actions': [
+ "ppl-ipn": {
+ adapter: {
+ name: "ppl-ipn",
+ title: "PPL IPN",
+ description:
+ "Verification of International Performer Number (IPN) for PPL members",
+ infoUrl: "https://www.ppluk.com/membership/more-information/ipn/",
+ maintainer: "Kendraio",
+ supportUrl: "https://www.kendra.io",
+ uploads: false,
+ actions: [
{
- 'title': 'PPL IPN Process',
- 'path': 'extra/ppl-ipn/ipn.txt'
- }
+ title: "PPL IPN Process",
+ path: "extra/ppl-ipn/ipn.txt",
+ },
],
- 'forms': {
- 'ipn': {
- 'jsonSchema': 'extra/ppl-ipn/schemas/ipn-jsonSchema.json',
- 'uiSchema': 'extra/ppl-ipn/schemas/ipn-uiSchema.json'
- }
- }
- }
+ forms: {
+ ipn: {
+ jsonSchema: "extra/ppl-ipn/schemas/ipn-jsonSchema.json",
+ uiSchema: "extra/ppl-ipn/schemas/ipn-uiSchema.json",
+ },
+ },
+ },
},
- 'bandcamp': {
- 'adapter': {
- 'name': 'bandcamp',
- 'title': 'Bandcamp',
- 'description': 'Discover amazing music and directly support the artists who make it.',
- 'infoUrl': 'https://bandcamp.com/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': true
- }
+ bandcamp: {
+ adapter: {
+ name: "bandcamp",
+ title: "Bandcamp",
+ description:
+ "Discover amazing music and directly support the artists who make it.",
+ infoUrl: "https://bandcamp.com/",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: true,
+ },
},
- 'm-rin': {
- 'adapter': {
- 'name': 'm-rin',
- 'title': 'Recording Information Notification (RIN)',
- 'description': 'This adapter defines the schema and parser for the DDEX M-RIN (Minimum) ' +
- 'Recording Information Notification standard.\nThis adapter enables support for import ' +
- 'and export of M-RIN data, as well as the core entities for storage of projects, people,' +
- ' recordings, and works.\n',
- 'infoUrl': 'http://www.ddex.net/recording-information-notification-rin',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'source': {
- 'parser': 'xml',
- 'parserConfig': {
- 'arrayAccessFormPaths': [
- '.\\.Party$',
- '.\\.PostalAddress$',
- '.\\.PhoneNumber$',
- '.\\.EmailAddress$',
- '.\\.MusicalWork$',
- '.\\.SoundRecording$',
- '.\\.Project$',
- '.\\.Session$',
- '.\\.SoundRecordingSessionReference$',
- '.\\.ContributorReference$',
- '.\\.SessionSoundRecordingReference$'
- ]
- },
- 'extractor': 'extractor.jmespath'
+ "m-rin": {
+ adapter: {
+ name: "m-rin",
+ title: "Recording Information Notification (RIN)",
+ description:
+ "This adapter defines the schema and parser for the DDEX M-RIN (Minimum) " +
+ "Recording Information Notification standard.\nThis adapter enables support for import " +
+ "and export of M-RIN data, as well as the core entities for storage of projects, people," +
+ " recordings, and works.\n",
+ infoUrl:
+ "http://www.ddex.net/recording-information-notification-rin",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ source: {
+ parser: "xml",
+ parserConfig: {
+ arrayAccessFormPaths: [
+ ".\\.Party$",
+ ".\\.PostalAddress$",
+ ".\\.PhoneNumber$",
+ ".\\.EmailAddress$",
+ ".\\.MusicalWork$",
+ ".\\.SoundRecording$",
+ ".\\.Project$",
+ ".\\.Session$",
+ ".\\.SoundRecordingSessionReference$",
+ ".\\.ContributorReference$",
+ ".\\.SessionSoundRecordingReference$",
+ ],
+ },
+ extractor: "extractor.jmespath",
},
- 'rootNodeType': 'Project',
- 'nodeTypes': [
- 'Project',
- 'Party',
- 'MusicalWork',
- 'Session',
- 'Resource'
+ rootNodeType: "Project",
+ nodeTypes: [
+ "Project",
+ "Party",
+ "MusicalWork",
+ "Session",
+ "Resource",
],
- 'linkTypes': [
- 'ContributorReference',
- 'MainArtist',
- 'Label'
- ],
- 'schemas': {
- 'Project': {
- 'options': {
- 'labelField': 'name'
- },
- 'properties': {
- 'name': {
- 'type': 'string',
- 'options': {
- 'label': 'Name'
- }
+ linkTypes: ["ContributorReference", "MainArtist", "Label"],
+ schemas: {
+ Project: {
+ options: {
+ labelField: "name",
+ },
+ properties: {
+ name: {
+ type: "string",
+ options: {
+ label: "Name",
+ },
+ },
+ artist: {
+ type: "string",
+ options: {
+ label: "Artist Name",
+ },
+ },
+ comment: {
+ type: "string",
+ options: {
+ fieldType: "TEXTAREA",
+ label: "Comment",
+ },
},
- 'artist': {
- 'type': 'string',
- 'options': {
- 'label': 'Artist Name'
- }
+ },
+ links: ["MainArtist", "Label", "ContributorReference"],
+ },
+ Party: {
+ options: {
+ labelField: "FullName",
+ },
+ properties: {
+ FullName: {
+ type: "string",
+ },
+ FullNameIndexed: {
+ type: "string",
},
- 'comment': {
- 'type': 'string',
- 'options': {
- 'fieldType': 'TEXTAREA',
- 'label': 'Comment'
- }
- }
- },
- 'links': [
- 'MainArtist',
- 'Label',
- 'ContributorReference'
- ]
- },
- 'Party': {
- 'options': {
- 'labelField': 'FullName'
- },
- 'properties': {
- 'FullName': {
- 'type': 'string'
+ ISNI: {
+ type: "string",
},
- 'FullNameIndexed': {
- 'type': 'string'
+ IsOrganization: {
+ type: "boolean",
+ options: {
+ fieldType: "SWITCH",
+ },
+ },
+ },
+ },
+ MusicalWork: {
+ options: {
+ labelField: "name",
+ },
+ properties: {
+ name: {
+ type: "string",
},
- 'ISNI': {
- 'type': 'string'
+ Comment: {
+ type: "string",
+ options: {
+ fieldType: "TEXTAREA",
+ },
},
- 'IsOrganization': {
- 'type': 'boolean',
- 'options': {
- 'fieldType': 'SWITCH'
- }
- }
- }
- },
- 'MusicalWork': {
- 'options': {
- 'labelField': 'name'
- },
- 'properties': {
- 'name': {
- 'type': 'string'
+ },
+ },
+ ContributorReference: {
+ properties: {
+ role: {
+ type: "string",
},
- 'Comment': {
- 'type': 'string',
- 'options': {
- 'fieldType': 'TEXTAREA'
- }
- }
- }
- },
- 'ContributorReference': {
- 'properties': {
- 'role': {
- 'type': 'string'
- }
- }
- }
- }
+ },
+ },
+ },
},
- 'schemas': {
- 'Party': {
- 'name': 'Party',
- 'description': 'A Composite containing details of a Party.',
- 'id': 'm-rin:Party',
- 'type': 'object',
- 'required': [
- 'PartyId',
- 'PartyReference',
- 'IsOrganization'
- ],
- 'properties': {
- 'PartyId': {
- 'description': 'A Composite containing details of the PartyId for the Party. ' +
- 'If no Namespace is given, the Identifier is a DdexPartyId (DPID). ' +
- 'Note that DPIDs are not normally used to identify Artists, producers or other Creators.\n',
- 'type': 'array',
- 'minItems': 1,
- 'items': {
- '$ref': 'm-rin:DetailedPartyId'
- }
- },
- 'PartyReference': {
- 'description': 'The Identifier (specific to the Message) of the Party. This is' +
- ' a LocalPartyAnchor starting with the letter P.\n',
- 'type': 'string',
- 'pattern': '^P[\\d\\-_a-zA-Z]+$'
- },
- 'PartyName': {
- 'description': 'A Composite containing details of the PartyName(s).',
- 'type': 'array',
- 'items': {
- '$ref': 'm-rin:PartyName'
- }
- },
- 'Publisher': {
- 'type': 'array',
- 'description': 'A Composite containing details of the MusicPublisher the Party is signed to.',
- 'items': {
- '$ref': 'm-rin:Affiliation'
- }
- },
- 'AuthorsSociety': {
- 'type': 'array',
- 'description': 'A Composite containing details of the AuthorsSociety the Party is a member of.',
- 'items': {
- '$ref': 'm-rin:Affiliation'
- }
- },
- 'Label': {
- 'description': 'A Composite containing details of the Label the Party is signed to.',
- 'type': 'array',
- 'items': {
- '$ref': 'm-rin:Affiliation'
- }
- },
- 'MusicLicensingCompany': {
- 'description': 'A Composite containing details of the MusicLicensingCompany the Party is a member of.',
- 'type': 'array',
- 'items': {
- '$ref': 'm-rin:Affiliation'
- }
- },
- 'Sex': {
- '$ref': 'avs:Sex',
- 'description': 'The sex of the Party.'
- },
- 'TerritoryOfResidency': {
- '$ref': 'm-rin:AllTerritoryCode',
- 'description': 'The country of main residency of the Party.'
- },
- 'GoverningAgreementType': {
- '$ref': 'm-rin:GoverningAgreementType',
- 'description': 'A Composite containing details of a Type of an agreement that ' +
- 'covers the Party\'s participation in making a SoundRecording.'
- },
- 'ArtistDelegatedUsageRights': {
- '$ref': 'm-rin:ArtistDelegatedUsageRights',
- 'description': 'A Composite containing details of the kinds of usage for which' +
- ' rights have been delegated by the Party.'
- },
- 'IsOrganization': {
- 'description': 'A Flag indicating whether the Party is an Organization (=true) or a Person (=false).',
- 'type': 'boolean'
- },
- 'PostalAddress': {
- 'description': 'A Composite containing details of a PostalAddress of the Party.',
- 'type': 'array',
- 'items': {
- '$ref': 'm-rin:PostalAddress'
- }
- },
- 'PhoneNumber': {
- 'description': 'A Composite containing details of a PhoneNumber of the Party.',
- 'type': 'array',
- 'items': {
- '$ref': 'm-rin:PhoneNumber'
- }
- },
- 'EmailAddress': {
- 'description': 'A Composite containing details of an EmailAddress of the Party.',
- 'type': 'array',
- 'items': {
- '$ref': 'm-rin:EmailAddress'
- }
- },
- 'Nationality': {
- '$ref': 'm-rin:CurrentTerritoryCode',
- 'description': 'The nationality of the Party.'
- },
- 'DateAndPlaceOfBirth': {
- '$ref': 'm-rin:EventDate',
- 'description': 'A Composite containing details of the Date and Place of birth. ' +
- 'This is a string with the syntax YYYY[-MM[-DD]].'
- },
- 'DateAndPlaceOfDeath': {
- '$ref': 'm-rin:EventDate',
- 'description': 'A Composite containing details of the Date and Place of death. This ' +
- 'is a string with the syntax YYYY[-MM[-DD]].'
- }
- }
+ schemas: {
+ Party: {
+ name: "Party",
+ description: "A Composite containing details of a Party.",
+ id: "m-rin:Party",
+ type: "object",
+ required: ["PartyId", "PartyReference", "IsOrganization"],
+ properties: {
+ PartyId: {
+ description:
+ "A Composite containing details of the PartyId for the Party. " +
+ "If no Namespace is given, the Identifier is a DdexPartyId (DPID). " +
+ "Note that DPIDs are not normally used to identify Artists, producers or other Creators.\n",
+ type: "array",
+ minItems: 1,
+ items: {
+ $ref: "m-rin:DetailedPartyId",
+ },
+ },
+ PartyReference: {
+ description:
+ "The Identifier (specific to the Message) of the Party. This is" +
+ " a LocalPartyAnchor starting with the letter P.\n",
+ type: "string",
+ pattern: "^P[\\d\\-_a-zA-Z]+$",
+ },
+ PartyName: {
+ description:
+ "A Composite containing details of the PartyName(s).",
+ type: "array",
+ items: {
+ $ref: "m-rin:PartyName",
+ },
+ },
+ Publisher: {
+ type: "array",
+ description:
+ "A Composite containing details of the MusicPublisher the Party is signed to.",
+ items: {
+ $ref: "m-rin:Affiliation",
+ },
+ },
+ AuthorsSociety: {
+ type: "array",
+ description:
+ "A Composite containing details of the AuthorsSociety the Party is a member of.",
+ items: {
+ $ref: "m-rin:Affiliation",
+ },
+ },
+ Label: {
+ description:
+ "A Composite containing details of the Label the Party is signed to.",
+ type: "array",
+ items: {
+ $ref: "m-rin:Affiliation",
+ },
+ },
+ MusicLicensingCompany: {
+ description:
+ "A Composite containing details of the MusicLicensingCompany the Party is a member of.",
+ type: "array",
+ items: {
+ $ref: "m-rin:Affiliation",
+ },
+ },
+ Sex: {
+ $ref: "avs:Sex",
+ description: "The sex of the Party.",
+ },
+ TerritoryOfResidency: {
+ $ref: "m-rin:AllTerritoryCode",
+ description: "The country of main residency of the Party.",
+ },
+ GoverningAgreementType: {
+ $ref: "m-rin:GoverningAgreementType",
+ description:
+ "A Composite containing details of a Type of an agreement that " +
+ "covers the Party's participation in making a SoundRecording.",
+ },
+ ArtistDelegatedUsageRights: {
+ $ref: "m-rin:ArtistDelegatedUsageRights",
+ description:
+ "A Composite containing details of the kinds of usage for which" +
+ " rights have been delegated by the Party.",
+ },
+ IsOrganization: {
+ description:
+ "A Flag indicating whether the Party is an Organization (=true) or a Person (=false).",
+ type: "boolean",
+ },
+ PostalAddress: {
+ description:
+ "A Composite containing details of a PostalAddress of the Party.",
+ type: "array",
+ items: {
+ $ref: "m-rin:PostalAddress",
+ },
+ },
+ PhoneNumber: {
+ description:
+ "A Composite containing details of a PhoneNumber of the Party.",
+ type: "array",
+ items: {
+ $ref: "m-rin:PhoneNumber",
+ },
+ },
+ EmailAddress: {
+ description:
+ "A Composite containing details of an EmailAddress of the Party.",
+ type: "array",
+ items: {
+ $ref: "m-rin:EmailAddress",
+ },
+ },
+ Nationality: {
+ $ref: "m-rin:CurrentTerritoryCode",
+ description: "The nationality of the Party.",
+ },
+ DateAndPlaceOfBirth: {
+ $ref: "m-rin:EventDate",
+ description:
+ "A Composite containing details of the Date and Place of birth. " +
+ "This is a string with the syntax YYYY[-MM[-DD]].",
+ },
+ DateAndPlaceOfDeath: {
+ $ref: "m-rin:EventDate",
+ description:
+ "A Composite containing details of the Date and Place of death. This " +
+ "is a string with the syntax YYYY[-MM[-DD]].",
+ },
+ },
},
- 'ProprietaryId': {
- 'name': 'ProprietaryId',
- 'description': 'A Composite containing details of a ProprietaryIdentifier.',
- 'id': 'm-rin:ProprietaryId',
- 'type': 'object',
- 'required': [
- 'ProprietaryId',
- 'Namespace'
- ],
- 'properties': {
- 'ProprietaryId': {
- 'type': 'string'
- },
- 'Namespace': {
- 'description': 'The Namespace of the ProprietaryIdentifier. This is represented in an' +
- ' XML schema as an XML Attribute.',
- 'type': 'string'
- }
- }
+ ProprietaryId: {
+ name: "ProprietaryId",
+ description:
+ "A Composite containing details of a ProprietaryIdentifier.",
+ id: "m-rin:ProprietaryId",
+ type: "object",
+ required: ["ProprietaryId", "Namespace"],
+ properties: {
+ ProprietaryId: {
+ type: "string",
+ },
+ Namespace: {
+ description:
+ "The Namespace of the ProprietaryIdentifier. This is represented in an" +
+ " XML schema as an XML Attribute.",
+ type: "string",
+ },
+ },
},
- 'PartyName': {
- 'name': 'PartyName',
- 'description': 'A Composite containing details of a PartyName. Name details for a Party ' +
- 'typically either contain a FullName or a KeyName.\n',
- 'id': 'm-rin:PartyName',
- 'type': 'object',
- 'required': [
- 'FullName'
- ],
- 'properties': {
- 'FullName': {
- '$ref': 'm-rin:Name',
- 'description': 'A Composite containing the complete Name of the Party, in its normal ' +
- 'form of presentation (e.g. John H. Smith, Acme Music Inc, the Beatles).'
- },
- 'FullNameAsciiTranscribed': {
- 'type': 'string',
- 'description': 'The FullName transcribed using 7-bit ASCII code.'
- },
- 'FullNameIndexed': {
- '$ref': 'm-rin:Name',
- 'description': 'A Composite containing the complete Name of the Party in the form in ' +
- 'which it normally appears in an alphabetic index, with the KeyName first (e.g. Smith,' +
- ' John H.; Beatles, The).'
- },
- 'NamesBeforeKeyName': {
- '$ref': 'm-rin:Name',
- 'description': 'A Composite containing the Name(s) preceding the KeyName in the ' +
- 'FullName (and that is placed after it in a FullNameIndexed). Examples: \'George\' ' +
- 'in \'George Michael\'; \'John Fitzgerald\' in \'John Fitzgerald Kennedy\'. ' +
- 'Not all PartyNames have a NamesBeforeKeyName (e.g. Madonna, EMI Music Inc).'
- },
- 'KeyName': {
- '$ref': 'm-rin:Name',
- 'description': 'A Composite containing the Part of a Name of the Party normally used to' +
- ' index an entry in an alphabetical list, such as \'Smith\' (in John Smith) or' +
- ' \'Garcia Marquez\' or \'Madonna\' or \'Francis de Sales\' (in Saint Francis de Sales). ' +
- 'For persons, this normally corresponds to the \'family name\' or names, which in ' +
- 'Western name forms usually comes as a surname at the end of a FullName, and in Asian ' +
- 'name forms often at the beginning of a FullName.'
- },
- 'NamesAfterKeyName': {
- '$ref': 'm-rin:Name',
- 'description': 'A Composite containing the Name(s) following the KeyName.' +
- ' Example:\'Ibrahim\' (in Anwar Ibrahim). This is common, e.g., in many Asian ' +
- 'personal name forms where a FullName begins with the KeyName, which is followed ' +
- 'by other names.'
- },
- 'AbbreviatedName': {
- '$ref': 'm-rin:Name',
- 'description': 'A Composite containing a short version of the PartyName (e.g. for use' +
- ' on devices with a small display).'
- },
- 'LanguageAndScriptCode': {
- 'type': 'string',
- 'description': 'The Language and script for the Elements of the PartyName as ' +
- 'defined in IETF RfC 5646. The default is the same as indicated for the containing ' +
- 'composite. Language and Script are provided as lang[-scipt][-region][-variant]. ' +
- 'This is represented in an XML schema as an XML Attribute.'
- }
- }
+ PartyName: {
+ name: "PartyName",
+ description:
+ "A Composite containing details of a PartyName. Name details for a Party " +
+ "typically either contain a FullName or a KeyName.\n",
+ id: "m-rin:PartyName",
+ type: "object",
+ required: ["FullName"],
+ properties: {
+ FullName: {
+ $ref: "m-rin:Name",
+ description:
+ "A Composite containing the complete Name of the Party, in its normal " +
+ "form of presentation (e.g. John H. Smith, Acme Music Inc, the Beatles).",
+ },
+ FullNameAsciiTranscribed: {
+ type: "string",
+ description:
+ "The FullName transcribed using 7-bit ASCII code.",
+ },
+ FullNameIndexed: {
+ $ref: "m-rin:Name",
+ description:
+ "A Composite containing the complete Name of the Party in the form in " +
+ "which it normally appears in an alphabetic index, with the KeyName first (e.g. Smith," +
+ " John H.; Beatles, The).",
+ },
+ NamesBeforeKeyName: {
+ $ref: "m-rin:Name",
+ description:
+ "A Composite containing the Name(s) preceding the KeyName in the " +
+ "FullName (and that is placed after it in a FullNameIndexed). Examples: 'George' " +
+ "in 'George Michael'; 'John Fitzgerald' in 'John Fitzgerald Kennedy'. " +
+ "Not all PartyNames have a NamesBeforeKeyName (e.g. Madonna, EMI Music Inc).",
+ },
+ KeyName: {
+ $ref: "m-rin:Name",
+ description:
+ "A Composite containing the Part of a Name of the Party normally used to" +
+ " index an entry in an alphabetical list, such as 'Smith' (in John Smith) or" +
+ " 'Garcia Marquez' or 'Madonna' or 'Francis de Sales' (in Saint Francis de Sales). " +
+ "For persons, this normally corresponds to the 'family name' or names, which in " +
+ "Western name forms usually comes as a surname at the end of a FullName, and in Asian " +
+ "name forms often at the beginning of a FullName.",
+ },
+ NamesAfterKeyName: {
+ $ref: "m-rin:Name",
+ description:
+ "A Composite containing the Name(s) following the KeyName." +
+ " Example:'Ibrahim' (in Anwar Ibrahim). This is common, e.g., in many Asian " +
+ "personal name forms where a FullName begins with the KeyName, which is followed " +
+ "by other names.",
+ },
+ AbbreviatedName: {
+ $ref: "m-rin:Name",
+ description:
+ "A Composite containing a short version of the PartyName (e.g. for use" +
+ " on devices with a small display).",
+ },
+ LanguageAndScriptCode: {
+ type: "string",
+ description:
+ "The Language and script for the Elements of the PartyName as " +
+ "defined in IETF RfC 5646. The default is the same as indicated for the containing " +
+ "composite. Language and Script are provided as lang[-scipt][-region][-variant]. " +
+ "This is represented in an XML schema as an XML Attribute.",
+ },
+ },
},
- 'DetailedPartyId': {
- 'name': 'DetailedPartyId',
- 'description': 'A Composite containing details of a PartyId.',
- 'id': 'm-rin:DetailedPartyId',
- 'type': 'object',
- 'properties': {
- 'ISNI': {
- 'description': 'An International Standard Name Identifier, the ISO 27729 Standard ' +
- 'Identifier for names. DDEX will enforce the syntax [0-9]{15}[X0-9] using XML ' +
- 'Schema in the future.\n',
- 'type': 'string'
- },
- 'DPID': {
- 'description': 'An Identifier of a Party according to the DdexPartyId standard DDEX-DPID.',
- 'type': 'string',
- 'pattern': 'PADPIDA[a-zA-Z0-9]+'
- },
- 'IpiNameNumber': {
- 'description': 'An Interested Party Identifier, a CISAC standard Identifier.',
- 'type': 'string'
- },
- 'IPN': {
- 'description': 'An International Performer Number, an IPDA Identifier (http://www2.ipddb.org/content/ipd-project).\n',
- 'type': 'string'
- },
- 'CisacSocietyId': {
- 'description': 'A CISAC Society Identifier, a CISAC standard Identifier for music rights societies.',
- 'type': 'string'
- },
- 'ProprietaryId': {
- 'description': 'A Composite containing details of a ProprietaryIdentifier of the Party.',
- 'type': 'array',
- 'items': {
- '$ref': 'm-rin:ProprietaryId'
- }
- }
- }
+ DetailedPartyId: {
+ name: "DetailedPartyId",
+ description: "A Composite containing details of a PartyId.",
+ id: "m-rin:DetailedPartyId",
+ type: "object",
+ properties: {
+ ISNI: {
+ description:
+ "An International Standard Name Identifier, the ISO 27729 Standard " +
+ "Identifier for names. DDEX will enforce the syntax [0-9]{15}[X0-9] using XML " +
+ "Schema in the future.\n",
+ type: "string",
+ },
+ DPID: {
+ description:
+ "An Identifier of a Party according to the DdexPartyId standard DDEX-DPID.",
+ type: "string",
+ pattern: "PADPIDA[a-zA-Z0-9]+",
+ },
+ IpiNameNumber: {
+ description:
+ "An Interested Party Identifier, a CISAC standard Identifier.",
+ type: "string",
+ },
+ IPN: {
+ description:
+ "An International Performer Number, an IPDA Identifier (http://www2.ipddb.org/content/ipd-project).\n",
+ type: "string",
+ },
+ CisacSocietyId: {
+ description:
+ "A CISAC Society Identifier, a CISAC standard Identifier for music rights societies.",
+ type: "string",
+ },
+ ProprietaryId: {
+ description:
+ "A Composite containing details of a ProprietaryIdentifier of the Party.",
+ type: "array",
+ items: {
+ $ref: "m-rin:ProprietaryId",
+ },
+ },
+ },
},
- 'Name': {
- 'name': 'Name',
- 'description': 'A Composite containing details of a Name.',
- 'id': 'm-rin:Name',
- 'type': 'object',
- 'required': [
- 'Name'
- ],
- 'properties': {
- 'Name': {
- 'type': 'string'
- },
- 'LanguageAndScriptCode': {
- 'type': 'string',
- 'description': 'The Language and script of the Name as defined in IETF RfC 5646. ' +
- 'The default is the same as indicated for the containing composite. Language and ' +
- 'Script are provided as lang[-scipt][-region][-variant]. This is represented in ' +
- 'an XML schema as an XML Attribute.'
- }
- }
- }
- }
+ Name: {
+ name: "Name",
+ description: "A Composite containing details of a Name.",
+ id: "m-rin:Name",
+ type: "object",
+ required: ["Name"],
+ properties: {
+ Name: {
+ type: "string",
+ },
+ LanguageAndScriptCode: {
+ type: "string",
+ description:
+ "The Language and script of the Name as defined in IETF RfC 5646. " +
+ "The default is the same as indicated for the containing composite. Language and " +
+ "Script are provided as lang[-scipt][-region][-variant]. This is represented in " +
+ "an XML schema as an XML Attribute.",
+ },
+ },
+ },
+ },
},
- 'example': {
- 'adapter': {
- 'name': 'example',
- 'title': 'Example',
- 'description': 'An adapter for examples and demonstrations.',
- 'infoUrl': 'https://www.example.com/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'https://www.kendra.io',
- 'uploads': false,
- 'configs': [
- 'extra/example/configs/productChart.json',
- 'extra/example/configs/customFormElements.json'
+ example: {
+ adapter: {
+ name: "example",
+ title: "Example",
+ description: "An adapter for examples and demonstrations.",
+ infoUrl: "https://www.example.com/",
+ maintainer: "Kendraio",
+ supportUrl: "https://www.kendra.io",
+ uploads: false,
+ configs: [
+ "extra/example/configs/productChart.json",
+ "extra/example/configs/customFormElements.json",
],
- 'forms': {
- 'customElements': {
- 'jsonSchema': 'extra/example/schemas/customElements.schema.json',
- 'uiSchema': 'extra/example/schemas/customElements.ui.json'
- }
- }
- }
+ forms: {
+ customElements: {
+ jsonSchema: "extra/example/schemas/customElements.schema.json",
+ uiSchema: "extra/example/schemas/customElements.ui.json",
+ },
+ },
+ },
},
- 'resonate': {
- 'adapter': {
- 'name': 'resonate',
- 'title': 'Resonate',
- 'description': 'Resonate Co-operative is a community owned music streaming platform',
- 'infoUrl': 'https://resonate.is/',
- 'maintainer': 'Resonate',
- 'supportUrl': 'https://kendra.io/'
- }
+ resonate: {
+ adapter: {
+ name: "resonate",
+ title: "Resonate",
+ description:
+ "Resonate Co-operative is a community owned music streaming platform",
+ infoUrl: "https://resonate.is/",
+ maintainer: "Resonate",
+ supportUrl: "https://kendra.io/",
+ },
},
- 'tidal': {
- 'adapter': {
- 'name': 'tidal',
- 'title': 'TIDAL',
- 'description': 'TIDAL is the first music service with High Fidelity sound quality, ' +
- 'High Definition music videos and Curated Editorial, expertly crafted by music journalists.',
- 'infoUrl': 'http://tidal.com/gb',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': true
- }
+ tidal: {
+ adapter: {
+ name: "tidal",
+ title: "TIDAL",
+ description:
+ "TIDAL is the first music service with High Fidelity sound quality, " +
+ "High Definition music videos and Curated Editorial, expertly crafted by music journalists.",
+ infoUrl: "http://tidal.com/gb",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: true,
+ },
},
- 'omi': {
- 'adapter': {
- 'name': 'omi',
- 'title': 'OMI',
- 'description': 'We are a non-profit initiative creating an open-source protocol for ' +
- 'the uniform identification of music rights holders and creators.',
- 'infoUrl': 'http://open-music.org/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': true
- }
+ omi: {
+ adapter: {
+ name: "omi",
+ title: "OMI",
+ description:
+ "We are a non-profit initiative creating an open-source protocol for " +
+ "the uniform identification of music rights holders and creators.",
+ infoUrl: "http://open-music.org/",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: true,
+ },
},
- 'kendraio': {
- 'adapter': {
- 'name': 'kendraio',
- 'title': 'Kendraio',
- 'description': 'An adapter for Kendraio specific features',
- 'infoUrl': 'https://www.kendra.io/',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'https://www.kendra.io',
- 'uploads': false,
- 'database': [
- 'extra/kendraio/schemas/music_recording.schema.json',
- 'extra/kendraio/schemas/music_work.schema.json',
- 'extra/kendraio/schemas/file.schema.json'
+ kendraio: {
+ adapter: {
+ name: "kendraio",
+ title: "Kendraio",
+ description: "An adapter for Kendraio specific features",
+ infoUrl: "https://www.kendra.io/",
+ maintainer: "Kendraio",
+ supportUrl: "https://www.kendra.io",
+ uploads: false,
+ database: [
+ "extra/kendraio/schemas/music_recording.schema.json",
+ "extra/kendraio/schemas/music_work.schema.json",
+ "extra/kendraio/schemas/file.schema.json",
],
- 'forms': {
- 'edit_music_recording': {
- 'jsonSchema': 'extra/kendraio/schemas/music_recording.schema.json',
- 'uiSchema': 'extra/kendraio/schemas/music_recording.ui.json'
- },
- 'edit_music_work': {
- 'jsonSchema': 'extra/kendraio/schemas/music_work.schema.json',
- 'uiSchema': 'extra/kendraio/schemas/music_work.ui.json'
- },
- 'edit_file': {
- 'jsonSchema': 'extra/kendraio/schemas/file.schema.json',
- 'uiSchema': 'extra/kendraio/schemas/file.ui.json'
- }
+ forms: {
+ edit_music_recording: {
+ jsonSchema:
+ "extra/kendraio/schemas/music_recording.schema.json",
+ uiSchema: "extra/kendraio/schemas/music_recording.ui.json",
+ },
+ edit_music_work: {
+ jsonSchema: "extra/kendraio/schemas/music_work.schema.json",
+ uiSchema: "extra/kendraio/schemas/music_work.ui.json",
+ },
+ edit_file: {
+ jsonSchema: "extra/kendraio/schemas/file.schema.json",
+ uiSchema: "extra/kendraio/schemas/file.ui.json",
+ },
},
- 'queries': [
- 'extra/kendraio/queries/listRecordings.json'
- ],
- 'actions': [
+ queries: ["extra/kendraio/queries/listRecordings.json"],
+ actions: [
{
- 'title': 'Claim Workflow States',
- 'path': 'extra/kendraio/flow-chart.txt'
+ title: "Claim Workflow States",
+ path: "extra/kendraio/flow-chart.txt",
},
{
- 'title': 'Rights Claim',
- 'path': 'extra/kendraio/sequence.txt'
+ title: "Rights Claim",
+ path: "extra/kendraio/sequence.txt",
},
{
- 'title': 'Claim object states',
- 'path': 'extra/kendraio/claim-process-states.txt'
+ title: "Claim object states",
+ path: "extra/kendraio/claim-process-states.txt",
},
{
- 'title': 'Adapter API example',
- 'path': 'extra/kendraio/adapter-api-form-example.txt'
- }
+ title: "Adapter API example",
+ path: "extra/kendraio/adapter-api-form-example.txt",
+ },
+ ],
+ configs: [
+ "extra/kendraio/configs/listRecordings.json",
+ "extra/kendraio/configs/bulkOperationsExample.json",
+ "extra/kendraio/configs/fakerTest.json",
],
- 'configs': [
- 'extra/kendraio/configs/listRecordings.json',
- 'extra/kendraio/configs/bulkOperationsExample.json',
- 'extra/kendraio/configs/fakerTest.json'
- ]
},
- 'schemas': {
- 'InclusionRelationship': {
- 'context': 'https://kendra.io/schema-v1',
- 'type': 'InclusionRelationship',
- 'label': 'Tag',
- 'icon': 'label',
- 'sourceNodeTypes': [
- 'ImageObject'
- ],
- 'targetNodeTypes': [
- 'Person'
- ],
- 'fields': [
+ schemas: {
+ InclusionRelationship: {
+ context: "https://kendra.io/schema-v1",
+ type: "InclusionRelationship",
+ label: "Tag",
+ icon: "label",
+ sourceNodeTypes: ["ImageObject"],
+ targetNodeTypes: ["Person"],
+ fields: [
{
- 'name': 'visibility',
- 'label': 'Visibility',
- 'type': 'string',
- 'widget': 'select',
- 'options': [
- 'Public',
- 'Private',
- 'Custom'
- ]
+ name: "visibility",
+ label: "Visibility",
+ type: "string",
+ widget: "select",
+ options: ["Public", "Private", "Custom"],
},
{
- 'name': 'boundingBox',
- 'label': 'Region',
- 'type': 'region',
- 'widget': 'none'
- }
- ]
- }
- }
+ name: "boundingBox",
+ label: "Region",
+ type: "region",
+ widget: "none",
+ },
+ ],
+ },
+ },
+ },
+ soundcloud: {
+ adapter: {
+ name: "soundcloud",
+ title: "SoundCloud",
+ description: "Testing additional adapter",
+ infoUrl: "http://soundcloud.com",
+ maintainer: "Kendraio",
+ supportUrl: "http://kendra.io",
+ uploads: true,
+ },
},
- 'soundcloud': {
- 'adapter': {
- 'name': 'soundcloud',
- 'title': 'SoundCloud',
- 'description': 'Testing additional adapter',
- 'infoUrl': 'http://soundcloud.com',
- 'maintainer': 'Kendraio',
- 'supportUrl': 'http://kendra.io',
- 'uploads': true
- }
- }
- }
+ },
]);
this.uploadEnabled = {
bloomen: true,
mixcloud: true,
ddex: true,
- 'copyright-hub': true,
+ "copyright-hub": true,
unsplash: true,
dotbc: true,
chant: true,
- 'apple-music': true,
+ "apple-music": true,
youtube: true,
teosto: true,
spotify: true,
schema: true,
- 'ppl-ipn': true,
+ "ppl-ipn": true,
bandcamp: true,
- 'm-rin': true,
+ "m-rin": true,
example: true,
resonate: true,
tidal: true,
omi: true,
kendraio: true,
- soundcloud: true
+ soundcloud: true,
};
}
doUpload() {
- if (Object.keys(this.uploadEnabled).filter(key => this.uploadEnabled[key]).length === 0) {
+ if (
+ Object.keys(this.uploadEnabled).filter((key) => this.uploadEnabled[key])
+ .length === 0
+ ) {
return;
}
- Object.keys(this.uploadEnabled).forEach(key => {
+ Object.keys(this.uploadEnabled).forEach((key) => {
this.uploadStatus[key] = 0;
});
- forkJoin(Object.keys(this.uploadEnabled)
- .filter(key => this.uploadEnabled[key])
- .map(key => interval(Math.random() * 30).pipe(
- tap(() => this.uploadStatus[key] += 1),
- take(100)
- ))).subscribe(() => {
- const message = 'Upload complete';
- this.snackBar.open(message, 'OK', {
- duration: 5000,
- horizontalPosition: 'center',
- verticalPosition: 'top'
- });
- this.isUploading = false;
+ forkJoin(
+ Object.keys(this.uploadEnabled)
+ .filter((key) => this.uploadEnabled[key])
+ .map((key) =>
+ interval(Math.random() * 30).pipe(
+ tap(() => (this.uploadStatus[key] += 1)),
+ take(100),
+ ),
+ ),
+ ).subscribe(() => {
+ const message = "Upload complete";
+ this.snackBar.open(message, "OK", {
+ duration: 5000,
+ horizontalPosition: "center",
+ verticalPosition: "top",
+ });
+ this.isUploading = false;
});
this.isUploading = true;
}
diff --git a/src/app/services/adapter-form-select.service.ts b/src/app/services/adapter-form-select.service.ts
index 8fc323795..10c4b902f 100644
--- a/src/app/services/adapter-form-select.service.ts
+++ b/src/app/services/adapter-form-select.service.ts
@@ -1,48 +1,49 @@
-import { Injectable } from '@angular/core';
-import { MatDialog } from '@angular/material/dialog';
-import {FormSelectDialogComponent} from '../dialogs/form-select-dialog/form-select-dialog.component';
-import {map, switchMap} from 'rxjs/operators';
-import {has} from 'lodash-es';
-import {Observable, of} from 'rxjs';
-import {KendraioFormService} from '../_shared/ui-form/services/kendraio.form.service';
-import {SwaggerFormSelectDialogComponent} from '../dialogs/swagger-form-select-dialog/swagger-form-select-dialog.component';
+import { Injectable } from "@angular/core";
+import { MatDialog } from "@angular/material/dialog";
+import { FormSelectDialogComponent } from "../dialogs/form-select-dialog/form-select-dialog.component";
+import { map, switchMap } from "rxjs/operators";
+import { has } from "lodash-es";
+import { Observable, of } from "rxjs";
+import { KendraioFormService } from "../_shared/ui-form/services/kendraio.form.service";
+import { SwaggerFormSelectDialogComponent } from "../dialogs/swagger-form-select-dialog/swagger-form-select-dialog.component";
@Injectable({
- providedIn: 'root'
+ providedIn: "root",
})
export class AdapterFormSelectService {
-
constructor(
private readonly dialog: MatDialog,
- private readonly formService: KendraioFormService
- ) { }
+ private readonly formService: KendraioFormService,
+ ) {}
selectForm(): Observable {
const dialogRef = this.dialog.open(FormSelectDialogComponent);
return dialogRef.afterClosed().pipe(
- switchMap(data => {
- if (!!data && has(data, 'adapterId') && has(data, 'formId')) {
+ switchMap((data) => {
+ if (!!data && has(data, "adapterId") && has(data, "formId")) {
const { adapterId, formId } = data;
if (adapterId.length > 0 && formId.length > 0) {
- return this.formService.getFormData(adapterId, formId).pipe(
- map(([ UISchema, JSONSchema ]) => ({ UISchema, JSONSchema }))
- );
+ return this.formService
+ .getFormData(adapterId, formId)
+ .pipe(
+ map(([UISchema, JSONSchema]) => ({ UISchema, JSONSchema })),
+ );
}
}
return of(false);
- })
+ }),
);
}
selectSwagger(): Observable {
const dialogRef = this.dialog.open(SwaggerFormSelectDialogComponent);
return dialogRef.afterClosed().pipe(
- switchMap(data => {
+ switchMap((data) => {
if (!!data) {
return of({ UISchema: {}, JSONSchema: data });
}
return of(false);
- })
+ }),
);
}
}
diff --git a/src/app/services/adapter-install.service.ts b/src/app/services/adapter-install.service.ts
index 8a3176365..42717c3ba 100644
--- a/src/app/services/adapter-install.service.ts
+++ b/src/app/services/adapter-install.service.ts
@@ -1,187 +1,257 @@
-import { Injectable } from '@angular/core';
-import {HttpClient} from '@angular/common/http';
-import {LocalDatabaseService} from './local-database.service';
-import {findIndex, get, has} from 'lodash-es';
-import {AppSettingsService} from './app-settings.service';
-import { MatSnackBar } from '@angular/material/snack-bar';
-import * as LZS from 'lz-string';
+import { Injectable } from "@angular/core";
+import { HttpClient } from "@angular/common/http";
+import { LocalDatabaseService } from "./local-database.service";
+import { findIndex, get, has } from "lodash-es";
+import { AppSettingsService } from "./app-settings.service";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import * as LZS from "lz-string";
@Injectable({
- providedIn: 'root'
+ providedIn: "root",
})
export class AdapterInstallService {
-
constructor(
private readonly http: HttpClient,
private readonly localData: LocalDatabaseService,
private readonly settings: AppSettingsService,
- private readonly notify: MatSnackBar
- ) { }
+ private readonly notify: MatSnackBar,
+ ) {}
addNewAdapter(adapter) {
- const errorReporter = params => err => console.error('db error', err.message, params);
+ const errorReporter = (params) => (err) =>
+ console.error("db error", err.message, params);
// console.log({ adapter });
- this.localData['adapters']
- .add({ ...adapter, name: get(adapter, 'adapterName'), modified: false })
+ this.localData["adapters"]
+ .add({ ...adapter, name: get(adapter, "adapterName"), modified: false })
.then(() => {
- this.notify.open('Saved adapter', 'OK', { verticalPosition: 'top', horizontalPosition: 'center', duration: 2000 });
+ this.notify.open("Saved adapter", "OK", {
+ verticalPosition: "top",
+ horizontalPosition: "center",
+ duration: 2000,
+ });
})
.catch(errorReporter(adapter));
}
addNewWorkflow(workflow) {
const { adapterName, workflowId, title } = workflow;
- const errorReporter = params => err => console.error('db error', err.message, params);
+ const errorReporter = (params) => (err) =>
+ console.error("db error", err.message, params);
// console.log({ workflow });
- this.localData['workflows']
+ this.localData["workflows"]
.add({ ...workflow, adapterName, workflowId, title, blocks: [] })
.catch(errorReporter(workflow))
.then(() => {
- this.localData['adapters'].get(adapterName).then(adapter => {
- const workflowMeta = get(adapter, 'workflow', []);
+ this.localData["adapters"].get(adapterName).then((adapter) => {
+ const workflowMeta = get(adapter, "workflow", []);
workflowMeta.push({ ...workflow, modified: false });
- this.localData['adapters'].update(adapterName, { ...adapter, workflow: workflowMeta, modified: true }).then(() => {
- this.notify.open('Saved workflow', 'OK', { verticalPosition: 'top', horizontalPosition: 'center', duration: 2000 });
- });
+ this.localData["adapters"]
+ .update(adapterName, {
+ ...adapter,
+ workflow: workflowMeta,
+ modified: true,
+ })
+ .then(() => {
+ this.notify.open("Saved workflow", "OK", {
+ verticalPosition: "top",
+ horizontalPosition: "center",
+ duration: 2000,
+ });
+ });
});
});
}
async cloneAdapter(payload) {
const { sourceAdapter, sourceId, targetAdapter, targetId } = payload;
- const workflow = get(await this.localData['workflows'].where({adapterName: sourceAdapter, workflowId: sourceId}).toArray(), '[0]', {});
+ const workflow = get(
+ await this.localData["workflows"]
+ .where({ adapterName: sourceAdapter, workflowId: sourceId })
+ .toArray(),
+ "[0]",
+ {},
+ );
- const errorReporter = params => err => console.error('db error', err.message, params);
- this.localData['workflows']
+ const errorReporter = (params) => (err) =>
+ console.error("db error", err.message, params);
+ this.localData["workflows"]
.add({ ...workflow, adapterName: targetAdapter, workflowId: targetId })
.catch(errorReporter(workflow))
.then(() => {
- this.localData['adapters'].get(targetAdapter).then(adapter => {
- const workflowMeta = get(adapter, 'workflow', []);
- workflowMeta.push({ adapterName: targetAdapter, workflowId: targetId, title: workflow.title, modified: false });
- this.localData['adapters'].update(targetAdapter, { ...adapter, workflow: workflowMeta, modified: true }).then(() => {
- this.notify.open('Saved workflow', 'OK', { verticalPosition: 'top', horizontalPosition: 'center', duration: 2000 });
+ this.localData["adapters"].get(targetAdapter).then((adapter) => {
+ const workflowMeta = get(adapter, "workflow", []);
+ workflowMeta.push({
+ adapterName: targetAdapter,
+ workflowId: targetId,
+ title: workflow.title,
+ modified: false,
});
+ this.localData["adapters"]
+ .update(targetAdapter, {
+ ...adapter,
+ workflow: workflowMeta,
+ modified: true,
+ })
+ .then(() => {
+ this.notify.open("Saved workflow", "OK", {
+ verticalPosition: "top",
+ horizontalPosition: "center",
+ duration: 2000,
+ });
+ });
});
});
}
async packageAdapter(adapterConfig) {
// console.log({adapterConfig});
- const {adapterName} = adapterConfig;
+ const { adapterName } = adapterConfig;
// const compressed = LZS.compressToEncodedURIComponent(JSON.stringify(adapterConfig));
- const database = get(adapterConfig, 'database', [])
- .map(item => ({ ...item, schema: `schemas/${item.schemaName}.json`}));
- const workflow = get(adapterConfig, 'workflow', [])
- .map(item => ({ ...item, config: `configs/${item.workflowId}.json`}));
- const forms = get(adapterConfig, 'forms', [])
- .map(item => ({ ...item, jsonSchema: `jsonSchema/${item.formId}.json`, uiSchema: `uiSchema/${item.formId}.json`}));
+ const database = get(adapterConfig, "database", []).map((item) => ({
+ ...item,
+ schema: `schemas/${item.schemaName}.json`,
+ }));
+ const workflow = get(adapterConfig, "workflow", []).map((item) => ({
+ ...item,
+ config: `configs/${item.workflowId}.json`,
+ }));
+ const forms = get(adapterConfig, "forms", []).map((item) => ({
+ ...item,
+ jsonSchema: `jsonSchema/${item.formId}.json`,
+ uiSchema: `uiSchema/${item.formId}.json`,
+ }));
const attachments = {};
- const databaseConfigs = await this.localData['schemas'].where({adapterName}).toArray();
- databaseConfigs.forEach(item => {
+ const databaseConfigs = await this.localData["schemas"]
+ .where({ adapterName })
+ .toArray();
+ databaseConfigs.forEach((item) => {
attachments[`schemas/${item.schemaName}.json`] = item;
});
- const workflowConfigs = await this.localData['workflows'].where({adapterName}).toArray();
- workflowConfigs.forEach(item => {
+ const workflowConfigs = await this.localData["workflows"]
+ .where({ adapterName })
+ .toArray();
+ workflowConfigs.forEach((item) => {
attachments[`configs/${item.workflowId}.json`] = item;
});
- const formConfigs = await this.localData['forms'].where({adapterName}).toArray();
- formConfigs.forEach(item => {
+ const formConfigs = await this.localData["forms"]
+ .where({ adapterName })
+ .toArray();
+ formConfigs.forEach((item) => {
attachments[`jsonSchema/${item.formId}.json`] = item.jsonSchema;
attachments[`uiSchema/${item.formId}.json`] = item.uiSchema;
});
- const exportData = {...adapterConfig, workflow, database, forms, attachments};
+ const exportData = {
+ ...adapterConfig,
+ workflow,
+ database,
+ forms,
+ attachments,
+ };
return exportData;
}
async exportAdapter(adapterConfig) {
- const {adapterName} = adapterConfig;
- this.packageAdapter(adapterConfig).then(exportData => {
+ const { adapterName } = adapterConfig;
+ this.packageAdapter(adapterConfig).then((exportData) => {
this.downloadData(exportData, adapterName);
});
}
downloadData(outputData, fileName) {
- const blob = new Blob([JSON.stringify(outputData, null, 2)], { type: `application/json;charset=utf-8;` });
- const link = document.createElement('a');
- if (link.download !== undefined) { // feature detection
+ const blob = new Blob([JSON.stringify(outputData, null, 2)], {
+ type: `application/json;charset=utf-8;`,
+ });
+ const link = document.createElement("a");
+ if (link.download !== undefined) {
+ // feature detection
// Browsers that support HTML5 download attribute
const url = URL.createObjectURL(blob);
- link.setAttribute('href', url);
- link.setAttribute('download', `${fileName}.json`);
- link.style.visibility = 'hidden';
+ link.setAttribute("href", url);
+ link.setAttribute("download", `${fileName}.json`);
+ link.style.visibility = "hidden";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}
-
-
importAdapter({ attachments, ...adapterConfig }) {
// const decompressed = LZS.decompressFromEncodedURIComponent(data);
// return JSON.parse(decompressed);
- const adapterName = get(adapterConfig, 'name');
+ const adapterName = get(adapterConfig, "name");
if (!adapterName) {
- throw new Error('Adapter config does not have a name property');
+ throw new Error("Adapter config does not have a name property");
}
- const errorReporter = params => err => console.error('db error', err.message, params);
+ const errorReporter = (params) => (err) =>
+ console.error("db error", err.message, params);
// Load in main adapter metadata
- this.localData['adapters']
+ this.localData["adapters"]
.add({ ...adapterConfig, adapterName, modified: false })
.catch(errorReporter({ adapterName }));
// Load in adapter dashboard route location
- if (has(adapterConfig, 'dashboard')) {
- this.localData['dashboards']
- .add({ ...get(adapterConfig, 'dashboard'), adapterName })
- .catch(errorReporter({ type: 'dashboard', adapterName }));
+ if (has(adapterConfig, "dashboard")) {
+ this.localData["dashboards"]
+ .add({ ...get(adapterConfig, "dashboard"), adapterName })
+ .catch(errorReporter({ type: "dashboard", adapterName }));
}
// Load services menu items
- const services = get(adapterConfig, 'services', []);
- this.localData['services']
+ const services = get(adapterConfig, "services", []);
+ this.localData["services"]
.add({ services, adapterName })
- .catch(errorReporter({ type: 'services', adapterName }));
+ .catch(errorReporter({ type: "services", adapterName }));
// Load in database schemas
- const schemas = get(adapterConfig, 'database', []);
+ const schemas = get(adapterConfig, "database", []);
schemas.forEach(({ name: schemaName, schema }) => {
const schemaConfig = get(attachments, schema, {});
- this.localData['schemas']
+ this.localData["schemas"]
.add({ ...schemaConfig, schemaName, adapterName })
.catch(errorReporter({ schema }));
});
// Load in workflow configs
- const workflows = get(adapterConfig, 'workflow', []);
+ const workflows = get(adapterConfig, "workflow", []);
workflows.forEach(({ title, workflowId, config }) => {
const workflowConfig = get(attachments, config, {});
- this.localData['workflows']
- .add({ ...workflowConfig, title, workflowId, adapterName, modified: false })
+ this.localData["workflows"]
+ .add({
+ ...workflowConfig,
+ title,
+ workflowId,
+ adapterName,
+ modified: false,
+ })
.catch(errorReporter({ workflowId }));
});
// Load in form configs
- const forms = get(adapterConfig, 'forms', []);
+ const forms = get(adapterConfig, "forms", []);
forms.forEach(({ formId, title, jsonSchema, uiSchema }) => {
- const jsonSchemaConfig = get(attachments, jsonSchema, {});
+ const jsonSchemaConfig = get(attachments, jsonSchema, {});
const uiSchemaConfig = get(attachments, uiSchema, {});
- this.localData['forms']
- .add({ formId, title, jsonSchema: jsonSchemaConfig, uiSchema: uiSchemaConfig, adapterName })
+ this.localData["forms"]
+ .add({
+ formId,
+ title,
+ jsonSchema: jsonSchemaConfig,
+ uiSchema: uiSchemaConfig,
+ adapterName,
+ })
.catch(errorReporter({ formId }));
});
// Load in API configs
- const apis = get(adapterConfig, 'apis', []);
+ const apis = get(adapterConfig, "apis", []);
apis.forEach((apiPath) => {
const apiConfig = get(attachments, apiPath, {});
- this.localData['apis']
+ this.localData["apis"]
.add({ adapterName, apiPath, apiConfig })
.catch(errorReporter({ apiPath }));
});
@@ -189,7 +259,8 @@ export class AdapterInstallService {
install({ repoUrl, name }) {
// console.log(`Installing ${name} from ${repoUrl}`);
- this.http.get(`${repoUrl}${name}.json`, { responseType: 'json' })
+ this.http
+ .get(`${repoUrl}${name}.json`, { responseType: "json" })
.subscribe(({ attachments, ...adapterConfig }) => {
this.importAdapter({ attachments, ...adapterConfig });
setTimeout(() => this.settings.settingsUpdated$.next(), 400);
@@ -197,12 +268,12 @@ export class AdapterInstallService {
}
uninstall({ name: adapterName }) {
- this.localData['adapters'].where({ adapterName }).delete();
- this.localData['dashboards'].where({ adapterName }).delete();
- this.localData['services'].where({ adapterName }).delete();
- this.localData['schemas'].where({ adapterName }).delete();
- this.localData['forms'].where({ adapterName }).delete();
- this.localData['workflows'].where({ adapterName }).delete();
- this.localData['apis'].where({ adapterName }).delete();
+ this.localData["adapters"].where({ adapterName }).delete();
+ this.localData["dashboards"].where({ adapterName }).delete();
+ this.localData["services"].where({ adapterName }).delete();
+ this.localData["schemas"].where({ adapterName }).delete();
+ this.localData["forms"].where({ adapterName }).delete();
+ this.localData["workflows"].where({ adapterName }).delete();
+ this.localData["apis"].where({ adapterName }).delete();
}
}
diff --git a/src/app/services/form-data.service.ts b/src/app/services/form-data.service.ts
index 9dda4c329..e96c912e1 100644
--- a/src/app/services/form-data.service.ts
+++ b/src/app/services/form-data.service.ts
@@ -1,92 +1,92 @@
-import { Injectable } from '@angular/core';
-import {of} from 'rxjs';
-import { MatDialog } from '@angular/material/dialog';
-import { MatSnackBar } from '@angular/material/snack-bar';
-import {FormDataSelectDialogComponent} from '../dialogs/form-data-select-dialog/form-data-select-dialog.component';
-import {DocumentRepositoryService} from './document-repository.service';
-import {map, switchMap, tap} from 'rxjs/operators';
-import {get, has} from 'lodash-es';
-import {ApiDataSelectDialogComponent} from '../dialogs/api-data-select-dialog/api-data-select-dialog.component';
-import {HttpClient} from '@angular/common/http';
+import { Injectable } from "@angular/core";
+import { of } from "rxjs";
+import { MatDialog } from "@angular/material/dialog";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { FormDataSelectDialogComponent } from "../dialogs/form-data-select-dialog/form-data-select-dialog.component";
+import { DocumentRepositoryService } from "./document-repository.service";
+import { map, switchMap, tap } from "rxjs/operators";
+import { get, has } from "lodash-es";
+import { ApiDataSelectDialogComponent } from "../dialogs/api-data-select-dialog/api-data-select-dialog.component";
+import { HttpClient } from "@angular/common/http";
@Injectable({
- providedIn: 'root'
+ providedIn: "root",
})
export class FormDataService {
-
constructor(
private readonly dialog: MatDialog,
private readonly notify: MatSnackBar,
private readonly database: DocumentRepositoryService,
- private readonly http: HttpClient
- ) { }
+ private readonly http: HttpClient,
+ ) {}
loadDataFromAPI() {
const dialogRef = this.dialog.open(ApiDataSelectDialogComponent);
return dialogRef.afterClosed().pipe(
- map(data => {
+ map((data) => {
if (!!data) {
const { endpoint, values } = data;
return { status: true, endpoint, values };
}
- return { status: false, endpoint: '', values: {}};
- })
+ return { status: false, endpoint: "", values: {} };
+ }),
);
}
loadData(schemaName) {
return this.database.listAllOfType(schemaName).pipe(
- switchMap(docs => {
+ switchMap((docs) => {
const dialogRef = this.dialog.open(FormDataSelectDialogComponent, {
data: {
schemaName,
- docs
+ docs,
},
});
return dialogRef.afterClosed().pipe(
- switchMap(values => {
- if (!!values && has(values, 'id')) {
- return this.database.getDoc(get(values, 'id'));
+ switchMap((values) => {
+ if (!!values && has(values, "id")) {
+ return this.database.getDoc(get(values, "id"));
}
return of(false);
- })
+ }),
);
- })
+ }),
);
}
saveData(schemaName, values) {
- return this.database.putDoc({ '@schema': schemaName, ... values})
- .pipe(tap(({ ok }) => {
+ return this.database.putDoc({ "@schema": schemaName, ...values }).pipe(
+ tap(({ ok }) => {
if (ok) {
- const message = 'Database update successful';
- this.notify.open(message, 'OK', {
+ const message = "Database update successful";
+ this.notify.open(message, "OK", {
duration: 4000,
- verticalPosition: 'top'
+ verticalPosition: "top",
});
}
- }));
+ }),
+ );
}
saveAPIData(endpoint, data) {
- return this.http.put(`${endpoint}/${data['id']}`, data).pipe(
+ return this.http.put(`${endpoint}/${data["id"]}`, data).pipe(
tap((ok) => {
if (ok) {
- const message = 'API update successful';
- this.notify.open(message, 'OK', {
+ const message = "API update successful";
+ this.notify.open(message, "OK", {
duration: 4000,
- verticalPosition: 'top'
+ verticalPosition: "top",
});
}
- })
+ }),
);
}
noSchemaName() {
- const message = 'No schema name provided';
- this.notify.open(message, 'OK', {
+ const message = "No schema name provided";
+ this.notify.open(message, "OK", {
duration: 4000,
- verticalPosition: 'top'
+ verticalPosition: "top",
});
}
}
diff --git a/src/app/services/global-error-handler.service.ts b/src/app/services/global-error-handler.service.ts
index 966a397b8..1fda5ad9a 100644
--- a/src/app/services/global-error-handler.service.ts
+++ b/src/app/services/global-error-handler.service.ts
@@ -1,22 +1,21 @@
-import { Injectable, ErrorHandler } from '@angular/core';
-import { MatSnackBar } from '@angular/material/snack-bar';
-import {Subject} from 'rxjs';
-import {debounceTime, throttleTime} from 'rxjs/operators';
+import { Injectable, ErrorHandler } from "@angular/core";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { Subject } from "rxjs";
+import { debounceTime, throttleTime } from "rxjs/operators";
@Injectable()
export class GlobalErrorHandlerService implements ErrorHandler {
-
error$ = new Subject();
- constructor(
- private readonly notify: MatSnackBar
- ) {
- this.error$.pipe(
- throttleTime(4000)
- ).subscribe((error: any) => {
+ constructor(private readonly notify: MatSnackBar) {
+ this.error$.pipe(throttleTime(4000)).subscribe((error: any) => {
this.notify.dismiss();
if (error.message && error.message.length > 0) {
- this.notify.open(error.message, 'Dismiss', { horizontalPosition: 'center', verticalPosition: 'top', duration: 4000 });
+ this.notify.open(error.message, "Dismiss", {
+ horizontalPosition: "center",
+ verticalPosition: "top",
+ duration: 4000,
+ });
}
});
}
diff --git a/src/app/services/share-link-generator.service.ts b/src/app/services/share-link-generator.service.ts
index c1f9a6df0..7b33b8fb5 100644
--- a/src/app/services/share-link-generator.service.ts
+++ b/src/app/services/share-link-generator.service.ts
@@ -1,63 +1,65 @@
-import { Injectable } from '@angular/core';
-import { environment } from '../../environments/environment';
-import * as LZS from 'lz-string';
-import { MatDialog } from '@angular/material/dialog';
-import { ShowShareLinkDialogComponent } from '../dialogs/show-share-link-dialog/show-share-link-dialog.component';
-import { LocalDatabaseService } from '../services/local-database.service';
+import { Injectable } from "@angular/core";
+import { environment } from "../../environments/environment";
+import * as LZS from "lz-string";
+import { MatDialog } from "@angular/material/dialog";
+import { ShowShareLinkDialogComponent } from "../dialogs/show-share-link-dialog/show-share-link-dialog.component";
+import { LocalDatabaseService } from "../services/local-database.service";
@Injectable({
- providedIn: 'root'
+ providedIn: "root",
})
export class ShareLinkGeneratorService {
-
constructor(
private readonly dialog: MatDialog,
- private readonly localDatabase: LocalDatabaseService
- ) { }
+ private readonly localDatabase: LocalDatabaseService,
+ ) {}
async shareFlowLink(path, flowData) {
//environment.urlPrefix = 'http://127.0.0.1:4200/';
- const compressed_flow = LZS.compressToEncodedURIComponent(JSON.stringify(flowData));
+ const compressed_flow = LZS.compressToEncodedURIComponent(
+ JSON.stringify(flowData),
+ );
const flowShareLink = `${environment.urlPrefix}${path}?data=${compressed_flow}`;
const json_metadata_string = await this.localDatabase.exportMetadataTable();
- const compressed_database = LZS.compressToEncodedURIComponent(json_metadata_string);
+ const compressed_database =
+ LZS.compressToEncodedURIComponent(json_metadata_string);
const dbShareLink = `${environment.urlPrefix}${path}?metadata=${compressed_database}`;
this.dialog.open(ShowShareLinkDialogComponent, {
data: {
flowShareLink: flowShareLink,
- dbShareLink: dbShareLink
- }
+ dbShareLink: dbShareLink,
+ },
});
}
getData() {
const url = new URL(window.location.href);
- const data = url.searchParams.get('data');
+ const data = url.searchParams.get("data");
if (!!data) {
const decompressed = LZS.decompressFromEncodedURIComponent(data);
return JSON.parse(decompressed);
}
- const metadata = url.searchParams.get('metadata');
+ const metadata = url.searchParams.get("metadata");
if (!!metadata) {
const decompressed = LZS.decompressFromEncodedURIComponent(metadata);
const parsed = JSON.parse(decompressed);
if (parsed.length === 0) {
- throw new Error('The metadata table is empty.');
+ throw new Error("The metadata table is empty.");
}
console.dir(parsed);
- const response = confirm("Do you want to replace your metadata table with the one from the share link? WARNING: You may lose data.");
+ const response = confirm(
+ "Do you want to replace your metadata table with the one from the share link? WARNING: You may lose data.",
+ );
if (response == true) {
this.localDatabase.importMetadataTable(parsed);
return true;
- }
- else {
+ } else {
return false;
}
}
return false;
}
-
}
diff --git a/src/app/services/workflow.service.ts b/src/app/services/workflow.service.ts
index 741ee4316..585d22965 100644
--- a/src/app/services/workflow.service.ts
+++ b/src/app/services/workflow.service.ts
@@ -1,41 +1,39 @@
-import { Injectable } from '@angular/core';
-import {NavigationEnd, Router} from '@angular/router';
-import {filter, take, tap, withLatestFrom} from 'rxjs/operators';
-import {ExportConfigDialogComponent} from '../dialogs/export-config-dialog/export-config-dialog.component';
-import * as stringify from 'json-stringify-safe';
-import {PasteConfigDialogComponent} from '../dialogs/paste-config-dialog/paste-config-dialog.component';
-import {clone, findIndex, get, has, isArray, pick, set} from 'lodash-es';
-import { MatDialog } from '@angular/material/dialog';
-import { MatSnackBar } from '@angular/material/snack-bar';
-import {PageTitleService} from './page-title.service';
-import {AdaptersService} from './adapters.service';
+import { Injectable } from "@angular/core";
+import { NavigationEnd, Router } from "@angular/router";
+import { filter, take, tap, withLatestFrom } from "rxjs/operators";
+import { ExportConfigDialogComponent } from "../dialogs/export-config-dialog/export-config-dialog.component";
+import * as stringify from "json-stringify-safe";
+import { PasteConfigDialogComponent } from "../dialogs/paste-config-dialog/paste-config-dialog.component";
+import { clone, findIndex, get, has, isArray, pick, set } from "lodash-es";
+import { MatDialog } from "@angular/material/dialog";
+import { MatSnackBar } from "@angular/material/snack-bar";
+import { PageTitleService } from "./page-title.service";
+import { AdaptersService } from "./adapters.service";
// tslint:disable-next-line:import-spacing
-import {AdapterBlocksConfigSelectDialogComponent} from
- '../dialogs/adapter-blocks-config-select-dialog/adapter-blocks-config-select-dialog.component';
-import {ShareLinkGeneratorService} from './share-link-generator.service';
-import {LoadWorkflowDialogComponent} from '../dialogs/load-workflow-dialog/load-workflow-dialog.component';
-import {SaveWorkflowDialogComponent} from '../dialogs/save-workflow-dialog/save-workflow-dialog.component';
-import {EditWorkflowMetadataDialogComponent} from '../dialogs/edit-workflow-metadata-dialog/edit-workflow-metadata-dialog.component';
-import {LocalDatabaseService} from './local-database.service';
-import { camelCase } from 'lodash-es';
-import {ConnectionManagerService} from './connection-manager.service';
-import {WorkflowRepoService} from './workflow-repo.service';
+import { AdapterBlocksConfigSelectDialogComponent } from "../dialogs/adapter-blocks-config-select-dialog/adapter-blocks-config-select-dialog.component";
+import { ShareLinkGeneratorService } from "./share-link-generator.service";
+import { LoadWorkflowDialogComponent } from "../dialogs/load-workflow-dialog/load-workflow-dialog.component";
+import { SaveWorkflowDialogComponent } from "../dialogs/save-workflow-dialog/save-workflow-dialog.component";
+import { EditWorkflowMetadataDialogComponent } from "../dialogs/edit-workflow-metadata-dialog/edit-workflow-metadata-dialog.component";
+import { LocalDatabaseService } from "./local-database.service";
+import { camelCase } from "lodash-es";
+import { ConnectionManagerService } from "./connection-manager.service";
+import { WorkflowRepoService } from "./workflow-repo.service";
-const DEFAULT_ADAPTER_NAME = 'Adapter name';
+const DEFAULT_ADAPTER_NAME = "Adapter name";
@Injectable({
- providedIn: 'root'
+ providedIn: "root",
})
export class WorkflowService {
-
- title = '';
+ title = "";
id;
blocks = [];
models = [];
context = {};
tags = [];
- dirty:boolean = false; // has this workflow been modified since last load/save?
+ dirty: boolean = false; // has this workflow been modified since last load/save?
constructor(
private readonly router: Router,
@@ -51,42 +49,54 @@ export class WorkflowService {
this.router.events
.pipe(
filter((e): e is NavigationEnd => e instanceof NavigationEnd),
- filter(({ url }) => url === '/workflow-builder')
+ filter(({ url }) => url === "/workflow-builder"),
)
- .subscribe(_ => this.loadState());
+ .subscribe((_) => this.loadState());
}
initBlocks({ isBuilder }) {
const urlData = this.shareLinks.getData();
if (urlData && isArray(urlData)) {
this.blocks = urlData;
- this.initWorkflow({ title: 'Flow name', blocks: urlData, context: {}, tags: [] }, true);
+ this.initWorkflow(
+ { title: "Flow name", blocks: urlData, context: {}, tags: [] },
+ true,
+ );
}
}
loadState() {
- const state = JSON.parse(localStorage.getItem('kendraio-workflow-state'));
- const title = get(state, 'title', 'Flow name');
- const blocks = get(state, 'blocks', []);
- const context = get(state, 'context', {});
- const tags = get(state, 'tags', []);
+ const state = JSON.parse(localStorage.getItem("kendraio-workflow-state"));
+ const title = get(state, "title", "Flow name");
+ const blocks = get(state, "blocks", []);
+ const context = get(state, "context", {});
+ const tags = get(state, "tags", []);
this.initWorkflow({ title, blocks, context, tags });
- this.id = get(state, 'id', false);
- set(this.context, 'app.adapterName', get(state, 'adapterName', this.getAdapterName()));
+ this.id = get(state, "id", false);
+ set(
+ this.context,
+ "app.adapterName",
+ get(state, "adapterName", this.getAdapterName()),
+ );
}
saveState() {
const { title, blocks, context, id } = this;
const adapterName = this.getAdapterName();
this.workflowRepo.clearCacheFor(adapterName, id);
- localStorage.setItem('kendraio-workflow-state', JSON.stringify({ title, blocks, context, id, adapterName }));
+ localStorage.setItem(
+ "kendraio-workflow-state",
+ JSON.stringify({ title, blocks, context, id, adapterName }),
+ );
}
refresh() {
// TODO: disabled this as was triggering a full workflow re-load
// this.pageTitle.onRefresh();
// ... when this refresh call should just re-init the running workflow
- this.models = this.blocks.map(blockDef => get(blockDef, 'defaultValue', {}));
+ this.models = this.blocks.map((blockDef) =>
+ get(blockDef, "defaultValue", {}),
+ );
this.models.push({});
// TODO: this is a partial refresh of context data, but needs refactoring
// set(this.context, 'adapters', this.adapters.getAdaptersInfo());
@@ -101,51 +111,68 @@ export class WorkflowService {
clearBlocks() {
this.blocks = [];
- this.id = '';
- this.title = 'Flow';
- set(this.context, 'app.adapterName', undefined);
+ this.id = "";
+ this.title = "Flow";
+ set(this.context, "app.adapterName", undefined);
this.saveState();
- this.router.navigate(['/workflow-builder']);
+ this.router.navigate(["/workflow-builder"]);
}
clearWorkflowData() {
- this.models = this.blocks.map(blockDef => get(blockDef, 'defaultValue', {}));
+ this.models = this.blocks.map((blockDef) =>
+ get(blockDef, "defaultValue", {}),
+ );
this.models.push({});
}
copyConfig() {
const dialogRef = this.dialog.open(ExportConfigDialogComponent, {
data: {
- configText: stringify({ title: this.title, blocks: this.blocks, id: this.id, adapterName: this.getAdapterName() }, null, 2)
- }
+ configText: stringify(
+ {
+ title: this.title,
+ blocks: this.blocks,
+ id: this.id,
+ adapterName: this.getAdapterName(),
+ },
+ null,
+ 2,
+ ),
+ },
});
}
pasteConfig() {
const dialogRef = this.dialog.open(PasteConfigDialogComponent, {});
- dialogRef.afterClosed().subscribe(value => {
+ dialogRef.afterClosed().subscribe((value) => {
if (!!value) {
- this.router.navigate(['/workflow-builder']).then(() => {
+ this.router.navigate(["/workflow-builder"]).then(() => {
try {
const config = JSON.parse(value);
- if (has(config, 'blocks')) {
- this.router.routerState.root.queryParams.pipe(
- take(1),
- withLatestFrom(this.router.routerState.root.fragment)
- ).subscribe(([queryParams, fragment]) => {
- this.initWorkflow({
- title: get(config, 'title', 'Imported config'),
- blocks: get(config, 'blocks', []),
- context: {queryParams, fragment},
- tags: get(config, 'tags', [])
+ if (has(config, "blocks")) {
+ this.router.routerState.root.queryParams
+ .pipe(
+ take(1),
+ withLatestFrom(this.router.routerState.root.fragment),
+ )
+ .subscribe(([queryParams, fragment]) => {
+ this.initWorkflow({
+ title: get(config, "title", "Imported config"),
+ blocks: get(config, "blocks", []),
+ context: { queryParams, fragment },
+ tags: get(config, "tags", []),
+ });
+ this.id = get(config, "id");
+ set(
+ this.context,
+ "app.adapterName",
+ get(config, "adapterName", DEFAULT_ADAPTER_NAME),
+ );
+ this.saveState();
});
- this.id = get(config, 'id');
- set(this.context, 'app.adapterName', get(config, 'adapterName', DEFAULT_ADAPTER_NAME));
- this.saveState();
- });
}
} catch (e) {
- console.log('Error importing config', e);
+ console.log("Error importing config", e);
}
});
}
@@ -159,15 +186,30 @@ export class WorkflowService {
this.blocks = blocks;
this.tags = tags || [];
this.context = clone(context);
- set(this.context, 'app.location', pick(location, ['origin', 'protocol', 'host', 'port', 'pathname', 'search', 'hash', 'href']));
+ set(
+ this.context,
+ "app.location",
+ pick(location, [
+ "origin",
+ "protocol",
+ "host",
+ "port",
+ "pathname",
+ "search",
+ "hash",
+ "href",
+ ]),
+ );
this.connectionManager.addToContext(this.context);
this.id = this.getWorkflowId();
- this.models = this.blocks.map(blockDef => get(blockDef, 'defaultValue', {}));
+ this.models = this.blocks.map((blockDef) =>
+ get(blockDef, "defaultValue", {}),
+ );
this.models.push({});
if (isBuilder) {
this.saveState();
}
- if (this.tags.includes('app')) {
+ if (this.tags.includes("app")) {
this.pageTitle.enableAppLayout();
} else {
this.pageTitle.disableAppLayout();
@@ -175,51 +217,88 @@ export class WorkflowService {
}
shareConfig() {
- this.shareLinks.shareFlowLink('workflow-builder', this.blocks);
+ this.shareLinks.shareFlowLink("workflow-builder", this.blocks);
}
loadFromAdapter() {
- const dialogRef = this.dialog.open(AdapterBlocksConfigSelectDialogComponent, {
- data: {}
- });
- dialogRef.afterClosed().subscribe(values => {
+ const dialogRef = this.dialog.open(
+ AdapterBlocksConfigSelectDialogComponent,
+ {
+ data: {},
+ },
+ );
+ dialogRef.afterClosed().subscribe((values) => {
if (!!values) {
this.initWorkflow({ ...values, context: {} });
- set(this.context, 'app.adapterName', get(values, 'adapterName', this.getAdapterName()));
+ set(
+ this.context,
+ "app.adapterName",
+ get(values, "adapterName", this.getAdapterName()),
+ );
}
});
}
saveToAdapter() {
- this.localData['workflows']
- .where('[adapterName+workflowId]')
+ this.localData["workflows"]
+ .where("[adapterName+workflowId]")
.equals([this.getAdapterName() || DEFAULT_ADAPTER_NAME, this.id])
- .modify({ blocks: this.blocks, title: this.title, workflowId: this.id, modified: true })
+ .modify({
+ blocks: this.blocks,
+ title: this.title,
+ workflowId: this.id,
+ modified: true,
+ })
.then(() => {
- this.localData['adapters'].get(this.getAdapterName()).then(adapter => {
- const workflow = get(adapter, 'workflow', []);
- const workflowIndex = findIndex(workflow, ({ workflowId }) => workflowId === this.id);
- if (workflowIndex !== -1) {
- workflow[workflowIndex] = { ...workflow[workflowIndex], title: this.title, modified: true };
- }
- this.localData['adapters'].update(this.getAdapterName(), { ...adapter, workflow, modified: true }).then(() => {
- this.notify.open('Saved workflow', 'OK', { verticalPosition: 'top', horizontalPosition: 'center', duration: 2000 });
+ this.localData["adapters"]
+ .get(this.getAdapterName())
+ .then((adapter) => {
+ const workflow = get(adapter, "workflow", []);
+ const workflowIndex = findIndex(
+ workflow,
+ ({ workflowId }) => workflowId === this.id,
+ );
+ if (workflowIndex !== -1) {
+ workflow[workflowIndex] = {
+ ...workflow[workflowIndex],
+ title: this.title,
+ modified: true,
+ };
+ }
+ this.localData["adapters"]
+ .update(this.getAdapterName(), {
+ ...adapter,
+ workflow,
+ modified: true,
+ })
+ .then(() => {
+ this.notify.open("Saved workflow", "OK", {
+ verticalPosition: "top",
+ horizontalPosition: "center",
+ duration: 2000,
+ });
+ });
});
- });
})
- .catch(error => console.log({ error }));
+ .catch((error) => console.log({ error }));
}
upload() {
const { title, blocks, id, tags } = this;
const dialogRef = this.dialog.open(SaveWorkflowDialogComponent, {
disableClose: true,
- data: { title, blocks, id: camelCase(id), adapterName: this.getAdapterName(), tags }
+ data: {
+ title,
+ blocks,
+ id: camelCase(id),
+ adapterName: this.getAdapterName(),
+ tags,
+ },
});
- dialogRef.afterClosed().subscribe(values => {
+ dialogRef.afterClosed().subscribe((values) => {
if (!!values) {
// console.log(values);
- this.id = get(values, 'id', this.id);
+ this.id = get(values, "id", this.id);
this.saveState();
this.dirty = false;
}
@@ -228,16 +307,20 @@ export class WorkflowService {
download() {
const dialogRef = this.dialog.open(LoadWorkflowDialogComponent);
- dialogRef.afterClosed().subscribe(values => {
+ dialogRef.afterClosed().subscribe((values) => {
if (!!values) {
// console.log(values);
- const blocks = get(values, 'blocks', []);
- const tags = get(values, 'tags', []);
- const title = get(values, 'title', 'Flow');
+ const blocks = get(values, "blocks", []);
+ const tags = get(values, "tags", []);
+ const title = get(values, "title", "Flow");
this.initWorkflow({ title, blocks, context: {}, tags });
- this.id = get(values, 'id');
- this.tags = get(values, 'tags', []);
- set(this.context, 'app.adapterName', get(values, 'adapterName', this.getAdapterName()));
+ this.id = get(values, "id");
+ this.tags = get(values, "tags", []);
+ set(
+ this.context,
+ "app.adapterName",
+ get(values, "adapterName", this.getAdapterName()),
+ );
this.saveState();
}
});
@@ -247,14 +330,18 @@ export class WorkflowService {
const { title, id, tags } = this;
const dialogRef = this.dialog.open(EditWorkflowMetadataDialogComponent, {
disableClose: true,
- data: { title, id, adapterName: this.getAdapterName(), tags }
+ data: { title, id, adapterName: this.getAdapterName(), tags },
});
- dialogRef.afterClosed().subscribe(values => {
+ dialogRef.afterClosed().subscribe((values) => {
if (!!values) {
- this.title = get(values, 'title', 'Flow');
- this.id = get(values, 'id');
- this.tags = get(values, 'tags');
- set(this.context, 'app.adapterName', get(values, 'adapterName', this.getAdapterName()));
+ this.title = get(values, "title", "Flow");
+ this.id = get(values, "id");
+ this.tags = get(values, "tags");
+ set(
+ this.context,
+ "app.adapterName",
+ get(values, "adapterName", this.getAdapterName()),
+ );
this.saveState();
this.dirty = true;
}
@@ -262,10 +349,10 @@ export class WorkflowService {
}
getAdapterName() {
- return get(this.context, 'app.adapterName', 'Adapter name');
+ return get(this.context, "app.adapterName", "Adapter name");
}
getWorkflowId() {
- return get(this.context, 'app.workflowId');
+ return get(this.context, "app.workflowId");
}
}
diff --git a/src/styles.scss b/src/styles.scss
index d0ed04415..864e14e28 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -1,6 +1,6 @@
-@use '@angular/material' as mat;
-@import '../node_modules/ag-grid-community/styles/ag-theme-alpine.css';
-@import '../node_modules/ag-grid-community/styles/ag-grid.css';
+@use "@angular/material" as mat;
+@import "../node_modules/ag-grid-community/styles/ag-theme-alpine.css";
+@import "../node_modules/ag-grid-community/styles/ag-grid.css";
@import "@fortawesome/fontawesome-free/css/all.css";
// Custom Theming for Angular Material
@@ -21,7 +21,7 @@
@include mat.all-legacy-component-typographies();
@include mat.legacy-core();
-@import '@angular/material/theming';
+@import "@angular/material/theming";
@include mat-core();
// Define the palettes for your theme using the Material Design palettes available in palette.scss
@@ -34,16 +34,17 @@ $kendraio-app-accent: mat.define-palette(mat.$orange-palette, A200, A100, A400);
$kendraio-app-warn: mat.define-palette(mat.$red-palette);
// Create the theme object (a Sass map containing all of the palettes).
-$kendraio-app-theme: mat.define-light-theme((
- color: (
- primary: $kendraio-app-primary,
- accent: $kendraio-app-accent,
- warn: $kendraio-app-warn
- ),
- typography: mat.define-typography-config(),
- density: 0,
-));
-
+$kendraio-app-theme: mat.define-light-theme(
+ (
+ color: (
+ primary: $kendraio-app-primary,
+ accent: $kendraio-app-accent,
+ warn: $kendraio-app-warn,
+ ),
+ typography: mat.define-typography-config(),
+ density: 0,
+ )
+);
// Include theme styles for core and each component used in your app.
// Alternatively, you can import and @include the theme mixins for each component
@@ -51,9 +52,15 @@ $kendraio-app-theme: mat.define-light-theme((
@include mat.all-legacy-component-themes($kendraio-app-theme);
@include angular-material-theme($kendraio-app-theme);
-
-html, body { height: 100%; overflow: hidden; }
-body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
+html,
+body {
+ height: 100%;
+ overflow: hidden;
+}
+body {
+ margin: 0;
+ font-family: Roboto, "Helvetica Neue", sans-serif;
+}
.mb-1 {
margin-bottom: 1em;
@@ -76,19 +83,19 @@ dynamic-material-form-control .mat-form-field {
display: none;
}
-.schedule-popup{
+.schedule-popup {
// display:none;
position: absolute;
- box-shadow: 3px 3px 3px rgba(55,3, 3, .5);
+ box-shadow: 3px 3px 3px rgba(55, 3, 3, 0.5);
padding: 16px;
background-color: silver;
-transform: translatey(-80px);
-transition: all 2s ease;
-z-index: 9;
-&.in{
- display: block;
-// transform: translate(80px,-80px);
-}
+ transform: translatey(-80px);
+ transition: all 2s ease;
+ z-index: 9;
+ &.in {
+ display: block;
+ // transform: translate(80px,-80px);
+ }
}
.editor-config-column .mat-expansion-panel-body {