Async and await implemented for loadEntities

This commit is contained in:
vaidankarswapnil 2021-08-16 13:27:30 +05:30
parent 046e6eb5a4
commit b7cb0b55a6
8 changed files with 508 additions and 483 deletions

View File

@ -535,11 +535,11 @@ input::-webkit-inner-spin-button {
}
.query-document-detail-list {
overflow-x: hidden;
// overflow-x: hidden;
height: 100%;
}
.query-table-clause-container {
max-height: 80px;
max-height: 150px;
overflow: scroll;
overflow-x: hidden;
}
@ -569,6 +569,14 @@ input::-webkit-inner-spin-button {
}
}
}
.noData {
background-color: #e3e2e6;
color: #e3e2e6;
padding-top: 1px;
height: 100%;
width: 100%;
}
// .pagination > li > div {
// }

View File

@ -1,8 +1,10 @@
import { IDropdownOption, Image, Label, Stack, Text, TextField } from "@fluentui/react";
import { useBoolean } from "@fluentui/react-hooks";
import React, { FunctionComponent, useEffect, useState } from "react";
import * as _ from "underscore";
import AddPropertyIcon from "../../../../images/Add-property.svg";
import RevertBackIcon from "../../../../images/RevertBack.svg";
import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils";
import { TableEntity } from "../../../Common/TableEntity";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { userContext } from "../../../UserContext";
@ -11,6 +13,7 @@ import * as DataTableUtilities from "../../Tables/DataTable/DataTableUtilities";
import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListViewModel";
import * as Entities from "../../Tables/Entities";
import { CassandraAPIDataClient, CassandraTableKey, TableDataClient } from "../../Tables/TableDataClient";
import * as TableEntityProcessor from "../../Tables/TableEntityProcessor";
import * as Utilities from "../../Tables/Utilities";
import NewQueryTablesTab from "../../Tabs/QueryTablesTab/QueryTablesTab";
import { RightPaneForm, RightPaneFormProps } from "../RightPaneForm/RightPaneForm";
@ -115,10 +118,62 @@ export const AddTableEntityPanel: FunctionComponent<AddTableEntityPanelProps> =
setIsExecuting(true);
const entity: Entities.ITableEntity = entityFromAttributes(entities);
await tableDataClient.createDocument(queryTablesTab.collection, entity);
// await tableEntityListViewModel.addEntityToCache(newEntity);
reloadEntities();
closeSidePanel();
const newEntity: Entities.ITableEntity = await tableDataClient.createDocument(queryTablesTab.collection, entity);
try {
await tableEntityListViewModel.addEntityToCache(newEntity);
// if (!tryInsertNewHeaders(tableEntityListViewModel, newEntity)) {
// tableEntityListViewModel.redrawTableThrottled();
reloadEntities();
setFormError("");
// }
} catch (error) {
const errorMessage = getErrorMessage(error);
setFormError(errorMessage);
handleError(errorMessage, "AddTableRow");
throw error;
} finally {
setIsExecuting(false);
}
// try {
// await tableDataClient.createDocument(queryTablesTab.collection, entity);
// reloadEntities();
// setFormError("");
// closeSidePanel();
// } catch (error) {
// const errorMessage = getErrorMessage(error);
// setFormError(errorMessage);
// handleError(errorMessage, "AddTableRow");
// } finally {
// setIsExecuting(false);
// }
};
const tryInsertNewHeaders = (viewModel: TableEntityListViewModel, newEntity: Entities.ITableEntity): boolean => {
let newHeaders: string[] = [];
const keys = Object.keys(newEntity);
keys &&
keys.forEach((key: string) => {
if (
!_.contains(viewModel.headers, key) &&
key !== TableEntityProcessor.keyProperties.attachments &&
key !== TableEntityProcessor.keyProperties.etag &&
key !== TableEntityProcessor.keyProperties.resourceId &&
key !== TableEntityProcessor.keyProperties.self &&
(!(userContext.apiType === "Cassandra") || key !== TableConstants.EntityKeyNames.RowKey)
) {
newHeaders.push(key);
}
});
let newHeadersInserted = false;
if (newHeaders.length) {
if (!DataTableUtilities.checkForDefaultHeader(viewModel.headers)) {
newHeaders = viewModel.headers.concat(newHeaders);
}
viewModel.updateHeaders(newHeaders, /* notifyColumnChanges */ true, /* enablePrompt */ false);
newHeadersInserted = true;
}
return newHeadersInserted;
};
/* Add new entity row */

View File

@ -4,10 +4,12 @@ import React, { FunctionComponent, useEffect, useState } from "react";
import * as _ from "underscore";
import AddPropertyIcon from "../../../../images/Add-property.svg";
import RevertBackIcon from "../../../../images/RevertBack.svg";
import { getErrorMessage, handleError } from "../../../Common/ErrorHandlingUtils";
import { TableEntity } from "../../../Common/TableEntity";
import { useSidePanel } from "../../../hooks/useSidePanel";
import { userContext } from "../../../UserContext";
import * as TableConstants from "../../Tables/Constants";
import * as DataTableUtilities from "../../Tables/DataTable/DataTableUtilities";
import TableEntityListViewModel from "../../Tables/DataTable/TableEntityListViewModel";
import * as Entities from "../../Tables/Entities";
import { CassandraAPIDataClient, TableDataClient } from "../../Tables/TableDataClient";
@ -213,9 +215,59 @@ export const EditTableEntityPanel: FunctionComponent<EditTableEntityPanelProps>
const entity: Entities.ITableEntity = entityFromAttributes(entities);
const newTableDataClient = userContext.apiType === "Cassandra" ? cassandraApiClient : tableDataClient;
const originalDocumentData = userContext.apiType === "Cassandra" ? originalDocument[0] : originalDocument;
await newTableDataClient.updateDocument(queryTablesTab.collection, originalDocumentData, entity);
reloadEntities();
closeSidePanel();
// await newTableDataClient.updateDocument(queryTablesTab.collection, originalDocumentData, entity);
try {
const newEntity: Entities.ITableEntity = await newTableDataClient.updateDocument(
queryTablesTab.collection,
originalDocumentData,
entity
);
await tableEntityListViewModel.updateCachedEntity(newEntity);
// if (!tryInsertNewHeaders(tableEntityListViewModel, newEntity)) {
// tableEntityListViewModel.redrawTableThrottled();
reloadEntities();
closeSidePanel();
// }
tableEntityListViewModel.selected.removeAll();
tableEntityListViewModel.selected.push(newEntity);
} catch (error) {
const errorMessage = getErrorMessage(error);
handleError(errorMessage, "EditTableRow");
throw error;
} finally {
setIsExecuting(false);
}
// reloadEntities();
// closeSidePanel();
};
const tryInsertNewHeaders = (viewModel: TableEntityListViewModel, newEntity: Entities.ITableEntity): boolean => {
let newHeaders: string[] = [];
const keys = Object.keys(newEntity);
keys &&
keys.forEach((key: string) => {
if (
!_.contains(viewModel.headers, key) &&
key !== TableEntityProcessor.keyProperties.attachments &&
key !== TableEntityProcessor.keyProperties.etag &&
key !== TableEntityProcessor.keyProperties.resourceId &&
key !== TableEntityProcessor.keyProperties.self &&
(!(userContext.apiType === "Cassandra") || key !== TableConstants.EntityKeyNames.RowKey)
) {
newHeaders.push(key);
}
});
let newHeadersInserted = false;
if (newHeaders.length) {
if (!DataTableUtilities.checkForDefaultHeader(viewModel.headers)) {
newHeaders = viewModel.headers.concat(newHeaders);
}
viewModel.updateHeaders(newHeaders, /* notifyColumnChanges */ true, /* enablePrompt */ false);
newHeadersInserted = true;
}
return newHeadersInserted;
};
// Add new entity row

View File

@ -170,6 +170,10 @@ abstract class DataTableViewModel {
}
protected renderPage(startIndex: number, pageSize: number) {
console.log(
"🚀 ~ file: DataTableViewModel.ts ~ line 179 ~ DataTableViewModel ~ renderPage ~ this.cache.data",
this.cache.data
);
var endIndex = pageSize < 0 ? this.cache.length : startIndex + pageSize;
var renderData = this.cache.data.slice(startIndex, endIndex);

View File

@ -148,6 +148,97 @@ export default class TableEntityListViewModel extends DataTableViewModel {
this.headers = newHeaders;
}
public async a(): Promise<Entities.ITableEntity[]> {
const a = await this.b();
console.log("🚀 ~ file: TableEntityListViewModel.ts ~ line 153 ~ TableEntityListViewModel ~ a ~ a", a);
return a;
}
public async b(): Promise<Entities.ITableEntity[]> {
const b = await this.c();
console.log("🚀 ~ file: TableEntityListViewModel.ts ~ line 157 ~ TableEntityListViewModel ~ b ~ b", b);
return b;
}
public async c(tableQuery?: Entities.ITableQuery, downloadSize?: number): Promise<any> {
var c: any;
var d: any;
if (this._documentIterator && this.continuationToken) {
// TODO handle Cassandra case
d = await this._documentIterator.fetchNext();
let entities: Entities.ITableEntity[] = TableEntityProcessor.convertDocumentsToEntities(d.resources);
let finalEntities: IListTableEntitiesSegmentedResult = <IListTableEntitiesSegmentedResult>{
Results: entities,
ContinuationToken: this._documentIterator.hasMoreResults(),
};
c = finalEntities;
// .fetchNext()
// .then((response) => response.resources)
// .then((documents: any[]) => {
// let entities: Entities.ITableEntity[] = TableEntityProcessor.convertDocumentsToEntities(documents);
// let finalEntities: IListTableEntitiesSegmentedResult = <IListTableEntitiesSegmentedResult>{
// Results: entities,
// ContinuationToken: this._documentIterator.hasMoreResults(),
// };
// return Promise.resolve(finalEntities);
// });
} else {
c = await this.queryTablesTab.container.tableDataClient.queryDocuments(
this.queryTablesTab.collection,
this.sqlQuery(),
true
);
}
// const c = await this.queryTablesTab.container.tableDataClient.queryDocuments(
// this.queryTablesTab.collection,
// this.sqlQuery(),
// true
// );
const result = c;
if (result) {
if (!this._documentIterator) {
this._documentIterator = result.iterator;
}
var actualDownloadSize: number = 0;
var entities = result.Results;
actualDownloadSize = entities.length;
this.continuationToken = this.isCancelled ? null : result.ContinuationToken;
if (!this.continuationToken) {
this.allDownloaded = true;
}
if (this.isCacheValid(this.tableQuery)) {
// Append to cache.
this.cache.data = this.cache.data.concat(entities.slice(0));
} else {
// Create cache.
this.cache.data = entities;
console.log(
"🚀 ~ file: TableEntityListViewModel.ts ~ line 797 ~ TableEntityListViewModel ~ .then ~ this.cache.data",
this.cache.data
);
}
this.cache.tableQuery = this.tableQuery;
this.cache.serverCallInProgress = false;
var nextDownloadSize: number = this.downloadSize - actualDownloadSize;
if (nextDownloadSize === 0 && this.tableQuery.top) {
this.allDownloaded = true;
}
if (this.allDownloaded || nextDownloadSize === 0) {
return Promise.resolve(this.cache.data);
}
return this.c(this.tableQuery, nextDownloadSize);
}
console.log("🚀 ~ file: TableEntityListViewModel.ts ~ line 161 ~ TableEntityListViewModel ~ c ~ data", c);
return this.cache.data;
}
/**
* This callback function called by datatable to fetch the next page of data and render.
* sSource - ajax URL of data source, ignored in our case as we are not using ajax.
@ -155,10 +246,10 @@ export default class TableEntityListViewModel extends DataTableViewModel {
* fnCallback - is the render callback with data to render.
* oSetting: current settings used for table initialization.
*/
public renderNextPageAndupdateCache(sSource?: any, aoData?: any, fnCallback?: any): Promise<void> {
var tablePageSize: number = 100;
public async renderNextPageAndupdateCache(): Promise<Entities.ITableEntity[]> {
var tablePageSize: number;
var prefetchNeeded = true;
// var columnSortOrder: any;
// Threshold(pages) for triggering cache prefetch.
// If number remaining pages in cache falls below prefetchThreshold prefetch will be triggered.
var prefetchThreshold = 10;
@ -169,12 +260,8 @@ export default class TableEntityListViewModel extends DataTableViewModel {
// Check if prefetch needed.
if (this.tablePageStartIndex + tablePageSize <= this.cache.length || this.allDownloaded) {
prefetchNeeded = false;
// if (columnSortOrder && (!this.cache.sortOrder || !_.isEqual(this.cache.sortOrder, columnSortOrder))) {
// this.sortColumns(columnSortOrder, oSettings);
// }
this.tablePageStartIndex = 0;
this.renderPage(this.tablePageStartIndex, tablePageSize);
// this.renderPage(0, 100);
this.renderPage(this.tablePageStartIndex, this.cache.length);
if (
!this.allDownloaded &&
this.tablePageStartIndex > 0 && // This is a case now that we can hit this as we re-construct table when we update column
@ -191,24 +278,17 @@ export default class TableEntityListViewModel extends DataTableViewModel {
if (prefetchNeeded) {
var downloadSize = tableQuery.top || this.downloadSize;
this.prefetchAndRender(
tableQuery,
this.tablePageStartIndex,
tablePageSize,
downloadSize
// draw,
// oSettings,
// columnSortOrder
);
return await this.prefetchAndRender(tableQuery, 0, tablePageSize, downloadSize);
} else {
return this.cache.data;
}
return undefined;
}
public addEntityToCache(entity: Entities.ITableEntity): Q.Promise<any> {
// Delay the add operation if we are fetching data from server, so as to avoid race condition.
if (this.cache.serverCallInProgress) {
return Utilities.delay(this.pollingInterval).then(() => {
return this.updateCachedEntity(entity);
Utilities.delay(this.pollingInterval).then(() => {
this.updateCachedEntity(entity);
});
}
@ -224,8 +304,8 @@ export default class TableEntityListViewModel extends DataTableViewModel {
public updateCachedEntity(entity: Entities.ITableEntity): Q.Promise<any> {
// Delay the add operation if we are fetching data from server, so as to avoid race condition.
if (this.cache.serverCallInProgress) {
return Utilities.delay(this.pollingInterval).then(() => {
return this.updateCachedEntity(entity);
Utilities.delay(this.pollingInterval).then(() => {
this.updateCachedEntity(entity);
});
}
var oldEntityIndex: number = _.findIndex(
@ -245,8 +325,8 @@ export default class TableEntityListViewModel extends DataTableViewModel {
// Delay the remove operation if we are fetching data from server, so as to avoid race condition.
if (this.cache.serverCallInProgress) {
return Utilities.delay(this.pollingInterval).then(() => {
return this.removeEntitiesFromCache(entities);
Utilities.delay(this.pollingInterval).then(() => {
this.removeEntitiesFromCache(entities);
});
}
@ -354,87 +434,89 @@ export default class TableEntityListViewModel extends DataTableViewModel {
});
}
private prefetchAndRender(
private async prefetchAndRender(
tableQuery: Entities.ITableQuery,
tablePageStartIndex: number,
tablePageSize: number,
downloadSize: number
// draw: number,/
// oSettings: any,
// columnSortOrder: any
): void {
): Promise<Entities.ITableEntity[]> {
console.log("🚀 ~ file: TableEntityListViewModel.ts ~ line 366 ~ TableEntityListViewModel ~ prefetchAndRender");
this.queryErrorMessage(null);
if (this.cache.serverCallInProgress) {
return;
return undefined;
}
this.prefetchData(tableQuery, downloadSize, /* currentRetry */ 0)
.then((result: IListTableEntitiesSegmentedResult) => {
if (!result) {
return;
try {
const result = await this.prefetchData(tableQuery, downloadSize, /* currentRetry */ 0);
if (!result) {
return undefined;
}
// Cache is assigned using prefetchData
var entities = this.cache.data;
console.log(
"🚀 ~ file: TableEntityListViewModel.ts ~ line 430 ~ TableEntityListViewModel ~ .then ~ entities outside",
entities
);
if (userContext.apiType === "Cassandra" && DataTableUtilities.checkForDefaultHeader(this.headers)) {
(<CassandraAPIDataClient>this.queryTablesTab.container.tableDataClient)
.getTableSchema(this.queryTablesTab.collection)
.then((headers: CassandraTableKey[]) => {
console.log(
"🚀 ~ file: TableEntityListViewModel.ts ~ line 438 ~ TableEntityListViewModel ~ .then ~ headers",
headers
);
this.updateHeaders(
headers.map((header) => header.property),
true
);
});
console.log(
"🚀 ~ file: TableEntityListViewModel.ts ~ line 430 ~ TableEntityListViewModel ~ .then ~ entities inside",
entities
);
} else {
var selectedHeadersUnion: string[] = DataTableUtilities.getPropertyIntersectionFromTableEntities(
entities,
userContext.apiType === "Cassandra"
);
var newHeaders: string[] = _.difference(selectedHeadersUnion, this.headers);
if (newHeaders.length > 0) {
// Any new columns found will be added into headers array, which will trigger a re-render of the DataTable.
// So there is no need to call it here.
this.updateHeaders(newHeaders, /* notifyColumnChanges */ true);
}
}
this.renderPage(tablePageStartIndex, entities.length);
var entities = this.cache.data;
if (userContext.apiType === "Cassandra" && DataTableUtilities.checkForDefaultHeader(this.headers)) {
(<CassandraAPIDataClient>this.queryTablesTab.container.tableDataClient)
.getTableSchema(this.queryTablesTab.collection)
.then((headers: CassandraTableKey[]) => {
this.updateHeaders(
headers.map((header) => header.property),
true
);
});
} else {
var selectedHeadersUnion: string[] = DataTableUtilities.getPropertyIntersectionFromTableEntities(
entities,
userContext.apiType === "Cassandra"
);
var newHeaders: string[] = _.difference(selectedHeadersUnion, this.headers);
if (newHeaders.length > 0) {
// Any new columns found will be added into headers array, which will trigger a re-render of the DataTable.
// So there is no need to call it here.
this.updateHeaders(newHeaders, /* notifyColumnChanges */ true);
} else {
if (columnSortOrder) {
this.sortColumns(columnSortOrder, oSettings);
}
// this.renderPage(renderCallBack, draw, tablePageStartIndex, tablePageSize, oSettings);
}
this.renderPage(0, 100);
}
if (result.ExceedMaximumRetries) {
var message: string = "We are having trouble getting your data. Please try again."; // localize
}
})
.catch((error: any) => {
const parsedErrors = parseError(error);
var errors = parsedErrors.map((error) => {
return <ViewModels.QueryError>{
message: error.message,
start: error.location ? error.location.start : undefined,
end: error.location ? error.location.end : undefined,
code: error.code,
severity: error.severity,
};
});
this.queryErrorMessage(errors[0].message);
if (this.queryTablesTab.onLoadStartKey != null && this.queryTablesTab.onLoadStartKey != undefined) {
TelemetryProcessor.traceFailure(
Action.Tab,
{
databaseName: this.queryTablesTab.collection.databaseId,
collectionName: this.queryTablesTab.collection.id(),
dataExplorerArea: Areas.Tab,
tabTitle: this.queryTablesTab.tabTitle(),
error: error,
},
this.queryTablesTab.onLoadStartKey
);
this.queryTablesTab.onLoadStartKey = null;
}
DataTableUtilities.turnOffProgressIndicator();
return result;
} catch (error) {
const parsedErrors = parseError(error);
var errors = parsedErrors.map((error) => {
return <ViewModels.QueryError>{
message: error.message,
start: error.location ? error.location.start : undefined,
end: error.location ? error.location.end : undefined,
code: error.code,
severity: error.severity,
};
});
this.queryErrorMessage(errors[0].message);
if (this.queryTablesTab.onLoadStartKey != null && this.queryTablesTab.onLoadStartKey != undefined) {
TelemetryProcessor.traceFailure(
Action.Tab,
{
databaseName: this.queryTablesTab.collection.databaseId,
collectionName: this.queryTablesTab.collection.id(),
dataExplorerArea: Areas.Tab,
tabTitle: this.queryTablesTab.tabTitle(),
error: error,
},
this.queryTablesTab.onLoadStartKey
);
this.queryTablesTab.onLoadStartKey = null;
}
DataTableUtilities.turnOffProgressIndicator();
}
// return undefined;
}
/**
@ -448,52 +530,54 @@ export default class TableEntityListViewModel extends DataTableViewModel {
* Note that this also means that we can get less entities than the requested download size in a successful call.
* See Microsoft Azure API Documentation at: https://msdn.microsoft.com/en-us/library/azure/dd135718.aspx
*/
private prefetchData(
private async prefetchData(
tableQuery: Entities.ITableQuery,
downloadSize: number,
currentRetry: number = 0
): Q.Promise<any> {
): Promise<any> {
console.log("🚀 ~ file: TableEntityListViewModel.ts ~ line 456 ~ TableEntityListViewModel ~ prefetchData");
var entities: any;
if (!this.cache.serverCallInProgress) {
this.cache.serverCallInProgress = true;
this.allDownloaded = false;
this.lastPrefetchTime = new Date().getTime();
var time = this.lastPrefetchTime;
var promise: Q.Promise<IListTableEntitiesSegmentedResult>;
if (this._documentIterator && this.continuationToken) {
// TODO handle Cassandra case
promise = Q(this._documentIterator.fetchNext().then((response) => response.resources)).then(
(documents: any[]) => {
let entities: Entities.ITableEntity[] = TableEntityProcessor.convertDocumentsToEntities(documents);
let finalEntities: IListTableEntitiesSegmentedResult = <IListTableEntitiesSegmentedResult>{
Results: entities,
ContinuationToken: this._documentIterator.hasMoreResults(),
};
return Q.resolve(finalEntities);
}
);
} else if (this.continuationToken && userContext.apiType === "Cassandra") {
promise = Q(
this.queryTablesTab.container.tableDataClient.queryDocuments(
var promise: Promise<IListTableEntitiesSegmentedResult>;
try {
if (this._documentIterator && this.continuationToken) {
// TODO handle Cassandra case
const fetchNext = await this._documentIterator.fetchNext();
let fetchNextEntities: Entities.ITableEntity[] = TableEntityProcessor.convertDocumentsToEntities(
fetchNext.resources
);
let finalEntities: IListTableEntitiesSegmentedResult = <IListTableEntitiesSegmentedResult>{
Results: fetchNextEntities,
ContinuationToken: this._documentIterator.hasMoreResults(),
};
entities = finalEntities;
} else if (this.continuationToken && userContext.apiType === "Cassandra") {
entities = this.queryTablesTab.container.tableDataClient.queryDocuments(
this.queryTablesTab.collection,
this.cqlQuery(),
true,
this.continuationToken
)
);
} else {
let query = this.sqlQuery();
if (userContext.apiType === "Cassandra") {
query = this.cqlQuery();
);
} else {
let query = this.sqlQuery();
if (userContext.apiType === "Cassandra") {
query = this.cqlQuery();
}
entities = await this.queryTablesTab.container.tableDataClient.queryDocuments(
this.queryTablesTab.collection,
query,
true
);
}
promise = Q(
this.queryTablesTab.container.tableDataClient.queryDocuments(this.queryTablesTab.collection, query, true)
);
}
return promise
.then((result: IListTableEntitiesSegmentedResult) => {
const result = entities;
if (result) {
if (!this._documentIterator) {
this._documentIterator = result.iterator;
}
@ -503,14 +587,14 @@ export default class TableEntityListViewModel extends DataTableViewModel {
// And as another service call is during process, we don't set serverCallInProgress to false here.
// Thus, end the prefetch.
if (this.lastPrefetchTime !== time) {
return Q.resolve(null);
return Promise.resolve(undefined);
}
var entities = result.Results;
actualDownloadSize = entities.length;
// Queries can fetch no results and still return a continuation header. See prefetchAndRender() method.
this.continuationToken = this.isCancelled ? null : result.ContinuationToken;
this.continuationToken = this.isCancelled ? undefined : result.ContinuationToken;
if (!this.continuationToken) {
this.allDownloaded = true;
@ -519,9 +603,23 @@ export default class TableEntityListViewModel extends DataTableViewModel {
if (this.isCacheValid(tableQuery)) {
// Append to cache.
this.cache.data = this.cache.data.concat(entities.slice(0));
console.log(
"🚀 ~ file: TableEntityListViewModel.ts ~ line 667 ~ TableEntityListViewModel ~ .then ~ this.cache.data",
this.cache.data
);
var p = new Promise<Entities.ITableEntity[]>((resolve) => {
if (this.cache.data) {
resolve(this.cache.data);
}
});
return p;
} else {
// Create cache.
this.cache.data = entities;
console.log(
"🚀 ~ file: TableEntityListViewModel.ts ~ line 671 ~ TableEntityListViewModel ~ .then ~ this.cache.data",
this.cache.data
);
}
this.cache.tableQuery = tableQuery;
@ -532,30 +630,20 @@ export default class TableEntityListViewModel extends DataTableViewModel {
this.allDownloaded = true;
}
// There are three possible results for a prefetch:
// 1. Continuation token is null or fetched items' size reaches predefined.
// 2. Continuation token is not null and fetched items' size hasn't reach predefined.
// 2.1 Retry times has reached predefined maximum.
// 2.2 Retry times hasn't reached predefined maximum.
// Correspondingly,
// For #1, end prefetch.
// For #2.1, set prefetch exceeds maximum retry number and end prefetch.
// For #2.2, go to next round prefetch.
if (this.allDownloaded || nextDownloadSize === 0) {
return Q.resolve(result);
return Promise.resolve(result);
}
if (currentRetry >= TableEntityListViewModel._maximumNumberOfPrefetchRetries) {
result.ExceedMaximumRetries = true;
return Q.resolve(result);
return Promise.resolve(result);
}
return this.prefetchData(tableQuery, nextDownloadSize, currentRetry + 1);
})
.catch((error: Error) => {
this.cache.serverCallInProgress = false;
return Q.reject(error);
});
}
} catch (error) {
this.cache.serverCallInProgress = false;
return Promise.reject(error);
}
}
return null;
}
}

View File

@ -170,6 +170,10 @@ export default class QueryViewModel {
this._tableEntityListViewModel.oDataQuery(filter);
this._tableEntityListViewModel.sqlQuery(this.setSqlFilter(queryTableRows));
this._tableEntityListViewModel.cqlQuery(filter);
console.log(
"🚀 ~ file: QueryViewModel.tsx ~ line 165 ~ QueryViewModel ~ this._tableEntityListViewModel.sqlQuery()",
this._tableEntityListViewModel.sqlQuery()
);
return userContext.apiType !== "Cassandra"
? this._tableEntityListViewModel.sqlQuery()

View File

@ -1,285 +0,0 @@
import * as ko from "knockout";
import React from "react";
import AddEntityIcon from "../../../images/AddEntity.svg";
import DeleteEntitiesIcon from "../../../images/DeleteEntities.svg";
import EditEntityIcon from "../../../images/Edit-entity.svg";
import ExecuteQueryIcon from "../../../images/ExecuteQuery.svg";
import QueryBuilderIcon from "../../../images/Query-Builder.svg";
import QueryTextIcon from "../../../images/Query-Text.svg";
import * as ViewModels from "../../Contracts/ViewModels";
import { useSidePanel } from "../../hooks/useSidePanel";
import { userContext } from "../../UserContext";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import Explorer from "../Explorer";
import { AddTableEntityPanel } from "../Panes/Tables/AddTableEntityPanel";
import { EditTableEntityPanel } from "../Panes/Tables/EditTableEntityPanel";
import TableCommands from "../Tables/DataTable/TableCommands";
import TableEntityListViewModel from "../Tables/DataTable/TableEntityListViewModel";
import QueryViewModel from "../Tables/QueryBuilder/QueryViewModel";
import { CassandraAPIDataClient, TableDataClient } from "../Tables/TableDataClient";
import template from "./QueryTablesTab.html";
import TabsBase from "./TabsBase";
// Will act as table explorer class
export default class QueryTablesTab extends TabsBase {
public readonly html = template;
public collection: ViewModels.Collection;
public tableEntityListViewModel = ko.observable<TableEntityListViewModel>();
public queryViewModel = ko.observable<QueryViewModel>();
public tableCommands: TableCommands;
public tableDataClient: TableDataClient;
public queryText = ko.observable("PartitionKey eq 'partitionKey1'"); // Start out with an example they can modify
public selectedQueryText = ko.observable("").extend({ notify: "always" });
public executeQueryButton: ViewModels.Button;
public addEntityButton: ViewModels.Button;
public editEntityButton: ViewModels.Button;
public deleteEntityButton: ViewModels.Button;
public queryBuilderButton: ViewModels.Button;
public queryTextButton: ViewModels.Button;
public container: Explorer;
constructor(options: ViewModels.TabOptions) {
super(options);
this.container = options.collection && options.collection.container;
this.tableCommands = new TableCommands(this.container);
this.tableDataClient = this.container.tableDataClient;
this.tableEntityListViewModel(new TableEntityListViewModel(this.tableCommands, this));
this.tableEntityListViewModel().queryTablesTab = this;
this.queryViewModel(new QueryViewModel(this));
const sampleQuerySubscription = this.tableEntityListViewModel().items.subscribe(() => {
if (this.tableEntityListViewModel().items().length > 0 && userContext.apiType === "Tables") {
this.queryViewModel().queryBuilderViewModel().setExample();
}
sampleQuerySubscription.dispose();
});
this.executeQueryButton = {
enabled: ko.computed<boolean>(() => {
return true;
}),
visible: ko.computed<boolean>(() => {
return true;
}),
};
this.queryBuilderButton = {
enabled: ko.computed<boolean>(() => {
return true;
}),
visible: ko.computed<boolean>(() => {
return true;
}),
isSelected: ko.computed<boolean>(() => {
return this.queryViewModel() ? this.queryViewModel().isHelperActive() : false;
}),
};
this.queryTextButton = {
enabled: ko.computed<boolean>(() => {
return true;
}),
visible: ko.computed<boolean>(() => {
return true;
}),
isSelected: ko.computed<boolean>(() => {
return this.queryViewModel() ? this.queryViewModel().isEditorActive() : false;
}),
};
this.addEntityButton = {
enabled: ko.computed<boolean>(() => {
return true;
}),
visible: ko.computed<boolean>(() => {
return true;
}),
};
this.editEntityButton = {
enabled: ko.computed<boolean>(() => {
return this.tableCommands.isEnabled(
TableCommands.editEntityCommand,
this.tableEntityListViewModel().selected()
);
}),
visible: ko.computed<boolean>(() => {
return true;
}),
};
this.deleteEntityButton = {
enabled: ko.computed<boolean>(() => {
return this.tableCommands.isEnabled(
TableCommands.deleteEntitiesCommand,
this.tableEntityListViewModel().selected()
);
}),
visible: ko.computed<boolean>(() => {
return true;
}),
};
this.buildCommandBarOptions();
}
public onAddEntityClick = (): void => {
useSidePanel
.getState()
.openSidePanel(
"Add Table Row",
<AddTableEntityPanel
tableDataClient={this.tableDataClient}
queryTablesTab={this}
tableEntityListViewModel={this.tableEntityListViewModel()}
cassandraApiClient={new CassandraAPIDataClient()}
/>,
"700px"
);
};
public onEditEntityClick = (): void => {
useSidePanel
.getState()
.openSidePanel(
"Edit Table Entity",
<EditTableEntityPanel
tableDataClient={this.tableDataClient}
queryTablesTab={this}
tableEntityListViewModel={this.tableEntityListViewModel()}
cassandraApiClient={new CassandraAPIDataClient()}
/>,
"700px"
);
};
public onDeleteEntityClick = (): void => {
this.tableCommands.deleteEntitiesCommand(this.tableEntityListViewModel());
};
public onActivate(): void {
super.onActivate();
const columns =
!!this.tableEntityListViewModel() &&
!!this.tableEntityListViewModel().table &&
this.tableEntityListViewModel().table.columns;
if (columns) {
columns.adjust();
$(window).resize();
}
}
protected getTabsButtons(): CommandButtonComponentProps[] {
const buttons: CommandButtonComponentProps[] = [];
if (this.queryBuilderButton.visible()) {
const label = userContext.apiType === "Cassandra" ? "CQL Query Builder" : "Query Builder";
buttons.push({
iconSrc: QueryBuilderIcon,
iconAlt: label,
onCommandClick: () => this.queryViewModel().selectHelper(),
commandButtonLabel: label,
ariaLabel: label,
hasPopup: false,
disabled: !this.queryBuilderButton.enabled(),
isSelected: this.queryBuilderButton.isSelected(),
});
}
if (this.queryTextButton.visible()) {
const label = userContext.apiType === "Cassandra" ? "CQL Query Text" : "Query Text";
buttons.push({
iconSrc: QueryTextIcon,
iconAlt: label,
onCommandClick: () => this.queryViewModel().selectEditor(),
commandButtonLabel: label,
ariaLabel: label,
hasPopup: false,
disabled: !this.queryTextButton.enabled(),
isSelected: this.queryTextButton.isSelected(),
});
}
if (this.executeQueryButton.visible()) {
const label = "Run Query";
buttons.push({
iconSrc: ExecuteQueryIcon,
iconAlt: label,
onCommandClick: () => this.queryViewModel().runQuery(),
commandButtonLabel: label,
ariaLabel: label,
hasPopup: false,
disabled: !this.executeQueryButton.enabled(),
});
}
if (this.addEntityButton.visible()) {
const label = userContext.apiType === "Cassandra" ? "Add Row" : "Add Entity";
buttons.push({
iconSrc: AddEntityIcon,
iconAlt: label,
onCommandClick: this.onAddEntityClick,
commandButtonLabel: label,
ariaLabel: label,
hasPopup: true,
disabled: !this.addEntityButton.enabled(),
});
}
if (this.editEntityButton.visible()) {
const label = userContext.apiType === "Cassandra" ? "Edit Row" : "Edit Entity";
buttons.push({
iconSrc: EditEntityIcon,
iconAlt: label,
onCommandClick: this.onEditEntityClick,
commandButtonLabel: label,
ariaLabel: label,
hasPopup: true,
disabled: !this.editEntityButton.enabled(),
});
}
if (this.deleteEntityButton.visible()) {
const label = userContext.apiType === "Cassandra" ? "Delete Rows" : "Delete Entities";
buttons.push({
iconSrc: DeleteEntitiesIcon,
iconAlt: label,
onCommandClick: this.onDeleteEntityClick,
commandButtonLabel: label,
ariaLabel: label,
hasPopup: true,
disabled: !this.deleteEntityButton.enabled(),
});
}
return buttons;
}
protected buildCommandBarOptions(): void {
ko.computed(() =>
ko.toJSON([
this.queryBuilderButton.visible,
this.queryBuilderButton.enabled,
this.queryTextButton.visible,
this.queryTextButton.enabled,
this.executeQueryButton.visible,
this.executeQueryButton.enabled,
this.addEntityButton.visible,
this.addEntityButton.enabled,
this.editEntityButton.visible,
this.editEntityButton.enabled,
this.deleteEntityButton.visible,
this.deleteEntityButton.enabled,
])
).subscribe(() => this.updateNavbarWithTabsButtons());
this.updateNavbarWithTabsButtons();
}
}

View File

@ -63,7 +63,7 @@ export interface Button {
isSelected?: boolean;
}
const PAGESIZE = 10;
const PAGESIZE = 100;
class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, IQueryTablesTabComponentStates> {
public collection: ViewModels.Collection;
@ -100,12 +100,6 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
this.tableDataClient = this.container.tableDataClient;
this.tableEntityListViewModel2(new TableEntityListViewModel(this.tableCommands, props.queryTablesTab));
const tableEntityListViewModel = new TableEntityListViewModel(this.tableCommands, props.queryTablesTab);
// const queryBuilderViewModel = new QueryViewModel(this.props.queryTablesTab).queryBuilderViewModel();
// const entityTypeOptions = queryBuilderViewModel.edmTypes();
// const timestampOptions = queryBuilderViewModel.timeOptions();
// const operatorsOptions = queryBuilderViewModel.operators();
// const operationOptions = queryBuilderViewModel.clauseRules();
this.state = {
tableEntityListViewModel,
@ -201,7 +195,7 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
}
componentDidMount(): void {
this.loadEntities(true);
this.loadEntities(true, false);
}
public createSelection = (): Selection => {
@ -294,33 +288,77 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
});
}
public async loadEntities(isInitialLoad: boolean): Promise<void> {
public async loadEntities(isInitialLoad: boolean, isRunQuery?: boolean): Promise<void> {
const { tableEntityListViewModel, selectedQueryText } = this.state;
// tableEntityListViewModel.renderNextPageAndupdateCache();
let headers: string[] = [];
//eslint-disable-next-line
let documents: any = {};
try {
if (userContext.apiType === "Cassandra") {
documents = await this.props.queryTablesTab.container.tableDataClient.queryDocuments(
this.props.queryTablesTab.collection,
selectedQueryText,
true
);
headers = this.getFormattedHeaders(documents.Results);
this.setupIntialEntities(documents.Results, headers, isInitialLoad);
} else {
const { collection } = this.props;
documents = await this.getDocuments(collection, selectedQueryText);
headers = this.getFormattedHeaders(documents.Results);
this.setupIntialEntities(documents.Results, headers, isInitialLoad);
// const data = await tableEntityListViewModel.a();
// console.log(
// "🚀 ~ file: QueryTablesTabComponent.tsx ~ line 311 ~ QueryTablesTabComponent ~ loadEntities ~ data",
// data
// );
// setTimeout(() => {
// console.log("Items > ", this.state.tableEntityListViewModel.items());
// }, 10000);
// await tableEntityListViewModel.renderNextPageAndupdateCache(selectedQueryText);
// setTimeout(() => {
// // console.log("Processing...");
// this.isEntitiesAvailable(isInitialLoad);
// }, 0);
if (!isRunQuery) {
try {
documents = await tableEntityListViewModel.renderNextPageAndupdateCache();
// setTimeout(() => {
// // console.log("Processing...");
// this.isEntitiesAvailable(isInitialLoad);
// }, 0);
// const data = await tableEntityListViewModel.a();
if (userContext.apiType === "Cassandra") {
console.log(
"🚀 ~ file: QueryTablesTabComponent.tsx ~ line 311 ~ QueryTablesTabComponent ~ loadEntities ~ data",
documents.Results
);
headers = this.getFormattedHeaders(documents.Results);
this.setupIntialEntities(headers, documents.Results, isInitialLoad);
} else {
console.log(
"🚀 ~ file: QueryTablesTabComponent.tsx ~ line 311 ~ QueryTablesTabComponent ~ loadEntities ~ data",
documents
);
headers = this.getFormattedHeaders(documents);
this.setupIntialEntities(headers, documents, isInitialLoad);
}
// this.isEntitiesAvailable(isInitialLoad, data);
} catch (error) {
this.setState({
queryErrorMessage: error.responseText,
hasQueryError: true,
isLoading: false,
});
}
this.setState({
queryErrorMessage: "",
hasQueryError: false,
});
} catch (error) {
if (error.responseText) {
} else {
try {
if (userContext.apiType === "Cassandra") {
documents = await this.props.queryTablesTab.container.tableDataClient.queryDocuments(
this.props.queryTablesTab.collection,
selectedQueryText,
true
);
headers = this.getFormattedHeaders(documents.Results);
this.setupIntialEntities(headers, documents.Results, isInitialLoad);
} else {
const { collection } = this.props;
documents = await this.getDocuments(collection, selectedQueryText);
headers = this.getFormattedHeaders(documents.Results);
this.setupIntialEntities(headers, documents.Results, isInitialLoad);
}
this.setState({
queryErrorMessage: "",
hasQueryError: false,
});
} catch (error) {
this.setState({
queryErrorMessage: error.responseText,
hasQueryError: true,
@ -331,17 +369,35 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
}
private setupIntialEntities = (
entities: Entities.ITableEntity[],
headers: string[],
isInitialLoad: boolean
entities?: Entities.ITableEntity[],
isInitialLoad?: boolean
): void => {
let minWidth: number;
let maxWidth: number;
let documentItems: IDocument[] = [];
let filteredItems: IDocument[] = [];
this.columns = [];
headers.map((header) => {
switch (header) {
case "PartitionKey":
case "RowKey":
minWidth = 50;
maxWidth = 100;
break;
case "Timestamp":
minWidth = 200;
maxWidth = 200;
break;
default:
minWidth = 30;
maxWidth = 50;
}
this.columns.push({
key: header,
name: header,
minWidth: 100,
maxWidth: 200,
minWidth: minWidth,
maxWidth: maxWidth,
data: "string",
fieldName: header,
isResizable: true,
@ -350,12 +406,14 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
sortAscendingAriaLabel: "Sorted A to Z",
sortDescendingAriaLabel: "Sorted Z to A",
onColumnClick: this.onColumnClick,
// onRender: (item: Entities.ITableEntity) => {
// return <div className={!item[header] ? "noData" : ""}>{item[header] ? item[header] : " "}</div>;
// },
});
});
const documentItems = this.generateDetailsList(entities);
const filteredItems = documentItems.slice(0, PAGESIZE);
documentItems = this.generateDetailsList(entities);
filteredItems = documentItems.slice(0, PAGESIZE);
this.setState(
{
@ -369,6 +427,7 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
queryText: this.state.queryViewModel.queryText(),
fromDocument: 0,
toDocument: PAGESIZE,
currentStartIndex: PAGESIZE,
},
() => {
if (isInitialLoad && headers.length > 0) {
@ -379,6 +438,36 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
);
};
// public isEntitiesAvailable(isInitialLoad: boolean, data?: Entities.ITableEntity[]): void {
// let headers: string[] = [];
// headers = this.getFormattedHeaders(data);
// this.setupIntialEntities(false, headers);
// const documentItems = this.generateDetailsList(data);
// const filteredItems = documentItems.slice(0, PAGESIZE);
// this.setState(
// {
// columns: this.columns,
// headers,
// operators: this.state.queryViewModel.queryBuilderViewModel().operators(),
// isLoading: false,
// items: filteredItems,
// entities: data,
// originalItems: documentItems,
// queryText: this.state.queryViewModel.queryText(),
// fromDocument: 0,
// toDocument: PAGESIZE,
// },
// () => {
// if (isInitialLoad && headers.length > 0) {
// this.loadFilterExample();
// this.onItemsSelectionChanged(true);
// }
// }
// );
// }
private onColumnClick = (ev: React.MouseEvent<HTMLElement>, column: IColumn): void => {
const { columns, items } = this.state;
const newColumns: IColumn[] = columns.slice();
@ -444,6 +533,7 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
//eslint-disable-next-line
let obj: any;
documents.map((item) => {
obj = {};
this.columns.map((col) => {
if (item[col.name]) {
obj = { ...obj, ...{ [col.name]: item[col.name]._ } };
@ -458,7 +548,7 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
this.setState({
isLoading: true,
});
this.loadEntities(false);
this.loadEntities(false, false);
}
public onAddEntityClick = (): void => {
@ -472,7 +562,8 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
tableEntityListViewModel={this.state.tableEntityListViewModel}
cassandraApiClient={new CassandraAPIDataClient()}
reloadEntities={() => this.reloadEntities()}
/>
/>,
"700px"
);
};
@ -492,7 +583,8 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
cassandraApiClient={new CassandraAPIDataClient()}
selectedEntity={this.state.selectedItems}
reloadEntities={() => this.reloadEntities()}
/>
/>,
"700px"
);
};
@ -511,10 +603,12 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
.deleteDocuments(this.state.tableEntityListViewModel.queryTablesTab.collection, entitiesToDelete)
//eslint-disable-next-line
.then((results: any) => {
this.setState({
isLoading: true,
return this.state.tableEntityListViewModel.removeEntitiesFromCache(entitiesToDelete).then(() => {
this.setState({
isLoading: true,
});
this.loadEntities(false, false);
});
this.loadEntities(false);
});
}
};
@ -525,7 +619,11 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
selectedQueryText: this.state.queryViewModel.runQuery(queryTableRows),
});
setTimeout(() => {
this.loadEntities(false);
console.log(
"🚀 ~ file: QueryTablesTabComponent.tsx ~ line 651 ~ QueryTablesTabComponent ~ runQuery ~ selectedQueryText",
this.state.selectedQueryText
);
this.loadEntities(false, queryTableRows.length > 0 ? true : false);
}, 2000);
this.setState({
queryText: this.state.queryViewModel.queryText(),
@ -1005,7 +1103,7 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
</div>
</div>
<div className="tablesQueryTab tableContainer">
<div className="tablesQueryTab tableContainer" data-is-scrollable="true">
{this.state.isLoading && <Spinner size={SpinnerSize.large} className="spinner" />}
{this.state.items.length > 0 && !this.state.isLoading && (
<DetailsList
@ -1018,6 +1116,7 @@ class QueryTablesTabComponent extends Component<IQueryTablesTabComponentProps, I
selection={this.state.selection}
selectionPreservedOnEmptyClick={true}
checkboxVisibility={CheckboxVisibility.always}
onShouldVirtualize={() => false}
/>
)}
{this.state.items.length === 0 && !this.state.isLoading && (