diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index ea1a35ae..9e1ab46f 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -111,6 +111,7 @@ export class AppComponent { this.matIconRegistry.addSvgIcon("cassandra", this.domSanitizer.bypassSecurityTrustResourceUrl("/assets/icons/db-logos/сassandra_logo.svg")); this.matIconRegistry.addSvgIcon("redis", this.domSanitizer.bypassSecurityTrustResourceUrl("/assets/icons/db-logos/redis_logo.svg")); this.matIconRegistry.addSvgIcon("elasticsearch", this.domSanitizer.bypassSecurityTrustResourceUrl("/assets/icons/db-logos/elasticsearch_logo.svg")); + this.matIconRegistry.addSvgIcon("clickhouse", this.domSanitizer.bypassSecurityTrustResourceUrl("/assets/icons/db-logos/clickhouse_logo.svg")); this.matIconRegistry.addSvgIcon("github", this.domSanitizer.bypassSecurityTrustResourceUrl("/assets/icons/github.svg")); this.matIconRegistry.addSvgIcon("google", this.domSanitizer.bypassSecurityTrustResourceUrl("/assets/icons/google.svg")); this.matIconRegistry.addSvgIcon("ai_rocket", this.domSanitizer.bypassSecurityTrustResourceUrl("/assets/icons/ai-rocket.svg")); diff --git a/frontend/src/app/components/connect-db/connect-db.component.html b/frontend/src/app/components/connect-db/connect-db.component.html index 8ebfb379..1ebc60b4 100644 --- a/frontend/src/app/components/connect-db/connect-db.component.html +++ b/frontend/src/app/components/connect-db/connect-db.component.html @@ -193,6 +193,20 @@

(masterKeyChange)="handleMasterKeyChange($event)"> + + + + Hostname + + + E.g. my-test-db.eu-west-2.aws.clickhouse.cloud +
+ Connections from internal IPs (e.g. localhost) are not supported +
+ + + To connect a database to an internal IP, use something like Pinggy + (here's a guide), + or to connect through an agent + + Hostname is invalid + + + + Port + + Port should not be empty + + + + Username + + Username should not be empty + + + + Password + + To keep password the same keep this field blank + Password needed due to hostname/port change + + + + + Database Name + + Name should not be empty + + + + + + Advanced settings + + + +
+ + + + + Use SSH tunnel + + + + Private SSH key + + Private SSH key should not be empty + + + + SSH host + + SSH host should not be empty + + + + SSH port + + SSH port should not be empty + + + + SSH username + + SSH username should not be empty + + + + Check SSL certificate + + + + SSL certificate + + SSL certificate should not be empty + +
+
\ No newline at end of file diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.spec.ts b/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.spec.ts new file mode 100644 index 00000000..7b03b62e --- /dev/null +++ b/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.spec.ts @@ -0,0 +1,40 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ClickhouseCredentialsFormComponent } from './clickhouse-credentials-form.component'; +import { FormsModule } from '@angular/forms'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { Angulartics2Module } from 'angulartics2'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { provideHttpClient } from '@angular/common/http'; + +describe('ClickhouseCredentialsFormComponent', () => { + let component: ClickhouseCredentialsFormComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + FormsModule, + MatCheckboxModule, + BrowserAnimationsModule, + Angulartics2Module.forRoot({}), + ClickhouseCredentialsFormComponent + ], + providers: [provideHttpClient()] +}) + .compileComponents(); + + fixture = TestBed.createComponent(ClickhouseCredentialsFormComponent); + component = fixture.componentInstance; + + component.connection = { + id: "12345678" + } as any; + + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.ts b/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.ts new file mode 100644 index 00000000..da950e9e --- /dev/null +++ b/frontend/src/app/components/connect-db/db-credentials-forms/clickhouse-credentials-form/clickhouse-credentials-form.component.ts @@ -0,0 +1,32 @@ +import { Angulartics2Module } from 'angulartics2'; +import { BaseCredentialsFormComponent } from '../base-credentials-form/base-credentials-form.component'; +import { Component } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { HostnameValidationDirective } from 'src/app/directives/hostnameValidator.directive'; +import { MasterEncryptionPasswordComponent } from '../../master-encryption-password/master-encryption-password.component'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { NgIf } from '@angular/common'; + +@Component({ + selector: 'app-clickhouse-credentials-form', + templateUrl: './clickhouse-credentials-form.component.html', + styleUrls: ['../base-credentials-form/base-credentials-form.component.css', './clickhouse-credentials-form.component.css'], + standalone: true, + imports: [ + NgIf, + FormsModule, + MatFormFieldModule, + MatInputModule, + MatCheckboxModule, + MatExpansionModule, + HostnameValidationDirective, + MasterEncryptionPasswordComponent, + Angulartics2Module + ] +}) +export class ClickhouseCredentialsFormComponent extends BaseCredentialsFormComponent { + +} diff --git a/frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts b/frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts index b255a7fb..3ec715d8 100644 --- a/frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts +++ b/frontend/src/app/components/db-table-row-edit/db-table-row-edit.component.ts @@ -227,7 +227,7 @@ export class DbTableRowEditComponent implements OnInit { .filter((field: TableField) => !this.getModifyingFields(res.structure).some(modifyingField => field.column_name === modifyingField.column_name)) .map((field: TableField) => field.column_name); this.readonlyFields = [...res.readonly_fields, ...this.nonModifyingFields]; - if (this.connectionType === DBtype.Dynamo) { + if (this.connectionType === DBtype.Dynamo || this.connectionType === DBtype.ClickHouse) { this.readonlyFields = [...this.readonlyFields, ...res.primaryColumns.map((field: TableField) => field.column_name)]; } this.tableForeignKeys = res.foreignKeys; @@ -584,7 +584,7 @@ export class DbTableRowEditComponent implements OnInit { //end crutch // don't ovverride primary key fields for dynamoDB - if (this.connectionType === DBtype.Dynamo) { + if (this.connectionType === DBtype.Dynamo || this.connectionType === DBtype.ClickHouse) { const primaryKeyFields = Object.keys(this.keyAttributesFromURL); primaryKeyFields.forEach((field) => { delete updatedRow[field]; diff --git a/frontend/src/app/consts/databases.ts b/frontend/src/app/consts/databases.ts index 82a05a71..5effddbd 100644 --- a/frontend/src/app/consts/databases.ts +++ b/frontend/src/app/consts/databases.ts @@ -8,6 +8,7 @@ export const supportedOrderedDatabases = [ "mssql", "redis", "elasticsearch", + "clickhouse", "ibmdb2" ] @@ -21,5 +22,6 @@ export const supportedDatabasesTitles = { mssql: "SQL Server", redis: "Redis", elasticsearch: "Elasticsearch", + clickhouse: "ClickHouse", ibmdb2: "IBM DB2" } \ No newline at end of file diff --git a/frontend/src/app/consts/record-edit-types.ts b/frontend/src/app/consts/record-edit-types.ts index 6b698ba9..f16cb448 100644 --- a/frontend/src/app/consts/record-edit-types.ts +++ b/frontend/src/app/consts/record-edit-types.ts @@ -327,5 +327,20 @@ export const recordEditTypes = { object: JsonEditorEditComponent, array: JsonEditorEditComponent, binary: FileEditComponent, + }, + clickhouse: { + string: TextEditComponent, + uuid: UuidEditComponent, + boolean: BooleanEditComponent, + integer: NumberEditComponent, + bigint: NumberEditComponent, + float: NumberEditComponent, + double: NumberEditComponent, + decimal: NumberEditComponent, + date: DateEditComponent, + datetime: DateTimeEditComponent, + json: JsonEditorEditComponent, + object: JsonEditorEditComponent, + array: JsonEditorEditComponent, } } diff --git a/frontend/src/app/consts/record-view-types.ts b/frontend/src/app/consts/record-view-types.ts index e90b35ca..fface9b2 100644 --- a/frontend/src/app/consts/record-view-types.ts +++ b/frontend/src/app/consts/record-view-types.ts @@ -320,5 +320,20 @@ export const recordViewFieldTypes = { object: JsonEditorRecordViewComponent, array: JsonEditorRecordViewComponent, binary: FileRecordViewComponent, + }, + clickhouse: { + string: TextRecordViewComponent, + uuid: UuidRecordViewComponent, + boolean: BooleanRecordViewComponent, + integer: NumberRecordViewComponent, + bigint: NumberRecordViewComponent, + float: NumberRecordViewComponent, + double: NumberRecordViewComponent, + decimal: NumberRecordViewComponent, + date: DateRecordViewComponent, + datetime: DateTimeRecordViewComponent, + json: JsonEditorRecordViewComponent, + object: JsonEditorRecordViewComponent, + array: JsonEditorRecordViewComponent, } } diff --git a/frontend/src/app/consts/table-display-types.ts b/frontend/src/app/consts/table-display-types.ts index abb6edf7..3d7bd039 100644 --- a/frontend/src/app/consts/table-display-types.ts +++ b/frontend/src/app/consts/table-display-types.ts @@ -320,5 +320,20 @@ export const tableDisplayTypes = { object: JsonEditorDisplayComponent, array: JsonEditorDisplayComponent, binary: FileDisplayComponent, + }, + clickhouse: { + string: TextDisplayComponent, + uuid: UuidDisplayComponent, + boolean: BooleanDisplayComponent, + integer: NumberDisplayComponent, + bigint: NumberDisplayComponent, + float: NumberDisplayComponent, + double: NumberDisplayComponent, + decimal: NumberDisplayComponent, + date: DateDisplayComponent, + datetime: DateTimeDisplayComponent, + json: JsonEditorDisplayComponent, + object: JsonEditorDisplayComponent, + array: JsonEditorDisplayComponent, } } diff --git a/frontend/src/app/models/connection.ts b/frontend/src/app/models/connection.ts index ab6d5d1d..a9fd268a 100644 --- a/frontend/src/app/models/connection.ts +++ b/frontend/src/app/models/connection.ts @@ -10,6 +10,7 @@ export enum DBtype { Cassandra = 'cassandra', Redis = 'redis', Elasticsearch = 'elasticsearch', + ClickHouse = 'clickhouse', DB2 = 'ibmdb2' } diff --git a/frontend/src/assets/icons/db-logos/clickhouse_logo.svg b/frontend/src/assets/icons/db-logos/clickhouse_logo.svg new file mode 100644 index 00000000..6484dc6b --- /dev/null +++ b/frontend/src/assets/icons/db-logos/clickhouse_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file