mirror of https://github.com/nocodb/nocodb
Browse Source
* chore: preps Signed-off-by: mertmit <mertmit99@gmail.com> * test: fix unit Signed-off-by: mertmit <mertmit99@gmail.com> --------- Signed-off-by: mertmit <mertmit99@gmail.com>pull/9999/head
Mert E.
3 days ago
committed by
GitHub
72 changed files with 1636 additions and 278 deletions
@ -0,0 +1,29 @@ |
|||||||
|
import type { Knex } from 'knex'; |
||||||
|
import { MetaTable } from '~/utils/globals'; |
||||||
|
|
||||||
|
const up = async (knex: Knex) => { |
||||||
|
await knex.schema.createTable(MetaTable.COL_LONG_TEXT, (table) => { |
||||||
|
table.string('id', 20).primary(); |
||||||
|
table.string('fk_workspace_id', 20); |
||||||
|
table.string('base_id', 20); |
||||||
|
table.string('fk_model_id', 20); |
||||||
|
table.string('fk_column_id', 20); |
||||||
|
|
||||||
|
table.string('fk_integration_id', 20); |
||||||
|
table.string('model', 255); |
||||||
|
|
||||||
|
table.text('prompt'); |
||||||
|
table.text('prompt_raw'); |
||||||
|
table.text('error'); |
||||||
|
|
||||||
|
table.timestamps(true, true); |
||||||
|
|
||||||
|
table.index('fk_column_id'); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
const down = async (knex: Knex) => { |
||||||
|
await knex.schema.dropTable(MetaTable.COL_LONG_TEXT); |
||||||
|
}; |
||||||
|
|
||||||
|
export { up, down }; |
@ -0,0 +1,48 @@ |
|||||||
|
import type { NcContext } from '~/interface/config'; |
||||||
|
import Noco from '~/Noco'; |
||||||
|
import LongTextColumn from '~/models/LongTextColumn'; |
||||||
|
|
||||||
|
export default class AIColumn extends LongTextColumn { |
||||||
|
id: string; |
||||||
|
|
||||||
|
fk_integration_id: string; |
||||||
|
model: string; |
||||||
|
prompt: string; |
||||||
|
prompt_raw: string; |
||||||
|
error?: string; |
||||||
|
|
||||||
|
public static castType(data: AIColumn): AIColumn { |
||||||
|
return data && new AIColumn(data); |
||||||
|
} |
||||||
|
|
||||||
|
public static async insert( |
||||||
|
context: NcContext, |
||||||
|
aiColumn: Partial<AIColumn> & { |
||||||
|
fk_model_id: string; |
||||||
|
fk_column_id: string; |
||||||
|
}, |
||||||
|
ncMeta = Noco.ncMeta, |
||||||
|
) { |
||||||
|
return this._insert( |
||||||
|
context, |
||||||
|
aiColumn, |
||||||
|
['fk_integration_id', 'model', 'prompt', 'prompt_raw', 'error'], |
||||||
|
ncMeta, |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
public static async update( |
||||||
|
context: NcContext, |
||||||
|
columnId: string, |
||||||
|
aiColumn: Partial<AIColumn>, |
||||||
|
ncMeta = Noco.ncMeta, |
||||||
|
) { |
||||||
|
return this._update( |
||||||
|
context, |
||||||
|
columnId, |
||||||
|
aiColumn, |
||||||
|
['fk_integration_id', 'model', 'prompt', 'prompt_raw', 'error'], |
||||||
|
ncMeta, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,113 @@ |
|||||||
|
import type { NcContext } from '~/interface/config'; |
||||||
|
import Noco from '~/Noco'; |
||||||
|
import NocoCache from '~/cache/NocoCache'; |
||||||
|
import { extractProps } from '~/helpers/extractProps'; |
||||||
|
import { CacheGetType, CacheScope, MetaTable } from '~/utils/globals'; |
||||||
|
import { Column } from '~/models/index'; |
||||||
|
import { NcError } from '~/helpers/catchError'; |
||||||
|
|
||||||
|
export default abstract class LongTextColumn { |
||||||
|
id: string; |
||||||
|
|
||||||
|
fk_workspace_id?: string; |
||||||
|
base_id: string; |
||||||
|
fk_model_id: string; |
||||||
|
fk_column_id: string; |
||||||
|
|
||||||
|
constructor(data: Partial<LongTextColumn>) { |
||||||
|
Object.assign(this, data); |
||||||
|
} |
||||||
|
|
||||||
|
public static castType(data: LongTextColumn): LongTextColumn { |
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
protected static async _insert( |
||||||
|
context: NcContext, |
||||||
|
longTextColumn: Partial<LongTextColumn> & { |
||||||
|
fk_model_id: string; |
||||||
|
fk_column_id: string; |
||||||
|
}, |
||||||
|
props: string[], |
||||||
|
ncMeta = Noco.ncMeta, |
||||||
|
) { |
||||||
|
const insertObj = extractProps(longTextColumn, [ |
||||||
|
'fk_workspace_id', |
||||||
|
'base_id', |
||||||
|
'fk_model_id', |
||||||
|
'fk_column_id', |
||||||
|
...(props || []), |
||||||
|
]); |
||||||
|
|
||||||
|
const column = await Column.get( |
||||||
|
context, |
||||||
|
{ |
||||||
|
colId: insertObj.fk_column_id, |
||||||
|
}, |
||||||
|
ncMeta, |
||||||
|
); |
||||||
|
|
||||||
|
if (!column) { |
||||||
|
NcError.fieldNotFound(insertObj.fk_column_id); |
||||||
|
} |
||||||
|
|
||||||
|
await ncMeta.metaInsert2( |
||||||
|
context.workspace_id, |
||||||
|
context.base_id, |
||||||
|
MetaTable.COL_LONG_TEXT, |
||||||
|
insertObj, |
||||||
|
); |
||||||
|
|
||||||
|
return this.read(context, longTextColumn.fk_column_id, ncMeta); |
||||||
|
} |
||||||
|
|
||||||
|
public static async read( |
||||||
|
context: NcContext, |
||||||
|
columnId: string, |
||||||
|
ncMeta = Noco.ncMeta, |
||||||
|
) { |
||||||
|
let column = |
||||||
|
columnId && |
||||||
|
(await NocoCache.get( |
||||||
|
`${CacheScope.COL_LONG_TEXT}:${columnId}`, |
||||||
|
CacheGetType.TYPE_OBJECT, |
||||||
|
)); |
||||||
|
if (!column) { |
||||||
|
column = await ncMeta.metaGet2( |
||||||
|
context.workspace_id, |
||||||
|
context.base_id, |
||||||
|
MetaTable.COL_LONG_TEXT, |
||||||
|
{ fk_column_id: columnId }, |
||||||
|
); |
||||||
|
await NocoCache.set(`${CacheScope.COL_LONG_TEXT}:${columnId}`, column); |
||||||
|
} |
||||||
|
|
||||||
|
return column ? this.castType(column) : null; |
||||||
|
} |
||||||
|
|
||||||
|
protected static async _update( |
||||||
|
context: NcContext, |
||||||
|
columnId: string, |
||||||
|
longTextColumn: Partial<LongTextColumn>, |
||||||
|
props: string[], |
||||||
|
ncMeta = Noco.ncMeta, |
||||||
|
) { |
||||||
|
const updateObj = extractProps(longTextColumn, [...(props || [])]); |
||||||
|
|
||||||
|
// set meta
|
||||||
|
await ncMeta.metaUpdate( |
||||||
|
context.workspace_id, |
||||||
|
context.base_id, |
||||||
|
MetaTable.COL_LONG_TEXT, |
||||||
|
updateObj, |
||||||
|
{ |
||||||
|
fk_column_id: columnId, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
await NocoCache.update( |
||||||
|
`${CacheScope.COL_LONG_TEXT}:${columnId}`, |
||||||
|
updateObj, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,150 @@ |
|||||||
|
import type { AIRecordType } from 'nocodb-sdk'; |
||||||
|
import type { BaseModelSqlv2 } from '~/db/BaseModelSqlv2'; |
||||||
|
import type KnexClient from '~/db/sql-client/lib/KnexClient'; |
||||||
|
import type { Column, Model, Source, User } from '~/models'; |
||||||
|
|
||||||
|
export const convertAIRecordTypeToValue = async (args: { |
||||||
|
source: Source; |
||||||
|
table: Model; |
||||||
|
column: Column; |
||||||
|
baseModel: BaseModelSqlv2; |
||||||
|
sqlClient: KnexClient; |
||||||
|
}) => { |
||||||
|
const { source, table, column, baseModel, sqlClient } = args; |
||||||
|
|
||||||
|
if (source.type === 'pg') { |
||||||
|
await sqlClient.raw( |
||||||
|
`UPDATE ??
|
||||||
|
SET ?? = TRIM('"' FROM (??::jsonb->>'value')) |
||||||
|
WHERE ?? ~ '^\\s*\\{.*\\}\\s*$' AND jsonb_typeof(??::jsonb) = 'object' AND (??::jsonb->'value') IS NOT NULL;`,
|
||||||
|
[ |
||||||
|
baseModel.getTnPath(table.table_name), |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
], |
||||||
|
); |
||||||
|
} else if (source.type === 'mysql' || source.type === 'mysql2') { |
||||||
|
await sqlClient.raw( |
||||||
|
`UPDATE ??
|
||||||
|
SET ?? = JSON_UNQUOTE(JSON_EXTRACT(??, '$.value')) |
||||||
|
WHERE JSON_VALID(??) AND JSON_TYPE(??) = 'OBJECT' AND JSON_EXTRACT(??, '$.value') IS NOT NULL;`,
|
||||||
|
[ |
||||||
|
baseModel.getTnPath(table.table_name), |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
], |
||||||
|
); |
||||||
|
} else if (source.type === 'sqlite3') { |
||||||
|
await sqlClient.raw( |
||||||
|
`UPDATE ??
|
||||||
|
SET ?? = json_extract(??, '$.value') |
||||||
|
WHERE json_valid(??) AND json_extract(??, '$.value') IS NOT NULL;`,
|
||||||
|
[ |
||||||
|
baseModel.getTnPath(table.table_name), |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
], |
||||||
|
); |
||||||
|
} else if (source.type === 'mssql') { |
||||||
|
await sqlClient.raw( |
||||||
|
`UPDATE ??
|
||||||
|
SET ?? = JSON_VALUE(??, '$.value') |
||||||
|
WHERE ISJSON(??) = 1 AND JSON_VALUE(??, '$.value') IS NOT NULL;`,
|
||||||
|
[ |
||||||
|
baseModel.getTnPath(table.table_name), |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
export const convertValueToAIRecordType = async (args: { |
||||||
|
source: Source; |
||||||
|
table: Model; |
||||||
|
column: Column; |
||||||
|
baseModel: BaseModelSqlv2; |
||||||
|
sqlClient: KnexClient; |
||||||
|
user: User; |
||||||
|
}) => { |
||||||
|
const { source, table, column, baseModel, sqlClient, user } = args; |
||||||
|
|
||||||
|
const commonRecord: Omit<AIRecordType, 'value'> = { |
||||||
|
lastModifiedBy: user.id, |
||||||
|
lastModifiedTime: baseModel.now(), |
||||||
|
isStale: true, |
||||||
|
}; |
||||||
|
|
||||||
|
// update every record with json which holds old value in value prop & commonRecord props in lastModifiedBy, lastModifiedTime, isStale
|
||||||
|
if (source.type === 'pg') { |
||||||
|
await sqlClient.raw( |
||||||
|
`UPDATE ??
|
||||||
|
SET ?? = jsonb_build_object('value', ??, 'lastModifiedBy', ?::text, 'lastModifiedTime', ?::text, 'isStale', ?::boolean) |
||||||
|
WHERE ?? is not null;`,
|
||||||
|
[ |
||||||
|
baseModel.getTnPath(table.table_name), |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
commonRecord.lastModifiedBy.toString(), |
||||||
|
commonRecord.lastModifiedTime.toString(), |
||||||
|
commonRecord.isStale, |
||||||
|
column.column_name, |
||||||
|
], |
||||||
|
); |
||||||
|
} else if (source.type === 'mysql' || source.type === 'mysql2') { |
||||||
|
await sqlClient.raw( |
||||||
|
`UPDATE ??
|
||||||
|
SET ?? = JSON_OBJECT('value', ??, 'lastModifiedBy', ?, 'lastModifiedTime', ?, 'isStale', ?) |
||||||
|
WHERE ?? is not null;`,
|
||||||
|
[ |
||||||
|
baseModel.getTnPath(table.table_name), |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
commonRecord.lastModifiedBy.toString(), |
||||||
|
commonRecord.lastModifiedTime.toString(), |
||||||
|
commonRecord.isStale, |
||||||
|
column.column_name, |
||||||
|
], |
||||||
|
); |
||||||
|
} else if (source.type === 'sqlite3') { |
||||||
|
await sqlClient.raw( |
||||||
|
`UPDATE ??
|
||||||
|
SET ?? = json_object('value', ??, 'lastModifiedBy', ?, 'lastModifiedTime', ?, 'isStale', ?) |
||||||
|
WHERE ?? is not null;`,
|
||||||
|
[ |
||||||
|
baseModel.getTnPath(table.table_name), |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
commonRecord.lastModifiedBy.toString(), |
||||||
|
commonRecord.lastModifiedTime.toString(), |
||||||
|
commonRecord.isStale, |
||||||
|
column.column_name, |
||||||
|
], |
||||||
|
); |
||||||
|
} else if (source.type === 'mssql') { |
||||||
|
await sqlClient.raw( |
||||||
|
`UPDATE ??
|
||||||
|
SET ?? = JSON_QUERY('{"value":' + ?? + ',"lastModifiedBy":' + ? + ',"lastModifiedTime":' + ? + ',"isStale":' + ? + '}') |
||||||
|
WHERE ?? is not null;`,
|
||||||
|
[ |
||||||
|
baseModel.getTnPath(table.table_name), |
||||||
|
column.column_name, |
||||||
|
column.column_name, |
||||||
|
commonRecord.lastModifiedBy.toString(), |
||||||
|
commonRecord.lastModifiedTime.toString(), |
||||||
|
commonRecord.isStale, |
||||||
|
column.column_name, |
||||||
|
], |
||||||
|
); |
||||||
|
} |
||||||
|
}; |
Loading…
Reference in new issue