github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/ui/src/util/api.ts (about)

     1  // Copyright 2018 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  /**
    12   * This module contains all the REST endpoints for communicating with the admin UI.
    13   */
    14  
    15  import _ from "lodash";
    16  import moment from "moment";
    17  
    18  import * as protos from "src/js/protos";
    19  import { FixLong } from "src/util/fixLong";
    20  
    21  export type DatabasesRequestMessage = protos.cockroach.server.serverpb.DatabasesRequest;
    22  export type DatabasesResponseMessage = protos.cockroach.server.serverpb.DatabasesResponse;
    23  
    24  export type DatabaseDetailsRequestMessage = protos.cockroach.server.serverpb.DatabaseDetailsRequest;
    25  export type DatabaseDetailsResponseMessage = protos.cockroach.server.serverpb.DatabaseDetailsResponse;
    26  
    27  export type TableDetailsRequestMessage = protos.cockroach.server.serverpb.TableDetailsRequest;
    28  export type TableDetailsResponseMessage = protos.cockroach.server.serverpb.TableDetailsResponse;
    29  
    30  export type EventsRequestMessage = protos.cockroach.server.serverpb.EventsRequest;
    31  export type EventsResponseMessage = protos.cockroach.server.serverpb.EventsResponse;
    32  
    33  export type LocationsRequestMessage = protos.cockroach.server.serverpb.LocationsRequest;
    34  export type LocationsResponseMessage = protos.cockroach.server.serverpb.LocationsResponse;
    35  
    36  export type NodesRequestMessage = protos.cockroach.server.serverpb.NodesRequest;
    37  export type NodesResponseMessage = protos.cockroach.server.serverpb.NodesResponse;
    38  
    39  export type GetUIDataRequestMessage = protos.cockroach.server.serverpb.GetUIDataRequest;
    40  export type GetUIDataResponseMessage = protos.cockroach.server.serverpb.GetUIDataResponse;
    41  
    42  export type SetUIDataRequestMessage = protos.cockroach.server.serverpb.SetUIDataRequest;
    43  export type SetUIDataResponseMessage = protos.cockroach.server.serverpb.SetUIDataResponse;
    44  
    45  export type RaftDebugRequestMessage = protos.cockroach.server.serverpb.RaftDebugRequest;
    46  export type RaftDebugResponseMessage = protos.cockroach.server.serverpb.RaftDebugResponse;
    47  
    48  export type TimeSeriesQueryRequestMessage = protos.cockroach.ts.tspb.TimeSeriesQueryRequest;
    49  export type TimeSeriesQueryResponseMessage = protos.cockroach.ts.tspb.TimeSeriesQueryResponse;
    50  
    51  export type HealthRequestMessage = protos.cockroach.server.serverpb.HealthRequest;
    52  export type HealthResponseMessage = protos.cockroach.server.serverpb.HealthResponse;
    53  
    54  export type ClusterRequestMessage = protos.cockroach.server.serverpb.ClusterRequest;
    55  export type ClusterResponseMessage = protos.cockroach.server.serverpb.ClusterResponse;
    56  
    57  export type TableStatsRequestMessage = protos.cockroach.server.serverpb.TableStatsRequest;
    58  export type TableStatsResponseMessage = protos.cockroach.server.serverpb.TableStatsResponse;
    59  
    60  export type NonTableStatsRequestMessage = protos.cockroach.server.serverpb.NonTableStatsRequest;
    61  export type NonTableStatsResponseMessage = protos.cockroach.server.serverpb.NonTableStatsResponse;
    62  
    63  export type LogsRequestMessage = protos.cockroach.server.serverpb.LogsRequest;
    64  export type LogEntriesResponseMessage = protos.cockroach.server.serverpb.LogEntriesResponse;
    65  
    66  export type LivenessRequestMessage = protos.cockroach.server.serverpb.LivenessRequest;
    67  export type LivenessResponseMessage = protos.cockroach.server.serverpb.LivenessResponse;
    68  
    69  export type JobsRequestMessage = protos.cockroach.server.serverpb.JobsRequest;
    70  export type JobsResponseMessage = protos.cockroach.server.serverpb.JobsResponse;
    71  
    72  export type QueryPlanRequestMessage = protos.cockroach.server.serverpb.QueryPlanRequest;
    73  export type QueryPlanResponseMessage = protos.cockroach.server.serverpb.QueryPlanResponse;
    74  
    75  export type ProblemRangesRequestMessage = protos.cockroach.server.serverpb.ProblemRangesRequest;
    76  export type ProblemRangesResponseMessage = protos.cockroach.server.serverpb.ProblemRangesResponse;
    77  
    78  export type CertificatesRequestMessage = protos.cockroach.server.serverpb.CertificatesRequest;
    79  export type CertificatesResponseMessage = protos.cockroach.server.serverpb.CertificatesResponse;
    80  
    81  export type RangeRequestMessage = protos.cockroach.server.serverpb.RangeRequest;
    82  export type RangeResponseMessage = protos.cockroach.server.serverpb.RangeResponse;
    83  
    84  export type AllocatorRangeRequestMessage = protos.cockroach.server.serverpb.AllocatorRangeRequest;
    85  export type AllocatorRangeResponseMessage = protos.cockroach.server.serverpb.AllocatorRangeResponse;
    86  
    87  export type RangeLogRequestMessage =
    88    protos.cockroach.server.serverpb.RangeLogRequest;
    89  export type RangeLogResponseMessage =
    90    protos.cockroach.server.serverpb.RangeLogResponse;
    91  
    92  export type SettingsRequestMessage = protos.cockroach.server.serverpb.SettingsRequest;
    93  export type SettingsResponseMessage = protos.cockroach.server.serverpb.SettingsResponse;
    94  
    95  export type UserLoginRequestMessage = protos.cockroach.server.serverpb.UserLoginRequest;
    96  export type UserLoginResponseMessage = protos.cockroach.server.serverpb.UserLoginResponse;
    97  
    98  export type StoresRequestMessage = protos.cockroach.server.serverpb.StoresRequest;
    99  export type StoresResponseMessage = protos.cockroach.server.serverpb.StoresResponse;
   100  
   101  export type UserLogoutResponseMessage = protos.cockroach.server.serverpb.UserLogoutResponse;
   102  
   103  export type StatementsResponseMessage = protos.cockroach.server.serverpb.StatementsResponse;
   104  
   105  export type DataDistributionResponseMessage = protos.cockroach.server.serverpb.DataDistributionResponse;
   106  
   107  export type EnqueueRangeRequestMessage = protos.cockroach.server.serverpb.EnqueueRangeRequest;
   108  export type EnqueueRangeResponseMessage = protos.cockroach.server.serverpb.EnqueueRangeResponse;
   109  
   110  export type MetricMetadataRequestMessage = protos.cockroach.server.serverpb.MetricMetadataRequest;
   111  export type MetricMetadataResponseMessage = protos.cockroach.server.serverpb.MetricMetadataResponse;
   112  
   113  export type StatementDiagnosticsReportsRequestMessage = protos.cockroach.server.serverpb.StatementDiagnosticsReportsRequest;
   114  export type StatementDiagnosticsReportsResponseMessage = protos.cockroach.server.serverpb.StatementDiagnosticsReportsResponse;
   115  
   116  export type CreateStatementDiagnosticsReportRequestMessage = protos.cockroach.server.serverpb.CreateStatementDiagnosticsReportRequest;
   117  export type CreateStatementDiagnosticsReportResponseMessage = protos.cockroach.server.serverpb.CreateStatementDiagnosticsReportResponse;
   118  
   119  export type StatementDiagnosticsRequestMessage = protos.cockroach.server.serverpb.StatementDiagnosticsRequest;
   120  export type StatementDiagnosticsResponseMessage = protos.cockroach.server.serverpb.StatementDiagnosticsResponse;
   121  
   122  // API constants
   123  
   124  export const API_PREFIX = "_admin/v1";
   125  export const STATUS_PREFIX = "_status";
   126  
   127  // HELPER FUNCTIONS
   128  
   129  // Inspired by https://github.com/github/fetch/issues/175
   130  //
   131  // withTimeout wraps a promise in a timeout.
   132  export function withTimeout<T>(promise: Promise<T>, timeout?: moment.Duration): Promise<T> {
   133    if (timeout) {
   134      return new Promise<T>((resolve, reject) => {
   135        setTimeout(() => reject(new Error(`Promise timed out after ${timeout.asMilliseconds()} ms`)), timeout.asMilliseconds());
   136        promise.then(resolve, reject);
   137      });
   138    } else {
   139      return promise;
   140    }
   141  }
   142  
   143  interface TRequest {
   144    constructor: {
   145      encode(message: TRequest, writer?: protobuf.Writer): protobuf.Writer;
   146    };
   147    toObject(): { [k: string]: any };
   148  }
   149  
   150  export function toArrayBuffer(encodedRequest: Uint8Array): ArrayBuffer {
   151    return encodedRequest.buffer.slice(encodedRequest.byteOffset, encodedRequest.byteOffset + encodedRequest.byteLength);
   152  }
   153  
   154  export class RequestError extends Error {
   155    status: number;
   156    constructor(statusText: string, status: number) {
   157      super(statusText);
   158      this.status = status;
   159      this.name = "RequestError";
   160    }
   161  }
   162  
   163  // timeoutFetch is a wrapper around fetch that provides timeout and protocol
   164  // buffer marshaling and unmarshalling.
   165  //
   166  // This function is intended for use with generated protocol buffers. In
   167  // particular, TResponse$Properties is a generated interface that describes the JSON
   168  // representation of the response, while TRequest and TResponse
   169  // are generated interfaces which are implemented by the protocol buffer
   170  // objects themselves. TResponseBuilder is an interface implemented by
   171  // the builder objects provided at runtime by protobuf.js.
   172  function timeoutFetch<TResponse$Properties, TResponse, TResponseBuilder extends {
   173    new(properties?: TResponse$Properties): TResponse
   174    encode(message: TResponse$Properties, writer?: protobuf.Writer): protobuf.Writer
   175    decode(reader: (protobuf.Reader | Uint8Array), length?: number): TResponse;
   176  }>(builder: TResponseBuilder, url: string, req: TRequest = null, timeout: moment.Duration = moment.duration(30, "s")): Promise<TResponse> {
   177    const params: RequestInit = {
   178      headers: {
   179        "Accept": "application/x-protobuf",
   180        "Content-Type": "application/x-protobuf",
   181        "Grpc-Timeout": timeout ? timeout.asMilliseconds() + "m" : undefined,
   182      },
   183      credentials: "same-origin",
   184    };
   185  
   186    if (req) {
   187      const encodedRequest = req.constructor.encode(req).finish();
   188      params.method = "POST";
   189      params.body = toArrayBuffer(encodedRequest);
   190    }
   191  
   192    return withTimeout(fetch(url, params), timeout).then((res) => {
   193      if (!res.ok) {
   194        throw new RequestError(res.statusText, res.status);
   195      }
   196      return res.arrayBuffer().then((buffer) => builder.decode(new Uint8Array(buffer)));
   197    });
   198  }
   199  
   200  export type APIRequestFn<TReq, TResponse> = (req: TReq, timeout?: moment.Duration) => Promise<TResponse>;
   201  
   202  // propsToQueryString is a helper function that converts a set of object
   203  // properties to a query string
   204  // - keys with null or undefined values will be skipped
   205  // - non-string values will be toString'd
   206  export function propsToQueryString(props: { [k: string]: any }) {
   207    return _.compact(_.map(props, (v: any, k: string) => !_.isNull(v) && !_.isUndefined(v) ? `${encodeURIComponent(k)}=${encodeURIComponent(v.toString())}` : null)).join("&");
   208  }
   209  /**
   210   * ENDPOINTS
   211   */
   212  
   213  const serverpb = protos.cockroach.server.serverpb;
   214  const tspb = protos.cockroach.ts.tspb;
   215  
   216  // getDatabaseList gets a list of all database names
   217  export function getDatabaseList(_req: DatabasesRequestMessage, timeout?: moment.Duration): Promise<DatabasesResponseMessage> {
   218    return timeoutFetch(serverpb.DatabasesResponse, `${API_PREFIX}/databases`, null, timeout);
   219  }
   220  
   221  // getDatabaseDetails gets details for a specific database
   222  export function getDatabaseDetails(req: DatabaseDetailsRequestMessage, timeout?: moment.Duration): Promise<DatabaseDetailsResponseMessage> {
   223    return timeoutFetch(serverpb.DatabaseDetailsResponse, `${API_PREFIX}/databases/${req.database}`, null, timeout);
   224  }
   225  
   226  // getTableDetails gets details for a specific table
   227  export function getTableDetails(req: TableDetailsRequestMessage, timeout?: moment.Duration): Promise<TableDetailsResponseMessage> {
   228    return timeoutFetch(serverpb.TableDetailsResponse, `${API_PREFIX}/databases/${req.database}/tables/${req.table}`, null, timeout);
   229  }
   230  
   231  // getUIData gets UI data
   232  export function getUIData(req: GetUIDataRequestMessage, timeout?: moment.Duration): Promise<GetUIDataResponseMessage> {
   233    const queryString = _.map(req.keys, (key) => "keys=" + encodeURIComponent(key)).join("&");
   234    return timeoutFetch(serverpb.GetUIDataResponse, `${API_PREFIX}/uidata?${queryString}`, null, timeout);
   235  }
   236  
   237  // setUIData sets UI data
   238  export function setUIData(req: SetUIDataRequestMessage, timeout?: moment.Duration): Promise<SetUIDataResponseMessage> {
   239    return timeoutFetch(serverpb.SetUIDataResponse, `${API_PREFIX}/uidata`, req as any, timeout);
   240  }
   241  
   242  // getEvents gets event data
   243  export function getEvents(req: EventsRequestMessage, timeout?: moment.Duration): Promise<EventsResponseMessage> {
   244    const queryString = propsToQueryString(_.pick(req, ["type", "target_id"]));
   245    return timeoutFetch(serverpb.EventsResponse, `${API_PREFIX}/events?unredacted_events=true&${queryString}`, null, timeout);
   246  }
   247  
   248  export function getLocations(_req: LocationsRequestMessage, timeout?: moment.Duration): Promise<LocationsResponseMessage> {
   249    return timeoutFetch(serverpb.LocationsResponse, `${API_PREFIX}/locations`, null, timeout);
   250  }
   251  
   252  // getNodes gets node data
   253  export function getNodes(_req: NodesRequestMessage, timeout?: moment.Duration): Promise<NodesResponseMessage> {
   254    return timeoutFetch(serverpb.NodesResponse, `${STATUS_PREFIX}/nodes`, null, timeout);
   255  }
   256  
   257  export function raftDebug(_req: RaftDebugRequestMessage): Promise<RaftDebugResponseMessage> {
   258    // NB: raftDebug intentionally does not pass a timeout through.
   259    return timeoutFetch(serverpb.RaftDebugResponse, `${STATUS_PREFIX}/raft`);
   260  }
   261  
   262  // queryTimeSeries queries for time series data
   263  export function queryTimeSeries(req: TimeSeriesQueryRequestMessage, timeout?: moment.Duration): Promise<TimeSeriesQueryResponseMessage> {
   264    return timeoutFetch(tspb.TimeSeriesQueryResponse, `ts/query`, req as any, timeout);
   265  }
   266  
   267  // getHealth gets health data
   268  export function getHealth(_req: HealthRequestMessage, timeout?: moment.Duration): Promise<HealthResponseMessage> {
   269    return timeoutFetch(serverpb.HealthResponse, `${API_PREFIX}/health`, null, timeout);
   270  }
   271  
   272  export function getJobs(req: JobsRequestMessage, timeout?: moment.Duration): Promise<JobsResponseMessage> {
   273    return timeoutFetch(serverpb.JobsResponse, `${API_PREFIX}/jobs?status=${req.status}&type=${req.type}&limit=${req.limit}`, null, timeout);
   274  }
   275  
   276  // getCluster gets info about the cluster
   277  export function getCluster(_req: ClusterRequestMessage, timeout?: moment.Duration): Promise<ClusterResponseMessage> {
   278    return timeoutFetch(serverpb.ClusterResponse, `${API_PREFIX}/cluster`, null, timeout);
   279  }
   280  
   281  // getTableStats gets detailed stats about the current table
   282  export function getTableStats(req: TableStatsRequestMessage, timeout?: moment.Duration): Promise<TableStatsResponseMessage> {
   283    return timeoutFetch(serverpb.TableStatsResponse, `${API_PREFIX}/databases/${req.database}/tables/${req.table}/stats`, null, timeout);
   284  }
   285  
   286  // getNonTableStats gets detailed stats about non-table data ranges on the
   287  // cluster.
   288  export function getNonTableStats(_req: NonTableStatsRequestMessage, timeout?: moment.Duration): Promise<NonTableStatsResponseMessage> {
   289    return timeoutFetch(serverpb.NonTableStatsResponse, `${API_PREFIX}/nontablestats`, null, timeout);
   290  }
   291  
   292  // TODO (maxlang): add filtering
   293  // getLogs gets the logs for a specific node
   294  export function getLogs(req: LogsRequestMessage, timeout?: moment.Duration): Promise<LogEntriesResponseMessage> {
   295    return timeoutFetch(serverpb.LogEntriesResponse, `${STATUS_PREFIX}/logs/${req.node_id}`, null, timeout);
   296  }
   297  
   298  // getLiveness gets cluster liveness information from the current node.
   299  export function getLiveness(_req: LivenessRequestMessage, timeout?: moment.Duration): Promise<LivenessResponseMessage> {
   300    return timeoutFetch(serverpb.LivenessResponse, `${API_PREFIX}/liveness`, null, timeout);
   301  }
   302  
   303  // getQueryPlan gets physical query plan JSON for the provided query.
   304  export function getQueryPlan(req: QueryPlanRequestMessage, timeout?: moment.Duration): Promise<QueryPlanResponseMessage> {
   305    return timeoutFetch(serverpb.QueryPlanResponse, `${API_PREFIX}/queryplan?query=${encodeURIComponent(req.query)}`, null, timeout);
   306  }
   307  
   308  // getProblemRanges returns information needed by the problem range debug page.
   309  export function getProblemRanges(req: ProblemRangesRequestMessage, timeout?: moment.Duration): Promise<ProblemRangesResponseMessage> {
   310    const query = (!_.isEmpty(req.node_id)) ? `?node_id=${req.node_id}` : "";
   311    return timeoutFetch(serverpb.ProblemRangesResponse, `${STATUS_PREFIX}/problemranges${query}`, null, timeout);
   312  }
   313  
   314  // getCertificates returns information about a node's certificates.
   315  export function getCertificates(req: CertificatesRequestMessage, timeout?: moment.Duration): Promise<CertificatesResponseMessage> {
   316    return timeoutFetch(serverpb.CertificatesResponse, `${STATUS_PREFIX}/certificates/${req.node_id}`, null, timeout);
   317  }
   318  
   319  // getRange returns information about a range form all nodes.
   320  export function getRange(req: RangeRequestMessage, timeout?: moment.Duration): Promise<RangeResponseMessage> {
   321    return timeoutFetch(serverpb.RangeResponse, `${STATUS_PREFIX}/range/${req.range_id}`, null, timeout);
   322  }
   323  
   324  // getAllocatorRange returns simulated Allocator info for the requested range
   325  export function getAllocatorRange(req: AllocatorRangeRequestMessage, timeout?: moment.Duration): Promise<AllocatorRangeResponseMessage> {
   326    return timeoutFetch(serverpb.AllocatorRangeResponse, `${STATUS_PREFIX}/allocator/range/${req.range_id}`, null, timeout);
   327  }
   328  
   329  // getRangeLog returns the range log for all ranges or a specific range
   330  export function getRangeLog(
   331    req: RangeLogRequestMessage,
   332    timeout?: moment.Duration,
   333  ): Promise<RangeLogResponseMessage> {
   334    const rangeID = FixLong(req.range_id);
   335    const rangeIDQuery = (rangeID.eq(0)) ? "" : `/${rangeID.toString()}`;
   336    const limit = (!_.isNil(req.limit)) ? `?limit=${req.limit}` : "";
   337    return timeoutFetch(
   338      serverpb.RangeLogResponse,
   339      `${API_PREFIX}/rangelog${rangeIDQuery}${limit}`,
   340      null,
   341      timeout,
   342    );
   343  }
   344  
   345  // getSettings gets all cluster settings. We request unredacted_values, which will attempt
   346  // to obtain all values from the server. The server will only accept to do so if
   347  // the user also happens to have admin privilege.
   348  export function getSettings(_req: SettingsRequestMessage, timeout?: moment.Duration): Promise<SettingsResponseMessage> {
   349    return timeoutFetch(serverpb.SettingsResponse, `${API_PREFIX}/settings?unredacted_values=true`, null, timeout);
   350  }
   351  
   352  export function userLogin(req: UserLoginRequestMessage, timeout?: moment.Duration): Promise<UserLoginResponseMessage> {
   353    return timeoutFetch(serverpb.UserLoginResponse, `login`, req as any, timeout);
   354  }
   355  
   356  export function userLogout(timeout?: moment.Duration): Promise<UserLogoutResponseMessage> {
   357    return timeoutFetch(serverpb.UserLogoutResponse, `logout`, null, timeout);
   358  }
   359  
   360  // getStores returns information about a node's stores.
   361  export function getStores(req: StoresRequestMessage, timeout?: moment.Duration): Promise<StoresResponseMessage> {
   362    return timeoutFetch(serverpb.StoresResponse, `${STATUS_PREFIX}/stores/${req.node_id}`, null, timeout);
   363  }
   364  
   365  // getStatements returns statements the cluster has recently executed, and some stats about them.
   366  export function getStatements(timeout?: moment.Duration): Promise<StatementsResponseMessage> {
   367    return timeoutFetch(serverpb.StatementsResponse, `${STATUS_PREFIX}/statements`, null, timeout);
   368  }
   369  
   370  export function getStatementDiagnosticsReports(timeout?: moment.Duration): Promise<StatementDiagnosticsReportsResponseMessage> {
   371    return timeoutFetch(serverpb.StatementDiagnosticsReportsResponse, `${STATUS_PREFIX}/stmtdiagreports`, null, timeout);
   372  }
   373  
   374  export function createStatementDiagnosticsReport(req: CreateStatementDiagnosticsReportRequestMessage, timeout?: moment.Duration): Promise<CreateStatementDiagnosticsReportResponseMessage> {
   375    return timeoutFetch(serverpb.CreateStatementDiagnosticsReportResponse, `${STATUS_PREFIX}/stmtdiagreports`, req as any, timeout);
   376  }
   377  
   378  export function getStatementDiagnostics(req: StatementDiagnosticsRequestMessage, timeout?: moment.Duration): Promise<StatementDiagnosticsResponseMessage> {
   379    return timeoutFetch(serverpb.StatementDiagnosticsResponse, `${STATUS_PREFIX}/stmtdiag/${req.statement_diagnostics_id}`, null, timeout);
   380  }
   381  
   382  // getDataDistribution returns information about how replicas are distributed across nodes.
   383  export function getDataDistribution(timeout?: moment.Duration): Promise<DataDistributionResponseMessage> {
   384    return timeoutFetch(serverpb.DataDistributionResponse, `${API_PREFIX}/data_distribution`, null, timeout);
   385  }
   386  
   387  export function enqueueRange(req: EnqueueRangeRequestMessage, timeout?: moment.Duration): Promise<EnqueueRangeResponseMessage> {
   388    return timeoutFetch(serverpb.EnqueueRangeResponse, `${API_PREFIX}/enqueue_range`, req as any, timeout);
   389  }
   390  
   391  export function getAllMetricMetadata(_req: MetricMetadataRequestMessage = null, timeout?: moment.Duration): Promise<MetricMetadataResponseMessage> {
   392    return timeoutFetch(serverpb.MetricMetadataResponse, `${API_PREFIX}/metricmetadata`, null, timeout);
   393  }