diff --git a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/sql/WebSQLResultsInfo.java b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/sql/WebSQLResultsInfo.java index 916efb4fc8..b4fee6f6e9 100644 --- a/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/sql/WebSQLResultsInfo.java +++ b/server/bundles/io.cloudbeaver.model/src/io/cloudbeaver/service/sql/WebSQLResultsInfo.java @@ -22,6 +22,7 @@ import org.jkiss.dbeaver.model.DBUtils; import org.jkiss.dbeaver.model.data.DBDAttributeBinding; import org.jkiss.dbeaver.model.data.DBDRowIdentifier; +import org.jkiss.dbeaver.model.exec.trace.DBCTrace; import org.jkiss.dbeaver.model.struct.*; import java.util.HashSet; @@ -38,6 +39,7 @@ public class WebSQLResultsInfo { @NotNull private final String id; private DBDAttributeBinding[] attributes; + private DBCTrace trace; private String queryText; public WebSQLResultsInfo(@NotNull DBSDataContainer dataContainer, @NotNull String id) { @@ -132,4 +134,11 @@ public boolean canRefreshResults() { (!(entity instanceof DBSDocumentContainer) && !entity.getDataSource().getInfo().isDynamicMetadata()); } + public DBCTrace getTrace() { + return trace; + } + + public void setTrace(@NotNull DBCTrace trace) { + this.trace = trace; + } } diff --git a/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls b/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls index d5ab8ee8e8..db942decd4 100644 --- a/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls +++ b/server/bundles/io.cloudbeaver.server/schema/service.sql.graphqls @@ -120,6 +120,7 @@ type SQLResultSet { # can't update data or load LOB file if hasRowIdentifier = false hasRowIdentifier: Boolean! hasChildrenCollection: Boolean! + hasDynamicTrace: Boolean! @since(version: "24.1.2") isSupportsDataFilter: Boolean! } @@ -211,6 +212,16 @@ type SQLScriptQuery { start: Int! end: Int! } + +#################################################### +# Dynamic trace info +#################################################### +type DynamicTraceProperty { + name: String! + value: String + description: String +} + #################################################### # Query and Mutation #################################################### @@ -402,6 +413,14 @@ extend type Mutation { asyncSqlRowDataCountResult(taskId: ID!): Int! + @since(version: "24.1.2") + sqlGetDynamicTrace( + projectId: ID, + connectionId: ID!, + contextId: ID!, + resultsId: ID! + ): [DynamicTraceProperty!]! + @since(version: "24.0.1") asyncSqlSetAutoCommit( projectId: ID!, diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java index 752357ba66..a7d5a95fc1 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/DBWServiceSQL.java @@ -26,6 +26,7 @@ import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.model.exec.DBCLogicalOperator; +import org.jkiss.dbeaver.model.exec.trace.DBCTraceProperty; import org.jkiss.dbeaver.model.sql.registry.SQLGeneratorDescriptor; import java.util.List; @@ -106,6 +107,17 @@ WebAsyncTaskInfo asyncReadDataFromContainer( @Nullable WebSQLDataFilter filter, @Nullable WebDataFormat dataFormat) throws DBWebException; + /** + * Reads dynamic trace from provided database results. + */ + @NotNull + @WebAction + List readDynamicTrace( + @NotNull WebSession webSession, + @NotNull WebSQLContextInfo contextInfo, + @NotNull String resultsId + ) throws DBException; + @WebAction Boolean closeResult(@NotNull WebSQLContextInfo sqlContext, @NotNull String resultId) throws DBWebException; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLContextInfo.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLContextInfo.java index 13862f2125..1dc81cbad9 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLContextInfo.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLContextInfo.java @@ -29,6 +29,7 @@ import org.jkiss.dbeaver.model.DBUtils; import org.jkiss.dbeaver.model.data.DBDAttributeBinding; import org.jkiss.dbeaver.model.exec.*; +import org.jkiss.dbeaver.model.exec.trace.DBCTrace; import org.jkiss.dbeaver.model.meta.Property; import org.jkiss.dbeaver.model.qm.QMTransactionState; import org.jkiss.dbeaver.model.qm.QMUtils; @@ -142,13 +143,21 @@ public void setDefaults(String catalogName, String schemaName) throws DBWebExcep } } + /** + * Saves results info into cache. + * Helps to find it with results id sent by front-end. + */ @NotNull - public WebSQLResultsInfo saveResult(@NotNull DBSDataContainer dataContainer, @NotNull DBDAttributeBinding[] attributes) { + public WebSQLResultsInfo saveResult( + @NotNull DBSDataContainer dataContainer, + @NotNull DBCTrace trace, + @NotNull DBDAttributeBinding[] attributes) { WebSQLResultsInfo resultInfo = new WebSQLResultsInfo( dataContainer, String.valueOf(resultId.incrementAndGet()) ); resultInfo.setAttributes(attributes); + resultInfo.setTrace(trace); resultInfoMap.put(resultInfo.getId(), resultInfo); return resultInfo; } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryDataReceiver.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryDataReceiver.java index 957b322f7f..6a5800cb20 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryDataReceiver.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryDataReceiver.java @@ -25,6 +25,8 @@ import org.jkiss.dbeaver.model.DBUtils; import org.jkiss.dbeaver.model.data.*; import org.jkiss.dbeaver.model.exec.*; +import org.jkiss.dbeaver.model.exec.trace.DBCTrace; +import org.jkiss.dbeaver.model.exec.trace.DBCTraceDynamic; import org.jkiss.dbeaver.model.impl.data.DBDValueError; import org.jkiss.dbeaver.model.meta.MetaData; import org.jkiss.dbeaver.model.sql.DBQuotaException; @@ -48,6 +50,7 @@ class WebSQLQueryDataReceiver implements DBDDataReceiver { private final WebSQLQueryResultSet webResultSet = new WebSQLQueryResultSet(); private DBDAttributeBinding[] bindings; + private DBCTrace trace; private List rows = new ArrayList<>(); private final Number rowLimit; @@ -71,6 +74,9 @@ public void fetchStart(@NotNull DBCSession session, @NotNull DBCResultSet dbResu DBCAttributeMetaData attrMeta = attributes.get(i); bindings[i] = new DBDAttributeBindingMeta(dataContainer, dbResult.getSession(), attrMeta); } + if (dbResult instanceof DBCResultSetTrace resultSetTrace) { + this.trace = resultSetTrace.getExecutionTrace(); + } } @Override @@ -153,8 +159,9 @@ public void fetchEnd(@NotNull DBCSession session, @NotNull DBCResultSet resultSe webResultSet.setRows(List.of(rows.toArray(new WebSQLQueryResultSetRow[0]))); webResultSet.setHasChildrenCollection(resultSet instanceof DBDSubCollectionResultSet); webResultSet.setSupportsDataFilter(dataContainer.isFeatureSupported(DBSDataContainer.FEATURE_DATA_FILTER)); + webResultSet.setHasDynamicTrace(trace instanceof DBCTraceDynamic); - WebSQLResultsInfo resultsInfo = contextInfo.saveResult(dataContainer, bindings); + WebSQLResultsInfo resultsInfo = contextInfo.saveResult(dataContainer, trace, bindings); webResultSet.setResultsInfo(resultsInfo); boolean isSingleEntity = DBExecUtils.detectSingleSourceTable(bindings) != null; diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryResultSet.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryResultSet.java index d7dba7e9cd..92882c4a7d 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryResultSet.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebSQLQueryResultSet.java @@ -39,6 +39,7 @@ public class WebSQLQueryResultSet { private boolean hasChildrenCollection; private boolean isSupportsDataFilter; + private boolean hasDynamicTrace; public WebSQLQueryResultSet() { } @@ -132,4 +133,13 @@ public boolean isSupportsDataFilter() { public void setSupportsDataFilter(boolean supportsDataFilter) { isSupportsDataFilter = supportsDataFilter; } + + @Property + public boolean isHasDynamicTrace() { + return hasDynamicTrace; + } + + public void setHasDynamicTrace(boolean hasDynamicTrace) { + this.hasDynamicTrace = hasDynamicTrace; + } } diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java index 662fe6bd7e..7c5ddcd015 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/WebServiceBindingSQL.java @@ -149,6 +149,12 @@ public void bindWiring(DBWBindingContext model) throws DBWebException { env.getArgument("resultsId"), env.getArgument("columnIndex"), new WebSQLResultsRow(env.getArgument("row")))) + .dataFetcher("sqlGetDynamicTrace", env -> + getService(env).readDynamicTrace( + getWebSession(env), + getSQLContext(env), + env.getArgument("resultsId") + )) .dataFetcher("updateResultsDataBatch", env -> getService(env).updateResultsDataBatch( getSQLContext(env), diff --git a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java index 6658935c90..cc52563e20 100644 --- a/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java +++ b/server/bundles/io.cloudbeaver.server/src/io/cloudbeaver/service/sql/impl/WebServiceSQL.java @@ -36,6 +36,9 @@ import org.jkiss.dbeaver.model.exec.DBCException; import org.jkiss.dbeaver.model.exec.DBCLogicalOperator; import org.jkiss.dbeaver.model.exec.DBExecUtils; +import org.jkiss.dbeaver.model.exec.trace.DBCTrace; +import org.jkiss.dbeaver.model.exec.trace.DBCTraceDynamic; +import org.jkiss.dbeaver.model.exec.trace.DBCTraceProperty; import org.jkiss.dbeaver.model.impl.sql.BasicSQLDialect; import org.jkiss.dbeaver.model.navigator.DBNNode; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; @@ -455,6 +458,21 @@ public void run(DBRProgressMonitor monitor) throws InvocationTargetException, In return contextInfo.getProcessor().getWebSession().createAndRunAsyncTask("Read data from container " + nodePath, runnable); } + @NotNull + @Override + public List readDynamicTrace( + @NotNull WebSession webSession, + @NotNull WebSQLContextInfo contextInfo, + @NotNull String resultsId + ) throws DBException { + WebSQLResultsInfo resultsInfo = contextInfo.getResults(resultsId); + DBCTrace trace = resultsInfo.getTrace(); + if (trace instanceof DBCTraceDynamic traceDynamic) { + return traceDynamic.getTraceProperties(webSession.getProgressMonitor()); + } + throw new DBWebException("Dynamic trace is not found in provided results info"); + } + @Override public WebSQLExecuteInfo asyncGetQueryResults(@NotNull WebSession webSession, @NotNull String taskId) throws DBWebException { WebAsyncTaskInfo taskStatus = webSession.asyncTaskStatus(taskId, false); diff --git a/webapp/packages/core-localization/src/locales/en.ts b/webapp/packages/core-localization/src/locales/en.ts index d2cdff1589..65deee4ee2 100644 --- a/webapp/packages/core-localization/src/locales/en.ts +++ b/webapp/packages/core-localization/src/locales/en.ts @@ -83,6 +83,8 @@ export default [ ['ui_rename_processing', 'Renaming...'], ['ui_interval', 'Interval'], ['ui_name', 'Name'], + ['ui_value', 'Value'], + ['ui_description', 'Description'], ['ui_cant_delete_item', "This item can't be deleted"], ['ui_no_items_placeholder', 'There are no items yet.'], ['ui_search_no_result_placeholder', 'No results have been found.'], diff --git a/webapp/packages/core-localization/src/locales/fr.ts b/webapp/packages/core-localization/src/locales/fr.ts index 0ec6f36c78..53b9f61f9f 100644 --- a/webapp/packages/core-localization/src/locales/fr.ts +++ b/webapp/packages/core-localization/src/locales/fr.ts @@ -54,14 +54,14 @@ export default [ ['ui_data_saving_error', 'Erreur de sauvegarde'], ['ui_data_remove_confirmation', 'Confirmation de suppression'], ['ui_data_delete_confirmation', 'Confirmation de suppression'], - ['ui_no_matches_placeholder', 'Votre recherche n\'a retourné aucun résultat.'], + ['ui_no_matches_placeholder', "Votre recherche n'a retourné aucun résultat."], ['ui_information', 'Information'], ['ui_clipboard', 'Presse-papiers'], ['ui_copy_to_clipboard', 'Copier'], ['ui_copy_to_clipboard_copied', 'Copié'], ['ui_copy_to_clipboard_failed_to_copy', 'Échec de la copie'], ['ui_clipboard_access_denied_title', 'Accès au presse-papiers refusé'], - ['ui_clipboard_access_denied_message', 'Vous devez donner accès au presse-papiers pour utiliser certaines fonctionnalités de l\'application'], + ['ui_clipboard_access_denied_message', "Vous devez donner accès au presse-papiers pour utiliser certaines fonctionnalités de l'application"], ['ui_reveal_password', 'Afficher ou masquer le mot de passe'], ['ui_capslock_on', 'Verr Maj est activé'], ['ui_page_not_found', 'Page non trouvée'], @@ -77,8 +77,10 @@ export default [ ['ui_rename_processing', 'Renommage...'], ['ui_interval', 'Intervalle'], ['ui_name', 'Nom'], + ['ui_value', 'Value'], + ['ui_description', 'Description'], ['ui_cant_delete_item', 'Cet élément ne peut pas être supprimé'], - ['ui_no_items_placeholder', 'Il n\'y a pas encore d\'éléments.'], + ['ui_no_items_placeholder', "Il n'y a pas encore d'éléments."], ['ui_search_no_result_placeholder', 'Aucun résultat trouvé.'], ['ui_save_reminder', 'Vous avez des modifications non sauvegardées.'], ['ui_yes', 'Oui'], @@ -125,17 +127,17 @@ export default [ ['ui_export', 'Exporter'], ['ui_you', 'Vous'], - ['root_permission_denied', 'Vous n\'avez pas les permissions'], - ['root_permission_no_permission', 'Vous n\'avez pas la permission pour cette action'], - ['app_root_session_expire_warning_title', 'La session est sur le point d\'expirer'], + ['root_permission_denied', "Vous n'avez pas les permissions"], + ['root_permission_no_permission', "Vous n'avez pas la permission pour cette action"], + ['app_root_session_expire_warning_title', "La session est sur le point d'expirer"], ['app_root_session_expire_warning_message', 'Votre session expirera dans moins de 5 minutes. Pour continuer à travailler, fermez cette popup.'], ['app_root_session_expire_warning_button', 'Je suis ici'], ['app_root_session_expired_title', 'Session expirée'], ['app_root_session_expired_message', 'La session a expiré. Voulez-vous recharger ?'], ['app_root_session_expired_reload', 'Recharger'], - ['app_root_server_node_changed_title', 'L\'application a été relancée'], - ['app_root_server_node_changed_message', 'L\'application a été relancée. Veuillez recharger la page.'], + ['app_root_server_node_changed_title', "L'application a été relancée"], + ['app_root_server_node_changed_message', "L'application a été relancée. Veuillez recharger la page."], ['app_root_quota_exceeded', 'Quota dépassé'], ['app_root_event_permissions_changed_message', 'Vos permissions ont été modifiées'], - ['core_eventsLog_dbeaverErrorDetails', 'Détails de l\'erreur'], + ['core_eventsLog_dbeaverErrorDetails', "Détails de l'erreur"], ]; diff --git a/webapp/packages/core-localization/src/locales/it.ts b/webapp/packages/core-localization/src/locales/it.ts index 98c13ed403..aa67c32470 100644 --- a/webapp/packages/core-localization/src/locales/it.ts +++ b/webapp/packages/core-localization/src/locales/it.ts @@ -80,6 +80,8 @@ export default [ ['ui_rename_processing', 'Renaming...'], ['ui_interval', 'Interval'], ['ui_name', 'Name'], + ['ui_value', 'Value'], + ['ui_description', 'Description'], ['ui_cant_delete_item', "This item can't be deleted"], ['ui_no_items_placeholder', 'Non ci sono ancora elementi.'], ['ui_search_no_result_placeholder', 'Nessun risultato trovato.'], diff --git a/webapp/packages/core-localization/src/locales/ru.ts b/webapp/packages/core-localization/src/locales/ru.ts index f765a90b56..8a14f7ea36 100644 --- a/webapp/packages/core-localization/src/locales/ru.ts +++ b/webapp/packages/core-localization/src/locales/ru.ts @@ -78,6 +78,8 @@ export default [ ['ui_folder_new', 'Новая папка'], ['ui_rename_processing', 'Переименование...'], ['ui_name', 'Название'], + ['ui_value', 'Значение'], + ['ui_description', 'Описание'], ['ui_interval', 'Интервал'], ['ui_cant_delete_item', 'Этот элемент нельзя удалить'], ['ui_no_items_placeholder', 'Вы еще ничего не добавили.'], diff --git a/webapp/packages/core-localization/src/locales/zh.ts b/webapp/packages/core-localization/src/locales/zh.ts index c0c89bb2bc..8d497d42fc 100644 --- a/webapp/packages/core-localization/src/locales/zh.ts +++ b/webapp/packages/core-localization/src/locales/zh.ts @@ -79,6 +79,8 @@ export default [ ['ui_rename', '重命名'], ['ui_rename_processing', '重命名中...'], ['ui_name', '名称'], + ['ui_value', 'Value'], + ['ui_description', 'Description'], ['ui_interval', 'Interval'], ['ui_cant_delete_item', '此项目不能删除'], ['ui_no_items_placeholder', '还没有项目'], diff --git a/webapp/packages/core-sdk/src/queries/grid/getSqlDynamicTrace.gql b/webapp/packages/core-sdk/src/queries/grid/getSqlDynamicTrace.gql new file mode 100644 index 0000000000..a7990fcaa5 --- /dev/null +++ b/webapp/packages/core-sdk/src/queries/grid/getSqlDynamicTrace.gql @@ -0,0 +1,7 @@ +mutation getSqlDynamicTrace($projectId: ID, $connectionId: ID!, $contextId: ID!, $resultsId: ID!) { + trace: sqlGetDynamicTrace(projectId: $projectId, connectionId: $connectionId, contextId: $contextId, resultsId: $resultsId) { + name + value + description + } +} diff --git a/webapp/packages/core-sdk/src/queries/grid/getSqlExecuteTaskResults.gql b/webapp/packages/core-sdk/src/queries/grid/getSqlExecuteTaskResults.gql index 4b633ccfc1..9b1ef04431 100644 --- a/webapp/packages/core-sdk/src/queries/grid/getSqlExecuteTaskResults.gql +++ b/webapp/packages/core-sdk/src/queries/grid/getSqlExecuteTaskResults.gql @@ -41,6 +41,7 @@ mutation getSqlExecuteTaskResults($taskId: ID!) { hasRowIdentifier isSupportsDataFilter hasChildrenCollection + hasDynamicTrace } } } diff --git a/webapp/packages/core-sdk/src/queries/grid/updateResultsDataBatch.gql b/webapp/packages/core-sdk/src/queries/grid/updateResultsDataBatch.gql index 306137c418..a45a1ffa71 100644 --- a/webapp/packages/core-sdk/src/queries/grid/updateResultsDataBatch.gql +++ b/webapp/packages/core-sdk/src/queries/grid/updateResultsDataBatch.gql @@ -32,6 +32,7 @@ mutation updateResultsDataBatch( hasRowIdentifier isSupportsDataFilter hasChildrenCollection + hasDynamicTrace } } } diff --git a/webapp/packages/core-utils/src/ILoadableState.ts b/webapp/packages/core-utils/src/ILoadableState.ts index 455f5ce063..003610d1a7 100644 --- a/webapp/packages/core-utils/src/ILoadableState.ts +++ b/webapp/packages/core-utils/src/ILoadableState.ts @@ -7,15 +7,15 @@ */ export interface ILoadableState { + readonly promise?: Promise | null; + readonly exception?: (Error | null)[] | Error | null; lazy?: boolean; isLoading: () => boolean; isLoaded: () => boolean; isError: () => boolean; - readonly exception?: (Error | null)[] | Error | null; load: () => void | Promise; reload?: () => void | Promise; - promise?: Promise | null; isOutdated?: () => boolean; isCancelled?: () => boolean; cancel?: () => void; diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/.gitignore b/webapp/packages/plugin-data-viewer-result-trace-details/.gitignore new file mode 100644 index 0000000000..15bc16c7c3 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/.gitignore @@ -0,0 +1,17 @@ +# dependencies +/node_modules + +# testing +/coverage + +# production +/lib + +# misc +.DS_Store +.env* + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/package.json b/webapp/packages/plugin-data-viewer-result-trace-details/package.json new file mode 100644 index 0000000000..333f595dab --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/package.json @@ -0,0 +1,40 @@ +{ + "name": "@cloudbeaver/plugin-data-viewer-result-trace-details", + "sideEffects": [ + "src/**/*.css", + "src/**/*.scss", + "public/**/*" + ], + "version": "0.1.0", + "description": "", + "license": "Apache-2.0", + "main": "dist/index.js", + "scripts": { + "build": "tsc -b", + "clean": "rimraf --glob dist", + "lint": "eslint ./src/ --ext .ts,.tsx", + "lint-fix": "eslint ./src/ --ext .ts,.tsx --fix", + "validate-dependencies": "core-cli-validate-dependencies", + "update-ts-references": "yarn run clean && typescript-resolve-references" + }, + "dependencies": { + "@cloudbeaver/core-blocks": "^0", + "@cloudbeaver/core-di": "^0", + "@cloudbeaver/core-localization": "^0", + "@cloudbeaver/core-sdk": "^0", + "@cloudbeaver/core-theming": "^0", + "@cloudbeaver/core-utils": "^0", + "@cloudbeaver/plugin-data-viewer": "^0", + "@cloudbeaver/plugin-react-data-grid": "^0", + "mobx": "^6", + "mobx-react-lite": "^4", + "react": "^18" + }, + "peerDependencies": {}, + "devDependencies": { + "@cloudbeaver/core-theming": "^0", + "@types/react": "^18", + "typescript": "^5", + "typescript-plugin-css-modules": "^5" + } +} diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/public/icons/result_details.svg b/webapp/packages/plugin-data-viewer-result-trace-details/public/icons/result_details.svg new file mode 100644 index 0000000000..0c8019bbcb --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/public/icons/result_details.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/public/icons/result_details_m.svg b/webapp/packages/plugin-data-viewer-result-trace-details/public/icons/result_details_m.svg new file mode 100644 index 0000000000..43545d7e13 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/public/icons/result_details_m.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/public/icons/result_details_sm.svg b/webapp/packages/plugin-data-viewer-result-trace-details/public/icons/result_details_sm.svg new file mode 100644 index 0000000000..986c1a6014 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/public/icons/result_details_sm.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/DVResultTraceDetailsBootstrap.ts b/webapp/packages/plugin-data-viewer-result-trace-details/src/DVResultTraceDetailsBootstrap.ts new file mode 100644 index 0000000000..eaf8c607f6 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/DVResultTraceDetailsBootstrap.ts @@ -0,0 +1,37 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { importLazyComponent } from '@cloudbeaver/core-blocks'; +import { Bootstrap, injectable } from '@cloudbeaver/core-di'; +import { ResultDataFormat } from '@cloudbeaver/core-sdk'; +import { DataPresentationService, DataPresentationType } from '@cloudbeaver/plugin-data-viewer'; + +const DVResultTraceDetailsPresentation = importLazyComponent(() => + import('./DVResultTraceDetailsPresentation').then(module => module.DVResultTraceDetailsPresentation), +); + +@injectable() +export class DVResultTraceDetailsBootstrap extends Bootstrap { + constructor(private readonly dataPresentationService: DataPresentationService) { + super(); + } + + register() { + this.dataPresentationService.add({ + id: 'result-trace-details-presentation', + type: DataPresentationType.toolsPanel, + dataFormat: ResultDataFormat.Resultset, + icon: '/icons/result_details_sm.svg', + title: 'plugin_data_viewer_result_trace_details', + hidden(dataFormat, model, resultIndex) { + const result = model.getResult(resultIndex); + return !result?.data?.hasDynamicTrace; + }, + getPresentationComponent: () => DVResultTraceDetailsPresentation, + }); + } +} diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/DVResultTraceDetailsPresentation.tsx b/webapp/packages/plugin-data-viewer-result-trace-details/src/DVResultTraceDetailsPresentation.tsx new file mode 100644 index 0000000000..7d6aec6703 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/DVResultTraceDetailsPresentation.tsx @@ -0,0 +1,61 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { observer } from 'mobx-react-lite'; + +import { Container, TextPlaceholder, useAutoLoad, useS, useTranslate } from '@cloudbeaver/core-blocks'; +import { DynamicTraceProperty } from '@cloudbeaver/core-sdk'; +import type { DataPresentationComponent, IDatabaseResultSet } from '@cloudbeaver/plugin-data-viewer'; +import DataGrid, { Column } from '@cloudbeaver/plugin-react-data-grid'; + +import { HeaderCell } from './ResultTraceDetailsTable/HeaderCell'; +import { RESULT_TRACE_DETAILS_TABLE_THEME_BASE_STYLES } from './styles/styles'; +import { useResultTraceDetails } from './useResultTraceDetails'; + +const COLUMNS: Column[] = [ + { + key: 'name', + name: 'ui_name', + resizable: true, + renderCell: props =>
{props.row.name}
, + renderHeaderCell: props => , + }, + { + key: 'value', + name: 'ui_value', + resizable: true, + renderCell: props =>
{props.row.value ?? ''}
, + renderHeaderCell: props => , + }, + { + key: 'description', + name: 'ui_description', + resizable: true, + renderCell: props =>
{props.row.description ?? ''}
, + renderHeaderCell: props => , + }, +]; + +export const DVResultTraceDetailsPresentation: DataPresentationComponent = observer( + function DVResultTraceDetailsPresentation({ model, resultIndex }) { + const translate = useTranslate(); + const state = useResultTraceDetails(model, resultIndex); + + useS(RESULT_TRACE_DETAILS_TABLE_THEME_BASE_STYLES); + useAutoLoad(DVResultTraceDetailsPresentation, state, undefined, undefined, true); + + if (!state.trace?.length) { + return {translate('plugin_data_viewer_result_trace_no_data_placeholder')}; + } + + return ( + + row.name} columns={COLUMNS} rowHeight={30} /> + + ); + }, +); diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/DVResultTraceDetailsService.ts b/webapp/packages/plugin-data-viewer-result-trace-details/src/DVResultTraceDetailsService.ts new file mode 100644 index 0000000000..f78ab4f7a5 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/DVResultTraceDetailsService.ts @@ -0,0 +1,25 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { injectable } from '@cloudbeaver/core-di'; +import { GraphQLService } from '@cloudbeaver/core-sdk'; + +@injectable() +export class DVResultTraceDetailsService { + constructor(private readonly graphQLService: GraphQLService) {} + + async getTraceDetails(projectId: string, connectionId: string, contextId: string, resultsId: string) { + const trace = await this.graphQLService.sdk.getSqlDynamicTrace({ + projectId, + connectionId, + contextId, + resultsId, + }); + + return trace; + } +} diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/LocaleService.ts b/webapp/packages/plugin-data-viewer-result-trace-details/src/LocaleService.ts new file mode 100644 index 0000000000..f8618985f9 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/LocaleService.ts @@ -0,0 +1,37 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { Bootstrap, injectable } from '@cloudbeaver/core-di'; +import { LocalizationService } from '@cloudbeaver/core-localization'; + +@injectable() +export class LocaleService extends Bootstrap { + constructor(private readonly localizationService: LocalizationService) { + super(); + } + + register(): void | Promise { + this.localizationService.addProvider(this.provider.bind(this)); + } + + load(): void | Promise {} + + private async provider(locale: string) { + switch (locale) { + case 'ru': + return (await import('./locales/ru')).default; + case 'it': + return (await import('./locales/it')).default; + case 'zh': + return (await import('./locales/zh')).default; + case 'fr': + return (await import('./locales/fr')).default; + default: + return (await import('./locales/en')).default; + } + } +} diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/ResultTraceDetailsTable/HeaderCell.tsx b/webapp/packages/plugin-data-viewer-result-trace-details/src/ResultTraceDetailsTable/HeaderCell.tsx new file mode 100644 index 0000000000..c8efc1eb8e --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/ResultTraceDetailsTable/HeaderCell.tsx @@ -0,0 +1,18 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { observer } from 'mobx-react-lite'; + +import { useTranslate } from '@cloudbeaver/core-blocks'; +import { DynamicTraceProperty } from '@cloudbeaver/core-sdk'; +import type { RenderHeaderCellProps } from '@cloudbeaver/plugin-react-data-grid'; + +export const HeaderCell = observer>(function HeaderCell(props) { + const translate = useTranslate(); + + return
{typeof props.column.name === 'string' ? translate(props.column.name) : props.column.name}
; +}); diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/index.ts b/webapp/packages/plugin-data-viewer-result-trace-details/src/index.ts new file mode 100644 index 0000000000..51b8e7f461 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/index.ts @@ -0,0 +1,11 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { dataViewerResultTraceDetailsPlugin } from './manifest'; + +export default dataViewerResultTraceDetailsPlugin; +export { dataViewerResultTraceDetailsPlugin }; diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/en.ts b/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/en.ts new file mode 100644 index 0000000000..fa1dbc8fe5 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/en.ts @@ -0,0 +1,11 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +export default [ + ['plugin_data_viewer_result_trace_details', 'Result details'], + ['plugin_data_viewer_result_trace_no_data_placeholder', 'No trace details were found'], +]; diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/fr.ts b/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/fr.ts new file mode 100644 index 0000000000..fa1dbc8fe5 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/fr.ts @@ -0,0 +1,11 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +export default [ + ['plugin_data_viewer_result_trace_details', 'Result details'], + ['plugin_data_viewer_result_trace_no_data_placeholder', 'No trace details were found'], +]; diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/it.ts b/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/it.ts new file mode 100644 index 0000000000..fa1dbc8fe5 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/it.ts @@ -0,0 +1,11 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +export default [ + ['plugin_data_viewer_result_trace_details', 'Result details'], + ['plugin_data_viewer_result_trace_no_data_placeholder', 'No trace details were found'], +]; diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/ru.ts b/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/ru.ts new file mode 100644 index 0000000000..060c0ea8b7 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/ru.ts @@ -0,0 +1,11 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +export default [ + ['plugin_data_viewer_result_trace_details', 'Детализация результата'], + ['plugin_data_viewer_result_trace_no_data_placeholder', 'Детали результата не найдены'], +]; diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/zh.ts b/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/zh.ts new file mode 100644 index 0000000000..fa1dbc8fe5 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/locales/zh.ts @@ -0,0 +1,11 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +export default [ + ['plugin_data_viewer_result_trace_details', 'Result details'], + ['plugin_data_viewer_result_trace_no_data_placeholder', 'No trace details were found'], +]; diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/manifest.ts b/webapp/packages/plugin-data-viewer-result-trace-details/src/manifest.ts new file mode 100644 index 0000000000..8baf9b5970 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/manifest.ts @@ -0,0 +1,20 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import type { PluginManifest } from '@cloudbeaver/core-di'; + +export const dataViewerResultTraceDetailsPlugin: PluginManifest = { + info: { + name: 'Result trace details data viewer plugin', + }, + + providers: [ + () => import('./DVResultTraceDetailsBootstrap').then(m => m.DVResultTraceDetailsBootstrap), + () => import('./DVResultTraceDetailsService').then(m => m.DVResultTraceDetailsService), + () => import('./LocaleService').then(m => m.LocaleService), + ], +}; diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/styles/styles.ts b/webapp/packages/plugin-data-viewer-result-trace-details/src/styles/styles.ts new file mode 100644 index 0000000000..5ac1acd7f4 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/styles/styles.ts @@ -0,0 +1,23 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import type { ThemeSelector } from '@cloudbeaver/core-theming'; + +export const RESULT_TRACE_DETAILS_TABLE_THEME_BASE_STYLES: ThemeSelector = async theme => { + let styles: any; + + switch (theme) { + case 'dark': + styles = await import('./themes/dark.module.scss'); + break; + default: + styles = await import('./themes/light.module.scss'); + break; + } + + return [styles.default]; +}; diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/styles/themes/_base-result-trace-details-grid.scss b/webapp/packages/plugin-data-viewer-result-trace-details/src/styles/themes/_base-result-trace-details-grid.scss new file mode 100644 index 0000000000..61d19ba709 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/styles/themes/_base-result-trace-details-grid.scss @@ -0,0 +1,55 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +@import '@cloudbeaver/core-theming/src/styles/branding'; + +@mixin base-result-trace-details-grid() { + :global { + .result-trace-details-grid-container { + @include mdc-typography(caption); + + :global(input) { + @include mdc-typography(caption); + } + } + .result-trace-details-grid-container:focus-within { + .rdg-cell:global([aria-selected='true']) { + box-shadow: inset 0 0 0 1px #0091ea !important; + } + } + .result-trace-details-grid-theme { + @include mdc-typography(caption); + @include mdc-theme-prop(color, on-surface, true); + + .rdg-header-row { + @include mdc-theme-prop(background-color, surface, true); + } + + .rdg-row { + @include mdc-theme-prop(border-color, background, true); + } + + .rdg-row:hover { + @include stripes-background($mdc-theme-sub-secondary, true); + } + + .rdg-cell { + @include mdc-theme-prop(border-color, background, true); + + &:focus { + outline: 0 !important; + } + + &:global([aria-selected='true']) { + outline: 0 !important; + box-shadow: inset 0 0 0 1px #808080 !important; + } + } + } + } +} diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/styles/themes/dark.module.scss b/webapp/packages/plugin-data-viewer-result-trace-details/src/styles/themes/dark.module.scss new file mode 100644 index 0000000000..0a5174edc0 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/styles/themes/dark.module.scss @@ -0,0 +1,18 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +@import '@cloudbeaver/core-theming/src/styles/theme-dark'; +@import './base-result-trace-details-grid'; + +:global .#{$theme-class} { + @include base-result-trace-details-grid; + + .result-trace-details-grid-theme { + --rdg-color-scheme: dark; + } +} diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/styles/themes/light.module.scss b/webapp/packages/plugin-data-viewer-result-trace-details/src/styles/themes/light.module.scss new file mode 100644 index 0000000000..a5e6a645eb --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/styles/themes/light.module.scss @@ -0,0 +1,18 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +@import '@cloudbeaver/core-theming/src/styles/theme-light'; +@import './base-result-trace-details-grid'; + +:global .#{$theme-class} { + @include base-result-trace-details-grid; + + .result-trace-details-grid-theme { + --rdg-color-scheme: light; + } +} diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/src/useResultTraceDetails.tsx b/webapp/packages/plugin-data-viewer-result-trace-details/src/useResultTraceDetails.tsx new file mode 100644 index 0000000000..2ea90909fa --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/src/useResultTraceDetails.tsx @@ -0,0 +1,116 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2024 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +import { computed, observable } from 'mobx'; + +import { useObservableRef } from '@cloudbeaver/core-blocks'; +import { useService } from '@cloudbeaver/core-di'; +import type { DynamicTraceProperty, GetSqlDynamicTraceMutation } from '@cloudbeaver/core-sdk'; +import { ILoadableState, isContainsException } from '@cloudbeaver/core-utils'; +import { + DatabaseMetadataAction, + IDatabaseDataModel, + IDatabaseResultSet, + IResultSetElementKey, + ResultSetCacheAction, +} from '@cloudbeaver/plugin-data-viewer'; + +import { DVResultTraceDetailsService } from './DVResultTraceDetailsService'; + +type ResultTraceDetailsPromise = Promise; + +interface MetadataState { + promise: ResultTraceDetailsPromise | null; + exception: Error | null; +} +interface State extends ILoadableState { + readonly trace: DynamicTraceProperty[] | undefined; + model: IDatabaseDataModel; + resultIndex: number; + cache: ResultSetCacheAction; + metadataState: MetadataState; +} + +const RESULT_TRACE_DETAILS_CACHE_KEY = Symbol('@cache/ResultTraceDetails'); +const RESULT_TRACE_DETAILS_METADATA_KEY = 'result-trace-details-panel'; +// @TODO Probably we want to implement a cache behavior that will only use Scope Key as sometimes we want +// a cache that only exists as long as result exists but dont want to specify row/column indexes +const FAKE_ELEMENT_KEY: IResultSetElementKey = { + column: { index: Number.MAX_SAFE_INTEGER }, + row: { index: Number.MAX_SAFE_INTEGER, subIndex: Number.MAX_SAFE_INTEGER }, +}; + +export function useResultTraceDetails(model: IDatabaseDataModel, resultIndex: number) { + const dvResultTraceDetailsService = useService(DVResultTraceDetailsService); + const cache = model.source.getAction(resultIndex, ResultSetCacheAction); + const metadataAction = model.source.getAction(resultIndex, DatabaseMetadataAction); + + const metadataState = metadataAction.get(RESULT_TRACE_DETAILS_METADATA_KEY, () => + observable({ + promise: null, + exception: null, + }), + ); + + const state = useObservableRef( + () => ({ + get trace(): DynamicTraceProperty[] | undefined { + return this.cache.get(FAKE_ELEMENT_KEY, RESULT_TRACE_DETAILS_CACHE_KEY); + }, + get promise(): ResultTraceDetailsPromise | null { + return this.metadataState.promise; + }, + get exception(): Error | null { + return this.metadataState.exception; + }, + isError() { + return isContainsException(this.exception); + }, + isLoaded() { + return this.trace !== undefined; + }, + async load() { + const result = this.model.getResult(this.resultIndex); + + try { + if (!result?.id) { + throw new Error('Result is not found'); + } + + if (this.metadataState.promise) { + return; + } + + this.metadataState.exception = null; + + this.metadataState.promise = dvResultTraceDetailsService.getTraceDetails( + result.projectId, + result.connectionId, + result.contextId, + result.id, + ); + + const { trace } = await this.metadataState.promise; + + this.cache.set(FAKE_ELEMENT_KEY, RESULT_TRACE_DETAILS_CACHE_KEY, trace); + } catch (exception: any) { + this.metadataState.exception = exception; + } finally { + this.metadataState.promise = null; + } + }, + }), + { + promise: computed, + exception: computed, + trace: computed, + }, + { model, resultIndex, cache, metadataState }, + ); + + return state; +} diff --git a/webapp/packages/plugin-data-viewer-result-trace-details/tsconfig.json b/webapp/packages/plugin-data-viewer-result-trace-details/tsconfig.json new file mode 100644 index 0000000000..abf98fd422 --- /dev/null +++ b/webapp/packages/plugin-data-viewer-result-trace-details/tsconfig.json @@ -0,0 +1,49 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo" + }, + "references": [ + { + "path": "../core-blocks/tsconfig.json" + }, + { + "path": "../core-di/tsconfig.json" + }, + { + "path": "../core-localization/tsconfig.json" + }, + { + "path": "../core-sdk/tsconfig.json" + }, + { + "path": "../core-theming/tsconfig.json" + }, + { + "path": "../core-theming/tsconfig.json" + }, + { + "path": "../core-utils/tsconfig.json" + }, + { + "path": "../plugin-data-viewer/tsconfig.json" + }, + { + "path": "../plugin-react-data-grid/tsconfig.json" + } + ], + "include": [ + "__custom_mocks__/**/*", + "src/**/*", + "src/**/*.json", + "src/**/*.css", + "src/**/*.scss" + ], + "exclude": [ + "**/node_modules", + "lib/**/*", + "dist/**/*" + ] +} diff --git a/webapp/packages/product-default/package.json b/webapp/packages/product-default/package.json index 53b9c603c5..d9ac9077b1 100644 --- a/webapp/packages/product-default/package.json +++ b/webapp/packages/product-default/package.json @@ -44,6 +44,7 @@ "@cloudbeaver/plugin-data-spreadsheet-new": "^0", "@cloudbeaver/plugin-data-viewer": "^0", "@cloudbeaver/plugin-data-viewer-result-set-grouping": "^0", + "@cloudbeaver/plugin-data-viewer-result-trace-details": "^0", "@cloudbeaver/plugin-datasource-context-switch": "^0", "@cloudbeaver/plugin-datasource-transaction-manager": "^0", "@cloudbeaver/plugin-ddl-viewer": "^0", diff --git a/webapp/packages/product-default/src/index.ts b/webapp/packages/product-default/src/index.ts index b11cc39113..a5925b7167 100644 --- a/webapp/packages/product-default/src/index.ts +++ b/webapp/packages/product-default/src/index.ts @@ -23,6 +23,7 @@ import { dataImportPluginManifest } from '@cloudbeaver/plugin-data-import'; import { dataSpreadsheetNewManifest } from '@cloudbeaver/plugin-data-spreadsheet-new'; import { dataViewerManifest } from '@cloudbeaver/plugin-data-viewer'; import { dvResultSetGroupingPlugin } from '@cloudbeaver/plugin-data-viewer-result-set-grouping'; +import { dataViewerResultTraceDetailsPlugin } from '@cloudbeaver/plugin-data-viewer-result-trace-details'; import { datasourceContextSwitchPluginManifest } from '@cloudbeaver/plugin-datasource-context-switch'; import { datasourceTransactionManagerPlugin } from '@cloudbeaver/plugin-datasource-transaction-manager'; import ddlViewer from '@cloudbeaver/plugin-ddl-viewer'; @@ -82,6 +83,7 @@ const PLUGINS: PluginManifest[] = [ dataExportManifest, dataImportPluginManifest, dataViewerManifest, + dataViewerResultTraceDetailsPlugin, dvResultSetGroupingPlugin, gisViewer, ddlViewer, diff --git a/webapp/packages/product-default/tsconfig.json b/webapp/packages/product-default/tsconfig.json index 4b6043c654..cf1f6b15b9 100644 --- a/webapp/packages/product-default/tsconfig.json +++ b/webapp/packages/product-default/tsconfig.json @@ -60,6 +60,9 @@ { "path": "../plugin-data-viewer-result-set-grouping/tsconfig.json" }, + { + "path": "../plugin-data-viewer-result-trace-details/tsconfig.json" + }, { "path": "../plugin-data-viewer/tsconfig.json" },