chore: fix imports in db-postgres adapter

This commit is contained in:
Alessio Gravili
2023-09-19 11:15:47 +02:00
parent a81401cf77
commit 4f68b722dc
6 changed files with 192 additions and 194 deletions

View File

@@ -1,16 +1,10 @@
/* eslint-disable no-restricted-syntax, no-await-in-loop */
import fs from 'fs';
import { CreateMigration } from 'payload/dist/database/types';
import type { CreateMigration } from 'payload/database'
import { generateDrizzleJson, generateMigration } from 'drizzle-kit/utils';
import { eq } from 'drizzle-orm';
import { jsonb, numeric, pgEnum, pgTable, varchar } from 'drizzle-orm/pg-core';
import { SanitizedCollectionConfig } from 'payload/dist/collections/config/types';
import type { DatabaseAdapter, Init } from 'payload/dist/database/types';
import { configToJSONSchema } from 'payload/dist/utilities/configToJSONSchema';
import prompts from 'prompts';
import { buildTable } from './schema/build';
import type { GenericEnum, GenericRelation, GenericTable, PostgresAdapter } from './types';
import { generateDrizzleJson, generateMigration } from 'drizzle-kit/utils'
import fs from 'fs'
import type { PostgresAdapter } from './types'
const migrationTemplate = (upSQL?: string) => `
import payload, { Payload } from 'payload';
@@ -22,7 +16,7 @@ export async function up(payload: Payload): Promise<void> {
export async function down(payload: Payload): Promise<void> {
// Migration code
};
`;
`
export const createMigration: CreateMigration = async function createMigration(
this: PostgresAdapter,
@@ -30,27 +24,30 @@ export const createMigration: CreateMigration = async function createMigration(
migrationDir,
migrationName,
) {
payload.logger.info({ msg: 'Creating migration from postgres adapter...' });
const dir = migrationDir || '.migrations'; // TODO: Verify path after linking
payload.logger.info({ msg: 'Creating migration from postgres adapter...' })
const dir = migrationDir || '.migrations' // TODO: Verify path after linking
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
fs.mkdirSync(dir)
}
const [yyymmdd, hhmmss] = new Date().toISOString().split('T');
const formattedDate = yyymmdd.replace(/\D/g, '');
const formattedTime = hhmmss.split('.')[0].replace(/\D/g, '');
const [yyymmdd, hhmmss] = new Date().toISOString().split('T')
const formattedDate = yyymmdd.replace(/\D/g, '')
const formattedTime = hhmmss.split('.')[0].replace(/\D/g, '')
const timestamp = `${formattedDate}_${formattedTime}`;
const timestamp = `${formattedDate}_${formattedTime}`
const formattedName = migrationName.replace(/\W/g, '_');
const fileName = `${timestamp}_${formattedName}.ts`;
const filePath = `${dir}/${fileName}`;
const formattedName = migrationName.replace(/\W/g, '_')
const fileName = `${timestamp}_${formattedName}.ts`
const filePath = `${dir}/${fileName}`
const snapshotJSON = fs.readFileSync(`${dir}/drizzle-snapshot.json`, 'utf8');
const drizzleJsonBefore = generateDrizzleJson(JSON.parse(snapshotJSON));
const drizzleJsonAfter = generateDrizzleJson(this.schema, drizzleJsonBefore.id);
const sqlStatements = await generateMigration(drizzleJsonBefore, drizzleJsonAfter);
fs.writeFileSync(filePath, migrationTemplate(sqlStatements.length ? sqlStatements?.join('\n') : undefined));
const snapshotJSON = fs.readFileSync(`${dir}/drizzle-snapshot.json`, 'utf8')
const drizzleJsonBefore = generateDrizzleJson(JSON.parse(snapshotJSON))
const drizzleJsonAfter = generateDrizzleJson(this.schema, drizzleJsonBefore.id)
const sqlStatements = await generateMigration(drizzleJsonBefore, drizzleJsonAfter)
fs.writeFileSync(
filePath,
migrationTemplate(sqlStatements.length ? sqlStatements?.join('\n') : undefined),
)
// TODO:
// Get the most recent migration schema from the file system
@@ -61,4 +58,4 @@ export const createMigration: CreateMigration = async function createMigration(
// and then inject them each into the `migrationTemplate` above,
// outputting the file into the migrations folder accordingly
// also make sure to output the JSON schema snapshot into a `./migrationsDir/meta` folder like Drizzle does
};
}

View File

@@ -1,25 +1,18 @@
import type { CreateVersion } from 'payload/database';
import type { CreateVersion } from 'payload/database'
import { buildVersionCollectionFields } from 'payload/versions';
import toSnakeCase from 'to-snake-case';
import { buildVersionCollectionFields } from 'payload/versions'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types';
import type { PostgresAdapter } from './types'
import { upsertRow } from './upsertRow';
import { upsertRow } from './upsertRow'
export const createVersion: CreateVersion = async function createVersion(
this: PostgresAdapter,
{
autosave,
collectionSlug,
createdAt,
parent,
updatedAt,
versionData,
},
{ autosave, collectionSlug, createdAt, parent, updatedAt, versionData },
) {
const collection = this.payload.collections[collectionSlug].config;
const tableName = toSnakeCase(collectionSlug);
const collection = this.payload.collections[collectionSlug].config
const tableName = toSnakeCase(collectionSlug)
const result = await upsertRow({
adapter: this,
@@ -31,10 +24,11 @@ export const createVersion: CreateVersion = async function createVersion(
updatedAt,
version: versionData,
},
db: this.db,
fields: buildVersionCollectionFields(collection),
operation: 'create',
tableName: `_${tableName}_versions`,
});
})
// const [doc] = await VersionModel.create(
// [
@@ -71,5 +65,5 @@ export const createVersion: CreateVersion = async function createVersion(
// ],
// }, { $unset: { latest: 1 } });
return result;
};
return result
}

View File

@@ -1,23 +1,23 @@
/* eslint-disable no-param-reassign */
import type { ArrayField, Block, Field } from 'payload/types';
import type { ArrayField, Block, Field } from 'payload/types'
import { fieldAffectsData } from 'payload/types';
import toSnakeCase from 'to-snake-case';
import { fieldAffectsData } from 'payload/types'
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from '../tyfpes';
import type { Result } from './buildFindManyArgs';
import type { PostgresAdapter } from '../types'
import type { Result } from './buildFindManyArgs'
type TraverseFieldArgs = {
_locales: Record<string, unknown>
adapter: PostgresAdapter
currentArgs: Record<string, unknown>,
currentArgs: Record<string, unknown>
currentTableName: string
depth?: number,
depth?: number
fields: Field[]
locatedArrays: { [path: string]: ArrayField },
locatedBlocks: Block[],
path: string,
topLevelArgs: Record<string, unknown>,
locatedArrays: { [path: string]: ArrayField }
locatedBlocks: Block[]
path: string
topLevelArgs: Record<string, unknown>
topLevelTableName: string
}
@@ -45,12 +45,12 @@ export const traverseFields = ({
},
orderBy: ({ _order }, { asc }) => [asc(_order)],
with: {},
};
}
const arrayTableName = `${currentTableName}_${toSnakeCase(field.name)}`;
const arrayTableName = `${currentTableName}_${toSnakeCase(field.name)}`
if (adapter.tables[`${arrayTableName}_locales`]) withArray.with._locales = _locales;
currentArgs.with[`${path}${field.name}`] = withArray;
if (adapter.tables[`${arrayTableName}_locales`]) withArray.with._locales = _locales
currentArgs.with[`${path}${field.name}`] = withArray
traverseFields({
_locales,
@@ -64,14 +64,14 @@ export const traverseFields = ({
path: '',
topLevelArgs,
topLevelTableName,
});
})
break;
break
}
case 'blocks':
field.blocks.forEach((block) => {
const blockKey = `_blocks_${block.slug}`;
const blockKey = `_blocks_${block.slug}`
if (!topLevelArgs[blockKey]) {
const withBlock: Result = {
@@ -80,10 +80,11 @@ export const traverseFields = ({
},
orderBy: ({ _order }, { asc }) => [asc(_order)],
with: {},
};
}
if (adapter.tables[`${topLevelTableName}_${toSnakeCase(block.slug)}_locales`]) withBlock.with._locales = _locales;
topLevelArgs.with[blockKey] = withBlock;
if (adapter.tables[`${topLevelTableName}_${toSnakeCase(block.slug)}_locales`])
withBlock.with._locales = _locales
topLevelArgs.with[blockKey] = withBlock
traverseFields({
_locales,
@@ -97,11 +98,11 @@ export const traverseFields = ({
path,
topLevelArgs,
topLevelTableName,
});
})
}
});
})
break;
break
case 'group':
traverseFields({
@@ -116,16 +117,16 @@ export const traverseFields = ({
path: `${path}${field.name}_`,
topLevelArgs,
topLevelTableName,
});
})
break;
break
default: {
break;
break
}
}
}
});
})
return topLevelArgs;
};
return topLevelArgs
}

View File

@@ -1,20 +1,20 @@
import type { FindGlobal } from 'payload/database';
import type { FindGlobal } from 'payload/database'
import toSnakeCase from 'to-snake-case';
import toSnakeCase from 'to-snake-case'
import type { PostgresAdapter } from './types';
import type { PostgresAdapter } from './types'
import { buildFindManyArgs } from './find/buildFindManyArgs';
import buildQuery from './queries/buildQuery';
import { transform } from './transform/read';
import { buildFindManyArgs } from './find/buildFindManyArgs'
import buildQuery from './queries/buildQuery'
import { transform } from './transform/read'
export const findGlobal: FindGlobal = async function findGlobal(
this: PostgresAdapter,
{ locale, req, slug, where },
) {
const db = req.transactionID ? this.sessions[req.transactionID] : this.db;
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug);
const tableName = toSnakeCase(slug);
const db = req.transactionID ? this.sessions[req.transactionID] : this.db
const globalConfig = this.payload.globals.config.find((config) => config.slug === slug)
const tableName = toSnakeCase(slug)
const query = await buildQuery({
adapter: this,
@@ -22,28 +22,28 @@ export const findGlobal: FindGlobal = async function findGlobal(
locale,
tableName,
where,
});
})
const findManyArgs = buildFindManyArgs({
adapter: this,
depth: 0,
fields: globalConfig.fields,
tableName,
});
})
findManyArgs.where = query;
findManyArgs.where = query
const doc = await db.query[tableName].findFirst(findManyArgs);
const doc = await db.query[tableName].findFirst(findManyArgs)
if (doc) {
const result = transform({
config: this.payload.config,
data: doc,
fields: globalConfig.fields,
});
})
return result;
return result
}
return null;
};
return null
}

View File

@@ -4,40 +4,40 @@
// type PushDiff = (schema: DrizzleSchemaExports) => Promise<{ warnings: string[], apply: () => Promise<void> }>
// drizzle-kit@utils
import { generateDrizzleJson, generateMigration, pushSchema } from 'drizzle-kit/utils';
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import { generateDrizzleJson, generateMigration, pushSchema } from 'drizzle-kit/utils'
import { drizzle } from 'drizzle-orm/node-postgres'
import { Pool } from 'pg'
async function generateUsage() {
const schema = await import('./data/users');
const schemaAfter = await import('./data/users-after');
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
const schema = await import('./data/users')
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
const schemaAfter = await import('./data/users-after')
const drizzleJsonBefore = generateDrizzleJson(schema);
const drizzleJsonAfter = generateDrizzleJson(schemaAfter);
const drizzleJsonBefore = generateDrizzleJson(schema)
const drizzleJsonAfter = generateDrizzleJson(schemaAfter)
const sqlStatements = await generateMigration(drizzleJsonBefore, drizzleJsonAfter);
const sqlStatements = await generateMigration(drizzleJsonBefore, drizzleJsonAfter)
console.log(sqlStatements);
console.log(sqlStatements)
}
async function pushUsage() {
const schemaAfter = await import('./data/users-after');
// @ts-expect-error Just TypeScript being broken // TODO: Open TypeScript issue
const schemaAfter = await import('./data/users-after')
const db = drizzle(
new Pool({ connectionString: '' }),
);
const db = drizzle(new Pool({ connectionString: '' }))
const response = await pushSchema(schemaAfter, db);
const response = await pushSchema(schemaAfter, db)
console.log('\n');
console.log('hasDataLoss: ', response.hasDataLoss);
console.log('warnings: ', response.warnings);
console.log('statements: ', response.statementsToExecute);
console.log('\n')
console.log('hasDataLoss: ', response.hasDataLoss)
console.log('warnings: ', response.warnings)
console.log('statements: ', response.statementsToExecute)
await response.apply();
await response.apply()
process.exit(0);
process.exit(0)
}

View File

@@ -1,22 +1,22 @@
/* eslint-disable no-param-reassign */
import { eq } from 'drizzle-orm';
import { eq } from 'drizzle-orm'
import type { BlockRowToInsert } from '../transform/write/types';
import type { Args } from './types';
import type { BlockRowToInsert } from '../transform/write/types'
import type { Args } from './types'
import { buildFindManyArgs } from '../find/buildFindManyArgs';
import { transform } from '../transform/read';
import { transformForWrite } from '../transform/write';
import { deleteExistingArrayRows } from './deleteExistingArrayRows';
import { deleteExistingRowsByPath } from './deleteExistingRowsByPath';
import { insertArrays } from './insertArrays';
import { buildFindManyArgs } from '../find/buildFindManyArgs'
import { transform } from '../transform/read'
import { transformForWrite } from '../transform/write'
import { deleteExistingArrayRows } from './deleteExistingArrayRows'
import { deleteExistingRowsByPath } from './deleteExistingRowsByPath'
import { insertArrays } from './insertArrays'
export const upsertRow = async ({
id,
adapter,
data,
db,
fields,
id,
operation,
path = '',
tableName,
@@ -30,80 +30,81 @@ export const upsertRow = async ({
fields,
path,
tableName,
});
})
// First, we insert the main row
let insertedRow: Record<string, unknown>;
let insertedRow: Record<string, unknown>
if (operation === 'update') {
const target = upsertTarget || adapter.tables[tableName].id;
const target = upsertTarget || adapter.tables[tableName].id
if (id) {
rowToInsert.row.id = id;
[insertedRow] = await db.insert(adapter.tables[tableName])
rowToInsert.row.id = id
;[insertedRow] = await db
.insert(adapter.tables[tableName])
.values(rowToInsert.row)
.onConflictDoUpdate({ set: rowToInsert.row, target })
.returning();
.returning()
} else {
[insertedRow] = await db.insert(adapter.tables[tableName])
;[insertedRow] = await db
.insert(adapter.tables[tableName])
.values(rowToInsert.row)
.onConflictDoUpdate({ set: rowToInsert.row, target, where })
.returning();
.returning()
}
} else {
[insertedRow] = await db.insert(adapter.tables[tableName])
.values(rowToInsert.row).returning();
;[insertedRow] = await db.insert(adapter.tables[tableName]).values(rowToInsert.row).returning()
}
const localesToInsert: Record<string, unknown>[] = [];
const relationsToInsert: Record<string, unknown>[] = [];
const blocksToInsert: { [blockType: string]: BlockRowToInsert[] } = {};
const localesToInsert: Record<string, unknown>[] = []
const relationsToInsert: Record<string, unknown>[] = []
const blocksToInsert: { [blockType: string]: BlockRowToInsert[] } = {}
// Maintain a list of promises to run locale, blocks, and relationships
// all in parallel
const promises = [];
const promises = []
// If there are locale rows with data, add the parent and locale to each
if (Object.keys(rowToInsert.locales).length > 0) {
Object.entries(rowToInsert.locales).forEach(([locale, localeRow]) => {
localeRow._parentID = insertedRow.id;
localeRow._locale = locale;
localesToInsert.push(localeRow);
});
localeRow._parentID = insertedRow.id
localeRow._locale = locale
localesToInsert.push(localeRow)
})
}
// If there are relationships, add parent to each
if (rowToInsert.relationships.length > 0) {
rowToInsert.relationships.forEach((relation) => {
relation.parent = insertedRow.id;
relationsToInsert.push(relation);
});
relation.parent = insertedRow.id
relationsToInsert.push(relation)
})
}
// If there are blocks, add parent to each, and then
// store by table name and rows
Object.keys(rowToInsert.blocks).forEach((blockName) => {
rowToInsert.blocks[blockName].forEach((blockRow) => {
blockRow.row._parentID = insertedRow.id;
if (!blocksToInsert[blockName]) blocksToInsert[blockName] = [];
blocksToInsert[blockName].push(blockRow);
});
});
blockRow.row._parentID = insertedRow.id
if (!blocksToInsert[blockName]) blocksToInsert[blockName] = []
blocksToInsert[blockName].push(blockRow)
})
})
// //////////////////////////////////
// INSERT LOCALES
// //////////////////////////////////
if (localesToInsert.length > 0) {
const localeTable = adapter.tables[`${tableName}_locales`];
const localeTable = adapter.tables[`${tableName}_locales`]
promises.push(async () => {
if (operation === 'update') {
await db.delete(localeTable).where(eq(localeTable._parentID, insertedRow.id));
await db.delete(localeTable).where(eq(localeTable._parentID, insertedRow.id))
}
await db.insert(localeTable).values(localesToInsert);
});
await db.insert(localeTable).values(localesToInsert)
})
}
// //////////////////////////////////
@@ -112,7 +113,7 @@ export const upsertRow = async ({
if (relationsToInsert.length > 0) {
promises.push(async () => {
const relationshipsTableName = `${tableName}_relationships`;
const relationshipsTableName = `${tableName}_relationships`
if (operation === 'update') {
await deleteExistingRowsByPath({
adapter,
@@ -122,19 +123,18 @@ export const upsertRow = async ({
parentID: insertedRow.id,
pathColumnName: 'path',
tableName: relationshipsTableName,
});
})
}
await db.insert(adapter.tables[relationshipsTableName])
.values(relationsToInsert).returning();
});
await db.insert(adapter.tables[relationshipsTableName]).values(relationsToInsert).returning()
})
}
// //////////////////////////////////
// INSERT BLOCKS
// //////////////////////////////////
const insertedBlockRows: Record<string, Record<string, unknown>[]> = {};
const insertedBlockRows: Record<string, Record<string, unknown>[]> = {}
Object.entries(blocksToInsert).forEach(([blockName, blockRows]) => {
// For each block, push insert into promises to run parallel
@@ -146,45 +146,49 @@ export const upsertRow = async ({
parentID: insertedRow.id,
pathColumnName: '_path',
tableName: `${tableName}_${blockName}`,
});
})
}
insertedBlockRows[blockName] = await db.insert(adapter.tables[`${tableName}_${blockName}`])
.values(blockRows.map(({ row }) => row)).returning();
insertedBlockRows[blockName] = await db
.insert(adapter.tables[`${tableName}_${blockName}`])
.values(blockRows.map(({ row }) => row))
.returning()
insertedBlockRows[blockName].forEach((row, i) => {
blockRows[i].row = row;
});
blockRows[i].row = row
})
const blockLocaleIndexMap: number[] = [];
const blockLocaleIndexMap: number[] = []
const blockLocaleRowsToInsert = blockRows.reduce((acc, blockRow, i) => {
if (Object.entries(blockRow.locales).length > 0) {
Object.entries(blockRow.locales).forEach(([blockLocale, blockLocaleData]) => {
if (Object.keys(blockLocaleData).length > 0) {
blockLocaleData._parentID = blockRow.row.id;
blockLocaleData._locale = blockLocale;
acc.push(blockLocaleData);
blockLocaleIndexMap.push(i);
blockLocaleData._parentID = blockRow.row.id
blockLocaleData._locale = blockLocale
acc.push(blockLocaleData)
blockLocaleIndexMap.push(i)
}
});
})
}
return acc;
}, []);
return acc
}, [])
if (blockLocaleRowsToInsert.length > 0) {
await db.insert(adapter.tables[`${tableName}_${blockName}_locales`])
.values(blockLocaleRowsToInsert).returning();
await db
.insert(adapter.tables[`${tableName}_${blockName}_locales`])
.values(blockLocaleRowsToInsert)
.returning()
}
await insertArrays({
adapter,
arrays: blockRows.map(({ arrays }) => arrays),
parentRows: insertedBlockRows[blockName],
});
});
});
})
})
})
// //////////////////////////////////
// INSERT ARRAYS RECURSIVELY
@@ -192,23 +196,25 @@ export const upsertRow = async ({
promises.push(async () => {
if (operation === 'update') {
await Promise.all(Object.entries(rowToInsert.arrays).map(async ([arrayTableName, tableRows]) => {
await deleteExistingArrayRows({
adapter,
parentID: insertedRow.id,
tableName: arrayTableName,
});
}));
await Promise.all(
Object.entries(rowToInsert.arrays).map(async ([arrayTableName, tableRows]) => {
await deleteExistingArrayRows({
adapter,
parentID: insertedRow.id,
tableName: arrayTableName,
})
}),
)
}
await insertArrays({
adapter,
arrays: [rowToInsert.arrays],
parentRows: [insertedRow],
});
});
})
})
await Promise.all(promises.map((promise) => promise()));
await Promise.all(promises.map((promise) => promise()))
// //////////////////////////////////
// RETRIEVE NEWLY UPDATED ROW
@@ -219,11 +225,11 @@ export const upsertRow = async ({
depth: 0,
fields,
tableName,
});
})
findManyArgs.where = eq(adapter.tables[tableName].id, insertedRow.id);
findManyArgs.where = eq(adapter.tables[tableName].id, insertedRow.id)
const doc = await db.query[tableName].findFirst(findManyArgs);
const doc = await db.query[tableName].findFirst(findManyArgs)
// //////////////////////////////////
// TRANSFORM DATA
@@ -233,7 +239,7 @@ export const upsertRow = async ({
config: adapter.payload.config,
data: doc,
fields,
});
})
return result;
};
return result
}