UNPKG

@getanthill/datastore

Version:

Event-Sourced Datastore

341 lines 12 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const axios_1 = __importDefault(require("axios")); const get_1 = __importDefault(require("lodash/get")); const has_1 = __importDefault(require("lodash/has")); const DEFAULT_HEADERS = Object.freeze({ 'Content-Type': 'application/json', Accept: 'application/json', }); class Metabase { constructor(config) { this.config = { baseUrl: 'http://localhost:3000', }; this.sessionToken = ''; this.config = { ...this.config, ...config, }; this.axios = axios_1.default.create({ baseURL: this.config.baseUrl, timeout: this.config.timeout, headers: { ...DEFAULT_HEADERS, }, }); } static toSnakeCase(str) { return str.replace(/ +/g, '_').toLowerCase(); } static toMetabaseDisplayCase(str) { return str .split('_') .map((s) => s[0].toUpperCase() + s.slice(1)) .join(' '); } setSessionToken(token) { this.sessionToken = token; this.axios.defaults.headers.common['X-Metabase-Session'] = token; } async authenticate() { const { data: { id }, } = await this.axios.request({ method: 'post', url: '/api/session', data: { username: this.config.username, password: this.config.password, }, }); this.setSessionToken(id); return id; } async request(config) { if (!this.sessionToken) { throw Metabase.ERRORS.UNAUTHENTICATED; } const { data } = await this.axios.request(config); return data; } getCurrentUser() { return this.request({ method: 'get', url: '/api/user/current', }); } getDatabases() { return this.request({ method: 'get', url: '/api/database', }); } getTableForeignKeys(tableId) { return this.request({ method: 'get', url: `/api/table/${tableId}/fks`, }); } getTableFields(tableId) { var _a, _b; return (_b = (_a = this.getTables().find((t) => t.id === tableId)) === null || _a === void 0 ? void 0 : _a.fields) !== null && _b !== void 0 ? _b : []; } getField(fieldId) { return this.request({ method: 'get', url: `/api/field/${fieldId}`, }); } updateField(fieldId, field) { return this.request({ method: 'put', url: `/api/field/${fieldId}`, data: field, }); } async getDatabaseTables(databaseId) { const metadata = await this.request({ method: 'get', url: `/api/database/${databaseId}/metadata`, params: { include_hidden: true, }, }); return metadata.tables; } updateTable(tableId, table) { return this.request({ method: 'put', url: `/api/table/${tableId}`, data: table, }); } /** Model schema sync */ static getNormalizedName(item) { if (Array.isArray(item.nfc_path)) { let paths = item.nfc_path; if (['event', 'entity'].includes(paths[0])) { paths.shift(); } paths = paths.filter((p) => !!p); return paths.map((p) => Metabase.toSnakeCase(p)).join('.'); } return Metabase.toSnakeCase(item.name); } static getModelNameFromTable(table) { return Metabase.getNormalizedName(table).replace(/_(events|snapshots)$/, ''); } static isEventsTable(table) { return Metabase.getNormalizedName(table).endsWith('_events'); } static isSnapshotsTable(table) { return Metabase.getNormalizedName(table).endsWith('_snapshots'); } static isCorrelationField(field, model) { const fieldName = Metabase.getNormalizedName(field); return model.correlation_field === fieldName; } static isEncryptedField(field, model) { var _a; const fieldName = Metabase.getNormalizedName(field); return ((_a = model.encrypted_fields) !== null && _a !== void 0 ? _a : []).includes(fieldName); } static getFieldVisibility(field, model, defaultValue = 'sensitive') { if (Metabase.isEncryptedField(field, model) === true) { return 'sensitive'; } if (Metabase.isCorrelationField(field, model) === true) { return 'normal'; } const fieldName = Metabase.getNormalizedName(field); const isEventsTable = Metabase.isEventsTable({ name: field.table_name, }); if (isEventsTable === true && fieldName === 'type') { return 'normal'; } if (['created_at', 'updated_at', 'version'].includes(fieldName)) { return 'normal'; } if ((0, has_1.default)(model, `schema.model.properties.${fieldName.split('.').join('.properties.')}`)) { return 'normal'; } return defaultValue; } /** * @see https://github.com/metabase/metabase/blob/6a6327646964559e735c3557d8c39f5ceff5dcd8/shared/src/metabase/types.cljc */ static getBaseTypeFromJsonSchema(field, model, fieldSchema, defaultBaseType = null) { if (fieldSchema.type === 'boolean') { return 'type/Boolean'; } if (fieldSchema.type === 'integer') { return 'type/Integer'; } if (fieldSchema.type === 'number') { return 'type/Float'; } return defaultBaseType; } /** * @see https://github.com/metabase/metabase/blob/6a6327646964559e735c3557d8c39f5ceff5dcd8/shared/src/metabase/types.cljc */ static getSemanticTypeFromJsonSchema(field, model, fieldSchema, defaultSemanticType = null) { if (Metabase.isCorrelationField(field, model)) { return 'type/PK'; } const fieldName = Metabase.getNormalizedName(field); const isEventsTable = Metabase.isEventsTable({ name: field.table_name, }); if (isEventsTable === true && fieldName === 'type') { return 'type/Category'; } if (fieldName === 'name') { return 'type/Name'; } if (fieldName === 'description') { return 'type/Description'; } if (fieldName === 'created_at') { return 'type/CreationTimestamp'; } if (fieldName === 'updated_at') { return 'type/UpdatedTimestamp'; } if (fieldSchema.type === 'boolean') { return 'type/Category'; } return defaultSemanticType; } setTables(tables) { this.config.tables = tables; return this; } getTables() { var _a; return (_a = this.config.tables) !== null && _a !== void 0 ? _a : []; } getTableById(id) { return this.getTables().find((table) => table.id === id); } setFields(fields) { this.config.fields = fields; return this; } getFields() { var _a; return (_a = this.config.fields) !== null && _a !== void 0 ? _a : []; } setModels(models) { this.config.models = models; return this; } getModels() { var _a; return (_a = this.config.models) !== null && _a !== void 0 ? _a : []; } setGraph(graph) { this.config.graph = graph; return this; } getGraph() { var _a; return ((_a = this.config.graph) !== null && _a !== void 0 ? _a : { nodes: [], edges: [], }); } getModelFromTable(table) { const modelName = Metabase.getModelNameFromTable(table); return this.getModels().find((m) => modelName === m.name || `${this.config.namespace}_${m.name}` === modelName || `${m.db}_${m.name}` === modelName); } getTableUpdatePayload(table) { var _a; const model = this.getModelFromTable(table); if (!model) { return null; } const tableName = Metabase.getNormalizedName(table); const payload = { name: tableName, display_name: tableName, description: (_a = model.description) !== null && _a !== void 0 ? _a : null, visibility_type: Metabase.isSnapshotsTable(table) === true ? 'hidden' : null, }; if (Metabase.isEventsTable(table) === true) { payload.description = 'Events associated to these entities. ' + (payload.description || ''); } return payload; } getFieldUpdatePayload(field) { const table = this.getTableById(field.table_id); if (!table) { return null; } field.table_name = table.name; const model = this.getModelFromTable({ name: field.table_name, }); if (!model) { return null; } const fieldName = Metabase.getNormalizedName(field); const isCorrelationField = Metabase.isCorrelationField(field, model); const fieldJsonSchema = (0, get_1.default)(model, `schema.model.properties.${fieldName}`, {}); const payload = { name: fieldName, display_name: fieldName, description: isCorrelationField === true ? 'Correlation field' : (fieldJsonSchema === null || fieldJsonSchema === void 0 ? void 0 : fieldJsonSchema.description) || null, base_type: Metabase.getBaseTypeFromJsonSchema(field, model, fieldJsonSchema, field.base_type), semantic_type: Metabase.getSemanticTypeFromJsonSchema(field, model, fieldJsonSchema, field.semantic_type), visibility_type: Metabase.getFieldVisibility(field, model, field.visibility_type), }; return this.getFieldUpdatePayloadWithForeignKeys(field, payload); } getFieldUpdatePayloadWithForeignKeys(field, payload) { const graph = this.getGraph(); const table = this.getTableById(field.table_id); if (!table) { return payload; } const model = this.getModelFromTable(table); if (!model) { return payload; } const fieldName = Metabase.getNormalizedName(field); const isCorrelationField = Metabase.isCorrelationField(field, model); const isEventsTable = Metabase.isEventsTable(table); const edges = graph.edges.filter((f) => f.source === model.name && f.key === fieldName); if (isCorrelationField === true && isEventsTable === true) { const correlationField = this.getFields().find((f) => Metabase.getNormalizedName(this.getTableById(f.table_id)) === model.name && Metabase.getNormalizedName(f) === fieldName); if (correlationField) { payload.semantic_type = 'type/FK'; payload.fk_target_field_id = correlationField.id; } } else if (edges.length === 1) { const correlationField = this.getFields().find((f) => Metabase.getNormalizedName(this.getTableById(f.table_id)) === edges[0].target && Metabase.getNormalizedName(f) === edges[0].correlation_field); if (correlationField) { payload.semantic_type = 'type/FK'; payload.fk_target_field_id = correlationField.id; } } return payload; } } Metabase.ERRORS = { UNAUTHENTICATED: new Error('Client must be authenticated'), }; exports.default = Metabase; //# sourceMappingURL=Metabase.js.map