LCOV - code coverage report
Current view: top level - dashql-app/src/connections - connection_params.ts Coverage Total Hit
Test: DashQL 6e74948 Lines: 100.0 % 68 68
Test Date: 2026-11-06 17:46:50

            Line data    Source code
       1              : import * as dashql from '@ankoh/dashql-core';
       2              : import type * as app_session from '@ankoh/dashql-jsonschema/app_session.js';
       3              : 
       4              : import { CONNECTOR_INFOS, ConnectorType, ConnectorInfo } from './connector_info.js';
       5              : import { ConnectionStateWithoutId, createConnectionState } from './connection_state.js';
       6              : import { ConnectionStateDetailsVariant } from './connection_state_details.js';
       7              : import { CONNECTOR_DESCRIPTORS, connectionParamsOf, connectionStateDetailsOf, connectorOfDetails, connectorOfParams, connectorParams } from './connector_descriptor.js';
       8              : 
       9              : // Re-export connection param types from JSON Schema
      10              : export type ConnectionParams = app_session.ConnectionParams;
      11              : export type HyperConnectionParams = app_session.HyperConnectionParams;
      12              : export type SalesforceConnectionParams = app_session.SalesforceConnectionParams;
      13              : export type TrinoConnectionParams = app_session.TrinoConnectionParams;
      14              : 
      15              : /// Raw form rows belong only to the local draft, never to committed/runtime parameters.
      16            1 : export function connectionParamsWithoutDraftRows(params: ConnectionParams): ConnectionParams {
      17            1 :     const connector = connectorOfParams(params);
      18            1 :     return connectionParamsOf(connector, connector.withoutDraftRows(connectorParams(connector, params)));
      19            1 : }
      20              : 
      21              : /// Sort the keys of an object so that its encoding doesn't depend on their order
      22            1 : function sortObjectKeys(value: unknown): unknown {
      23            1 :     return value != null && typeof value === 'object' && !Array.isArray(value)
      24            1 :         ? Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b))) : value;
      25            1 : }
      26              : 
      27              : /// Is a value an empty list or an empty map?
      28            1 : function isEmptyCollection(value: unknown): boolean {
      29            1 :     return value != null && typeof value === 'object' && Object.keys(value).length === 0;
      30            1 : }
      31              : 
      32              : /// A canonical encoding of connection params that doesn't depend on the order of object keys
      33            1 : export function connectionParamsKey(params: ConnectionParams | undefined): string {
      34            1 :     return JSON.stringify(params, (_key, value) => sortObjectKeys(value)) ?? '';
      35            1 : }
      36              : 
      37            1 : export function connectionParamsEqual(left: ConnectionParams | undefined, right: ConnectionParams | undefined): boolean {
      38            1 :     return connectionParamsKey(left) === connectionParamsKey(right);
      39            1 : }
      40              : 
      41              : /// A canonical encoding of the database that connection params describe.
      42              : /// Secrets and login hints are stripped like for sharing, and empty lists and maps count as absent:
      43              : /// default params leave them out, params written by a form or a session file carry them empty.
      44            1 : function connectionIdentityKey(params: ConnectionParams): string {
      45            1 :     return JSON.stringify(sanitizeConnectionParamsForSharing(params, false), function (this: unknown, _key, value) {
      46            1 :         return !Array.isArray(this) && isEmptyCollection(value) ? undefined : sortObjectKeys(value);
      47            1 :     }) ?? '';
      48            1 : }
      49              : 
      50              : /// Do two params describe the same connection, ignoring everything that sharing strips?
      51              : /// Shared sessions carry sanitized params without secrets and login hints, they still describe the
      52              : /// connection a local session uses with the full params.
      53            1 : export function connectionParamsShareEqual(left: ConnectionParams, right: ConnectionParams): boolean {
      54            1 :     return connectionIdentityKey(left) === connectionIdentityKey(right);
      55            1 : }
      56              : 
      57              : /// Do the relations of a connection outlive the app? See `ConnectorDescriptor.catalogIsDurable`.
      58            1 : export function connectionCatalogIsDurable(params: ConnectionParams): boolean {
      59            1 :     const connector = connectorOfParams(params);
      60            1 :     return connector.catalogIsDurable(connectorParams(connector, params));
      61            1 : }
      62              : 
      63            1 : export function getConnectionStateDetailsFromParams(params: ConnectionParams): ConnectionStateDetailsVariant {
      64            1 :     const connector = connectorOfParams(params);
      65            1 :     return connectionStateDetailsOf(connector, connectorParams(connector, params));
      66            1 : }
      67              : 
      68            1 : export function getConnectionParamsFromStateDetails(details: ConnectionStateDetailsVariant): ConnectionParams | null {
      69            1 :     const setupParams = details.value.proto.setupParams;
      70            1 :     return setupParams ? connectionParamsOf(connectorOfDetails(details), setupParams) : null;
      71            1 : }
      72              : 
      73              : /// Strip sensitive credentials from connection params before they are encoded into a shared
      74              : /// link or file. Identity/hint fields (instance urls, consumer keys, usernames, login hints)
      75              : /// are preserved so a recipient can be prompted with a prefilled sign-in, but no secret that
      76              : /// would grant access on its own is included. This is always applied when connection info is
      77              : /// shared — sharing raw secrets is never intended.
      78              : ///
      79              : /// The login hint (the resolved account username / login) identifies the sharer personally, so
      80              : /// it is only carried when `withLoginHint` is true. When false it is cleared while the rest of
      81              : /// the (non-secret) connection identity is preserved.
      82            1 : export function sanitizeConnectionParamsForSharing(params: ConnectionParams, withLoginHint: boolean = true): ConnectionParams {
      83            1 :     const connector = connectorOfParams(params);
      84            1 :     const withoutDraftRows = connector.withoutDraftRows(connectorParams(connector, params));
      85            1 :     return connectionParamsOf(connector, connector.sanitizeForSharing(withoutDraftRows, withLoginHint));
      86            1 : }
      87              : 
      88              : /// Whether the connection params carry a login hint (the resolved account username / login) that
      89              : /// could be shared. Used to decide if the "share login hint" toggle should be offered at all.
      90            1 : export function connectionParamsHaveLoginHint(params: ConnectionParams | null): boolean {
      91            1 :     if (!params) return false;
      92            1 :     const connector = connectorOfParams(params);
      93            1 :     return connector.loginHint(connectorParams(connector, params)) != null;
      94            1 : }
      95              : 
      96              : /// The params that identify the results of a query in the query result cache
      97            1 : export function createConnectionParamsSignature(params: ConnectionParams): unknown {
      98            1 :     const connector = connectorOfParams(params);
      99            1 :     return connector.cacheSignature(connectorParams(connector, params));
     100            1 : }
     101              : 
     102            1 : export function createConnectionStateFromParams(dql: dashql.DashQL, params: ConnectionParams): ConnectionStateWithoutId {
     103            1 :     return createConnectionState(dql, connectorOfParams(params).info, getConnectionStateDetailsFromParams(params), { params });
     104            1 : }
     105              : 
     106            1 : export function createDefaultHyperWasmConnectionState(dql: dashql.DashQL): ConnectionStateWithoutId {
     107            1 :     const connector = CONNECTOR_INFOS[ConnectorType.HYPER];
     108            1 :     return createConnectionStateFromParams(dql, createDefaultConnectionParamsForConnector(connector));
     109            1 : }
     110              : 
     111            1 : export function createDefaultConnectionParamsForConnector(info: ConnectorInfo): ConnectionParams {
     112            1 :     const connector = CONNECTOR_DESCRIPTORS[info.connectorType];
     113            1 :     return connectionParamsOf(connector, connector.defaultParams());
     114            1 : }
     115              : 
     116              : /// The endpoint that a new connection to a Hyper server starts with
     117            1 : export const DEFAULT_HYPER_ENDPOINT = 'http://localhost:7484';
     118              : 
     119              : /// The params of a new connection that the user attaches.
     120              : /// The app holds a single connection to the embedded Hyper, an attached Hyper connection speaks to a server.
     121            1 : export function createAttachedConnectionParamsForConnector(info: ConnectorInfo): ConnectionParams {
     122            1 :     const params = createDefaultConnectionParamsForConnector(info);
     123            1 :     if (info.connectorType !== ConnectorType.HYPER || !('hyper' in params)) return params;
     124            1 :     return { ...params, hyper: { ...params.hyper, protocol: 'V3_HTTP', endpoint: DEFAULT_HYPER_ENDPOINT } };
     125            1 : }
        

Generated by: LCOV version 2.0-1