Skip to content

Commit

Permalink
GDB-7820 introduce saved query edit operation (#47)
Browse files Browse the repository at this point in the history
## What
This MR introduces support for editing of saved queries.

## Why
GDB workbench supports functionality for saving sparql queries as well as managing the saved queries like editing them.

## How
* Introduced the edit query action in the saved queries popup as well as respective handlers in the ontotext yasgui component.
* Extended models and improved the save query handling.
* Implemented tests.
  • Loading branch information
svilenvelikov authored Jan 18, 2023
1 parent 89fa003 commit 38705d5
Show file tree
Hide file tree
Showing 18 changed files with 2,957 additions and 126 deletions.
36 changes: 36 additions & 0 deletions cypress/e2e/editor-actions/edit-saved-query.spec.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {YasqeSteps} from "../../steps/yasqe-steps";
import {QueryStubs} from "../../stubs/query-stubs";
import ActionsPageSteps from "../../steps/actions-page-steps";

describe('Edit saved query action', () => {
beforeEach(() => {
QueryStubs.stubDefaultQueryResponse();
// Given I have opened a page with the yasgui
// And there is an open tab with sparql query in it
ActionsPageSteps.visit();
});

it('Should be able to edit saved query', () => {
// Given I have opened the saved queries popup
YasqeSteps.showSavedQueries();
YasqeSteps.getSavedQueriesPopup().should('be.visible');
// When I hover on any query name in the list
YasqeSteps.editQuery(4);
// Then I expect to see the edit query action
YasqeSteps.getSaveQueryDialog().should('be.visible');
YasqeSteps.getQueryNameField().should('have.value', 'q2');
YasqeSteps.getQueryField().should('have.value', 'select * where { \n\t?s ?p ?o .\n} limit 100 \n');
YasqeSteps.getIsPublicField().should('not.be.checked');
// When I change the query data
YasqeSteps.writeQueryName('-new');
YasqeSteps.clearQueryField();
YasqeSteps.writeQuery('select *');
YasqeSteps.toggleIsPublic();
// And I save the query
YasqeSteps.saveQuery();
// Then I expect that the query is saved
YasqeSteps.getSaveQueryDialog().should('not.exist');
YasqeSteps.getSavedQueriesPopup().should('not.exist');
ActionsPageSteps.getSaveQueryPayload().should('contain.value', '{"queryName":"q2-new","query":"select *","isPublic":true}');
});
});
20 changes: 19 additions & 1 deletion cypress/e2e/editor-actions/save-query.spec.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ describe('Save query action', () => {
ActionsPageSteps.getSaveQueryPayload().should('contain.value', '{"queryName":"Query","query":"select * where { ?s ?p ?o . } limit 100","isPublic":false}');
});

it('Should be able to edit query and save it', () => {
it('Should be able to change the query and save it', () => {
// When I click on the save query button
YasqeSteps.getCreateSavedQueryButton().should('be.visible');
YasqeSteps.createSavedQuery();
Expand Down Expand Up @@ -161,4 +161,22 @@ describe('Save query action', () => {
// And query is saved
ActionsPageSteps.getSaveQueryPayload().should('contain.value', '{"queryName":"Query two","query":"select * where { ?s ?p ?o . } limit 100","isPublic":false}');
});

it('Should reset the error message from previous failure', () => {
// I have saved a query with name Query once
YasqeSteps.createSavedQuery();
YasqeSteps.saveQuery();
YasqeSteps.getSaveQueryDialog().should('not.exist');
// When I try to save a query with the same query name again
YasqeSteps.createSavedQuery();
YasqeSteps.saveQuery();
// Then the save query dialog remains open
YasqeSteps.getSaveQueryDialog().should('be.visible');
YasqeSteps.getErrorsPane().should('contain.text', 'Query name already exist!');
// When I close the dialog and reopen it again
YasqeSteps.cancelSaveQuery();
YasqeSteps.createSavedQuery();
YasqeSteps.getSaveQueryDialog().should('be.visible');
YasqeSteps.getErrorsPane().should('not.exist');
});
});
10 changes: 9 additions & 1 deletion cypress/steps/yasqe-steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,12 @@ export class YasqeSteps {
this.getQueryNameField().clear();
}

static getIsPublicField() {
return this.getSaveQueryDialog().find('#publicQuery');
}

static toggleIsPublic() {
this.getSaveQueryDialog().find('#publicQuery').click();
this.getIsPublicField().click();
}

static getControlBar() {
Expand Down Expand Up @@ -130,4 +134,8 @@ export class YasqeSteps {
return el[tabIndex].CodeMirror.getValue();
});
}

static editQuery(index: number) {
this.getSavedQueries().eq(index).realHover().find('.edit-saved-query').click();
}
}
4 changes: 3 additions & 1 deletion cypress/support/e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@
import './commands'

// Alternatively you can use CommonJS syntax:
// require('./commands')
// require('./commands')

import 'cypress-real-events';
5 changes: 3 additions & 2 deletions cypress/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@
],
"types": [
"cypress",
"node"
"node",
"cypress-real-events"
]
},
"include": [
"**/*.ts"
]
}
}
22 changes: 21 additions & 1 deletion ontotext-yasgui-web-component/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
*/
import { HTMLStencilElement, JSXBase } from "@stencil/core/internal";
import { ExternalYasguiConfiguration } from "./models/external-yasgui-configuration";
import { SavedQueriesData, SavedQueryConfig, SaveQueryData, UpdateQueryData } from "./models/model";
import { QueryEvent, QueryResponseEvent } from "./models/event";
import { SavedQueriesData, SaveQueryData } from "./models/model";
import { ServiceFactory } from "./services/service-factory";
export namespace Components {
/**
Expand Down Expand Up @@ -35,6 +35,10 @@ export namespace Components {
* An input property containing the chosen translation language.
*/
"language": string;
/**
* A configuration model related with all the saved queries actions.
*/
"savedQueryConfig"?: SavedQueryConfig;
"setQuery": (query: string) => Promise<void>;
}
interface SaveQueryDialog {
Expand Down Expand Up @@ -155,6 +159,14 @@ declare namespace LocalJSX {
* Event emitted when after query response is returned.
*/
"onQueryResponse"?: (event: OntotextYasguiCustomEvent<QueryResponseEvent>) => void;
/**
* Event emitted when a query payload is updated and the query name is the same as the one being edited. In result the client must perform a query update.
*/
"onUpdateSavedQuery"?: (event: OntotextYasguiCustomEvent<SaveQueryData>) => void;
/**
* A configuration model related with all the saved queries actions.
*/
"savedQueryConfig"?: SavedQueryConfig;
}
interface SaveQueryDialog {
/**
Expand All @@ -169,6 +181,10 @@ declare namespace LocalJSX {
* Event fired when the create button in the dialog is triggered. The event payload holds the new saved query data.
*/
"onInternalSaveQueryEvent"?: (event: SaveQueryDialogCustomEvent<SaveQueryData>) => void;
/**
* Event fired when the create button in the dialog is triggered and the query name is the same as the one that was initially provided a.k.a. the query is updated. The event payload holds the updated query data.
*/
"onInternalUpdateQueryEvent"?: (event: SaveQueryDialogCustomEvent<UpdateQueryData>) => void;
"serviceFactory"?: ServiceFactory;
}
interface SavedQueriesPopup {
Expand All @@ -177,6 +193,10 @@ declare namespace LocalJSX {
* Event fired when the saved queries popup should be closed.
*/
"onInternalCloseSavedQueriesPopupEvent"?: (event: SavedQueriesPopupCustomEvent<any>) => void;
/**
* Event fired when the edit saved query button is triggered.
*/
"onInternalEditSavedQueryEvent"?: (event: SavedQueriesPopupCustomEvent<SaveQueryData>) => void;
/**
* Event fired when a saved query is selected from the list.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,14 @@ import {HtmlElementsUtil} from '../../services/utils/html-elements-util';
import {OntotextYasguiService} from '../../services/yasgui/ontotext-yasgui-service';
import {ExternalYasguiConfiguration} from "../../models/external-yasgui-configuration";
import {TranslationService} from '../../services/translation.service';
import {SavedQueriesData, SaveQueryData} from "../../models/model";
import {ServiceFactory} from '../../services/service-factory';
import {YasguiConfigurationBuilder} from '../../services/yasgui/configuration/yasgui-configuration-builder';
import {
SavedQueriesData,
SavedQueryConfig,
SaveQueryData,
UpdateQueryData
} from "../../models/model";

type EventArguments = [Yasqe, Request, number];

Expand Down Expand Up @@ -84,6 +89,11 @@ export class OntotextYasguiWebComponent {
*/
@Prop() language: string

/**
* A configuration model related with all the saved queries actions.
*/
@Prop() savedQueryConfig?: SavedQueryConfig;

/**
* Event emitted when before query to be executed.
*/
Expand All @@ -100,6 +110,12 @@ export class OntotextYasguiWebComponent {
*/
@Event() createSavedQuery: EventEmitter<SaveQueryData>;

/**
* Event emitted when a query payload is updated and the query name is the same as the one being
* edited. In result the client must perform a query update.
*/
@Event() updateSavedQuery: EventEmitter<SaveQueryData>;

/**
* Event emitted when saved queries is expected to be loaded by the component client and provided
* back in order to be displayed.
Expand All @@ -121,13 +137,33 @@ export class OntotextYasguiWebComponent {
*/
queryDuration = 0;

/**
* A model which is set when query details are populated and the query is going to be saved.
*/
@State() saveQueryData: SaveQueryData;

/**
* A model which is set when an existing saved query is edited and is going to be saved.
*/
@State() savedQueryData: SaveQueryData;

/**
* If the yasgui layout is oriented vertically or not.
*/
@State() isVerticalOrientation = true;

@Watch('config')
configurationChanged(newConfig: ExternalYasguiConfiguration) {
this.init(newConfig);
}

@Watch('savedQueryConfig')
savedQueryConfigChanged() {
this.shouldShowSaveQueryDialog();
this.shouldShowSavedQueriesPopup();
this.saveQueryData = this.initSaveQueryData();
}

@Watch('language')
languageChanged(newLang: string) {
this.translationService.setLanguage(newLang);
Expand All @@ -151,6 +187,7 @@ export class OntotextYasguiWebComponent {
@Listen('internalCreateSavedQueryEvent')
saveQueryHandler() {
this.showSaveQueryDialog = true;
this.saveQueryData = this.getDefaultSaveQueryData();
}

/**
Expand All @@ -161,6 +198,14 @@ export class OntotextYasguiWebComponent {
this.createSavedQuery.emit(event.detail);
}

/**
* Handler for the event fired when the query should be saved by the component client.
*/
@Listen('internalUpdateQueryEvent')
updateSavedQueryHandler(event: CustomEvent<UpdateQueryData>) {
this.updateSavedQuery.emit(event.detail);
}

/**
* Handler for the event fired when the saveQueryDialog gets closed.
*/
Expand All @@ -180,6 +225,7 @@ export class OntotextYasguiWebComponent {
@Listen('internalShowSavedQueriesEvent')
showSavedQueriesHandler() {
this.loadSavedQueries.emit(true);
this.showSavedQueriesPopup = true;
}

/**
Expand All @@ -192,6 +238,16 @@ export class OntotextYasguiWebComponent {
this.ontotextYasgui.createNewTab(queryData.queryName, queryData.query);
}

/**
* Handler for the event fired when the edit saved query button is triggered.
*/
@Listen('internalEditSavedQueryEvent')
editSavedQueryHandler(event: CustomEvent<SaveQueryData>) {
this.savedQueryData = event.detail
this.showSavedQueriesPopup = false;
this.showSaveQueryDialog = true;
}

/**
* Handler for the event for closing the saved queries popup.
*/
Expand Down Expand Up @@ -239,9 +295,6 @@ export class OntotextYasguiWebComponent {
// * Configure the web component
this.ontotextYasguiService.postConstruct(this.hostElement, this.ontotextYasgui.getConfig());

this.shouldShowSaveQueryDialog();
this.shouldShowSavedQueriesPopup();

// * Register any needed event handler
this.ontotextYasgui.registerYasqeEventListener('query', this.onQuery.bind(this));
this.ontotextYasgui.registerYasqeEventListener('queryResponse', (args: EventArguments) => this.onQueryResponse(args[0], args[1], args[2]));
Expand Down Expand Up @@ -270,29 +323,51 @@ export class OntotextYasguiWebComponent {
this.queryResponse.emit({duration});
}

private getSaveQueryData(): SaveQueryData {
const data: SaveQueryData = {
private getDefaultSaveQueryData(): SaveQueryData {
return {
queryName: '',
query: '',
isPublic: false
owner: '',
isPublic: false,
messages: []
};
if (this.ontotextYasgui) {
}

private initSaveQueryData(): SaveQueryData {
return {
queryName: '',
query: '',
owner: '',
isPublic: false,
messages: this.savedQueryConfig?.errorMessage || []
};
}

private getSaveQueryData(): SaveQueryData {
const data: SaveQueryData = this.saveQueryData || this.getDefaultSaveQueryData();
// first take into account if there is a saved query selected for edit
// then take the data from the currently opened yasgui which means a new unsaved yet query
if (this.savedQueryData) {
data.queryName = this.savedQueryData.queryName;
data.query = this.savedQueryData.query;
data.isPublic = this.savedQueryData.isPublic;
data.isNew = false;
} else if (this.ontotextYasgui) {
data.queryName = this.ontotextYasgui.getTabName();
data.query = this.ontotextYasgui.getTabQuery();
data.isPublic = false;
data.isNew = true;
}
if (this.config.savedQuery && !this.isSavedQuerySaved()) {
data.messages = this.config.savedQuery.errorMessage;
}
data.messages = this.saveQueryData && this.saveQueryData.messages;
return data;
}

private getSaveQueriesData(): SavedQueriesData {
const data: SavedQueriesData = {
savedQueriesList: []
};
if (this.config.savedQueries) {
data.savedQueriesList = this.config.savedQueries.data.map((savedQuery) => {
if (this.savedQueryConfig && this.savedQueryConfig.savedQueries) {
data.savedQueriesList = this.savedQueryConfig.savedQueries.map((savedQuery) => {
return {
queryName: savedQuery.queryName,
query: savedQuery.query,
Expand All @@ -314,15 +389,15 @@ export class OntotextYasguiWebComponent {
}

private shouldShowSaveQueryDialog(): void {
this.showSaveQueryDialog = this.showSaveQueryDialog && (!this.config.savedQuery || !this.isSavedQuerySaved());
this.showSaveQueryDialog = this.showSaveQueryDialog && !this.isSavedQuerySaved();
}

private isSavedQuerySaved() {
return this.config.savedQuery && this.config.savedQuery.saveSuccess;
return this.savedQueryConfig && this.savedQueryConfig.saveSuccess;
}

private shouldShowSavedQueriesPopup(): void {
this.showSavedQueriesPopup = this.showSavedQueriesPopup || !!(this.config.savedQueries?.data && this.config.savedQueries?.data.length > 0);
this.showSavedQueriesPopup = this.showSavedQueriesPopup && (this.savedQueryConfig?.savedQueries && this.savedQueryConfig?.savedQueries.length > 0);
}

private destroy() {
Expand All @@ -339,6 +414,7 @@ export class OntotextYasguiWebComponent {
if (!this.config) {
return (<Host></Host>);
}

const classList = `yasgui-host-element ${this.getOrientationMode()} ${this.getRenderMode()}`;
return (
<Host class={classList}>
Expand Down
Loading

0 comments on commit 38705d5

Please sign in to comment.