vitess.io/vitess@v0.16.2/web/vtadmin/src/components/routes/Schemas.tsx (about)

     1  /**
     2   * Copyright 2021 The Vitess Authors.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  import { orderBy } from 'lodash-es';
    17  import * as React from 'react';
    18  import { Link } from 'react-router-dom';
    19  
    20  import { useSchemas } from '../../hooks/api';
    21  import { useDocumentTitle } from '../../hooks/useDocumentTitle';
    22  import { useSyncedURLParam } from '../../hooks/useSyncedURLParam';
    23  import { filterNouns } from '../../util/filterNouns';
    24  import { formatBytes } from '../../util/formatBytes';
    25  import { getTableDefinitions } from '../../util/tableDefinitions';
    26  import { DataCell } from '../dataTable/DataCell';
    27  import { DataFilter } from '../dataTable/DataFilter';
    28  import { DataTable } from '../dataTable/DataTable';
    29  import { ContentContainer } from '../layout/ContentContainer';
    30  import { WorkspaceHeader } from '../layout/WorkspaceHeader';
    31  import { WorkspaceTitle } from '../layout/WorkspaceTitle';
    32  import { KeyspaceLink } from '../links/KeyspaceLink';
    33  import { QueryLoadingPlaceholder } from '../placeholders/QueryLoadingPlaceholder';
    34  import { HelpTooltip } from '../tooltip/HelpTooltip';
    35  
    36  const TABLE_COLUMNS = [
    37      'Keyspace',
    38      'Table',
    39      <div className="text-right">
    40          Approx. Size{' '}
    41          <HelpTooltip
    42              text={
    43                  <span>
    44                      Size is an approximate value derived from <span className="font-mono">INFORMATION_SCHEMA</span>.
    45                  </span>
    46              }
    47          />
    48      </div>,
    49      <div className="text-right">
    50          Approx. Rows{' '}
    51          <HelpTooltip
    52              text={
    53                  // c.f. https://dev.mysql.com/doc/refman/5.7/en/information-schema-tables-table.html
    54                  <span>
    55                      Row count is an approximate value derived from <span className="font-mono">INFORMATION_SCHEMA</span>
    56                      . Actual values may vary by as much as 40% to 50%.
    57                  </span>
    58              }
    59          />
    60      </div>,
    61  ];
    62  
    63  export const Schemas = () => {
    64      useDocumentTitle('Schemas');
    65  
    66      const schemasQuery = useSchemas();
    67      const { value: filter, updateValue: updateFilter } = useSyncedURLParam('filter');
    68  
    69      const filteredData = React.useMemo(() => {
    70          const tableDefinitions = getTableDefinitions(schemasQuery.data);
    71  
    72          const mapped = tableDefinitions.map((d) => ({
    73              cluster: d.cluster?.name,
    74              clusterID: d.cluster?.id,
    75              keyspace: d.keyspace,
    76              table: d.tableDefinition?.name,
    77              _raw: d,
    78          }));
    79  
    80          const filtered = filterNouns(filter, mapped);
    81          return orderBy(filtered, ['cluster', 'keyspace', 'table']);
    82      }, [schemasQuery.data, filter]);
    83  
    84      const renderRows = (rows: typeof filteredData) =>
    85          rows.map((row, idx) => {
    86              const href =
    87                  row.clusterID && row.keyspace && row.table
    88                      ? `/schema/${row.clusterID}/${row.keyspace}/${row.table}`
    89                      : null;
    90              return (
    91                  <tr key={idx}>
    92                      <DataCell>
    93                          <KeyspaceLink clusterID={row.clusterID} name={row.keyspace}>
    94                              <div>{row.keyspace}</div>
    95                              <div className="text-sm text-secondary">{row.cluster}</div>
    96                          </KeyspaceLink>
    97                      </DataCell>
    98                      <DataCell className="font-bold">{href ? <Link to={href}>{row.table}</Link> : row.table}</DataCell>
    99                      <DataCell className="text-right">
   100                          <div>{formatBytes(row._raw.tableSize?.data_length)}</div>
   101                          <div className="text-sm text-secondary">
   102                              {formatBytes(row._raw.tableSize?.data_length, 'B')}
   103                          </div>
   104                      </DataCell>
   105                      <DataCell className="text-right">{(row._raw.tableSize?.row_count || 0).toLocaleString()}</DataCell>
   106                  </tr>
   107              );
   108          });
   109  
   110      return (
   111          <div>
   112              <WorkspaceHeader>
   113                  <WorkspaceTitle>Schemas</WorkspaceTitle>
   114              </WorkspaceHeader>
   115              <ContentContainer>
   116                  <DataFilter
   117                      autoFocus
   118                      onChange={(e) => updateFilter(e.target.value)}
   119                      onClear={() => updateFilter('')}
   120                      placeholder="Filter schemas"
   121                      value={filter || ''}
   122                  />
   123                  <DataTable columns={TABLE_COLUMNS} data={filteredData} renderRows={renderRows} />
   124                  <QueryLoadingPlaceholder query={schemasQuery} />
   125              </ContentContainer>
   126          </div>
   127      );
   128  };