Add ClickHouse support#2467
Conversation
evantahler
left a comment
There was a problem hiding this comment.
Nice! You've set up the boilerplate.
There are some changes to make the package.json, but otherwise this looks good!
evantahler
left a comment
There was a problem hiding this comment.
I think the next step is to wire up CI. Then there are few TODOs to fixup, and this will be ready!
60bb6ed to
be90b6d
Compare
|
All the tests are no passing, fixed the template issues and rebased with main. I still need to apply the changes from #2522 |
* Add custom getColumns function to identify CH types
* Cleanup types in MySQL plugin * Update connection ifaces in plugin to accept type instead of `any`
* Update table import to use own implementation of getting data * Implement selects to do type casting based on column types in order to be able to apply the queries * Update `cleanupDataType` to not change case and move to `utils`
208c4ec to
f0f2a72
Compare
|
Applied feedback from the review. After looking into how to resolve the problems with the mysql query conflicts while running the tests in parallel, I couldn't find a good answer online and I feared that this would be come a problem in production. I saw the same issue occur while running exports from the UI app. So, I implemented a custom connect with the clickhouse npm module, which uses the HTTP interface. |
evantahler
left a comment
There was a problem hiding this comment.
🥳
Just lock that dependency, and this is 👍!
|
Addressed review comments and pushed changes! |
| import path from "path"; | ||
| import fs from "fs-extra"; | ||
| import os from "os"; | ||
|
|
||
| process.env.GROUPAROO_INJECTED_PLUGINS = JSON.stringify({ | ||
| "@grouparoo/clickhouse": { path: path.join(__dirname, "..", "..") }, | ||
| }); | ||
|
|
||
| import { helper } from "@grouparoo/spec-helper"; | ||
| import { api } from "actionhero"; | ||
| import { Generate } from "@grouparoo/core/src/bin/generate"; | ||
| import { Apply } from "@grouparoo/core/src/bin/apply"; | ||
| import { | ||
| beforeData, | ||
| afterData, | ||
| appOptions, | ||
| usersTableName, | ||
| } from "../utils/data"; | ||
|
|
||
| process.env.GROUPAROO_CONFIG_DIR = `${os.tmpdir()}/test/${ | ||
| process.env.JEST_WORKER_ID | ||
| }/config`; |
There was a problem hiding this comment.
Feels a little odd that these aren't all imports -> env
Do the later imports require they be set?
There was a problem hiding this comment.
@evantahler is this intentional for a reason?
| // .replace(`host: "localhost"`, `host: "${appOptions.host}"`) | ||
| // .replace(`port: 5432`, `port: "${appOptions.port}"`) |
There was a problem hiding this comment.
If these are commented out, do we need them?
| // mapping: { id: "userId" }, | ||
| // state: "ready", |
| session | ||
| ); | ||
| expect(bootstrapResponse.error).toBeUndefined(); | ||
| expect(bootstrapResponse.property).toBeTruthy(); |
There was a problem hiding this comment.
If this is a complex object, assuming it doesn't change, could do a snapshot against it.
Or test the object+properties directly if we want that level of detail.
| groupForeignKey = groupForeignKey?.toString(); | ||
| groupColumnName = groupColumnName?.toString(); | ||
|
|
||
| if (Object.keys(newRecordProperties).length === 0) { |
There was a problem hiding this comment.
| if (Object.keys(newRecordProperties).length === 0) { | |
| if (!Object.keys(newRecordProperties).length) { |
Purely stylistic choice.
| oldRecordProperties[k] !== undefined) | ||
| ); | ||
|
|
||
| if (columnsToErase.length > 0) { |
There was a problem hiding this comment.
| if (columnsToErase.length > 0) { | |
| if (columnsToErase.length) { |
Minor nitpick!
| error = e; | ||
| } finally { | ||
| if (error) { | ||
| throw error; | ||
| } |
There was a problem hiding this comment.
Why not just let this throw? Or is error getting grabbed at a higher level?
There was a problem hiding this comment.
I'm not sure but this is how it's implemented in all the SQL-based plugins.
| const out: DataResponseRow[] = []; | ||
| const rows = await connection.asyncQuery(query, params); | ||
| rows.forEach((row) => out.push(row)); | ||
| return out; |
There was a problem hiding this comment.
Why not just return await connection.asyncQuery(query, params)?
There was a problem hiding this comment.
Not sure either, this was a copy and paste from mysql and there may be a reason why copying the values into anew array ([...out]) ?
|
Pushed one more change to address @krishnaglick's feedback and update the version to match main. Ready to merge. |
| const { host, port, database, user, password } = appOptions; | ||
|
|
||
| let url = String(host); | ||
| if (url && !url.match(/^localhost/i) && !url.match(/^https?/)) { |
There was a problem hiding this comment.
It might be good to combine these match calls.
Change description
This change adds a plugin and demo generator for the ClickHouse OLAP DB. Supports source and destination, table and query imports. The plugin is based on on the MySQL plugin.
Demo usage:
Checklists
Development
Impact
Please explain any security, performance, migration, or other impacts if relevant:
Code review