mirror of
https://github.com/hcengineering/platform.git
synced 2026-09-12 04:37:44 +02:00
Workspace Versioning
Closes #857 Signed-off-by: Andrey Sobolev <haiodo@gmail.com>
This commit is contained in:
+13
-38
@@ -14,7 +14,7 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import accountPlugin, {
|
||||
import {
|
||||
ACCOUNT_DB,
|
||||
assignWorkspace,
|
||||
createAccount,
|
||||
@@ -22,23 +22,20 @@ import accountPlugin, {
|
||||
dropAccount,
|
||||
dropWorkspace,
|
||||
getAccount,
|
||||
listAccounts,
|
||||
listWorkspaces,
|
||||
listAccounts
|
||||
upgradeWorkspace
|
||||
} from '@anticrm/account'
|
||||
import { setMetadata } from '@anticrm/platform'
|
||||
import toolPlugin, { prepareTools, version } from '@anticrm/server-tool'
|
||||
import { program } from 'commander'
|
||||
import { Client } from 'minio'
|
||||
import { Db, MongoClient } from 'mongodb'
|
||||
import { rebuildElastic } from './elastic'
|
||||
import { importXml } from './importer'
|
||||
import { clearTelegramHistory } from './telegram'
|
||||
import { diffWorkspace, dumpWorkspace, restoreWorkspace, upgradeWorkspace } from './workspace'
|
||||
import { diffWorkspace, dumpWorkspace, restoreWorkspace } from './workspace'
|
||||
|
||||
const mongodbUri = process.env.MONGO_URL
|
||||
if (mongodbUri === undefined) {
|
||||
console.error('please provide mongodb url.')
|
||||
process.exit(1)
|
||||
}
|
||||
const { mongodbUri, minio } = prepareTools()
|
||||
|
||||
const transactorUrl = process.env.TRANSACTOR_URL
|
||||
if (transactorUrl === undefined) {
|
||||
@@ -46,40 +43,14 @@ if (transactorUrl === undefined) {
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const minioEndpoint = process.env.MINIO_ENDPOINT
|
||||
if (minioEndpoint === undefined) {
|
||||
console.error('please provide minio endpoint')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const minioAccessKey = process.env.MINIO_ACCESS_KEY
|
||||
if (minioAccessKey === undefined) {
|
||||
console.error('please provide minio access key')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const minioSecretKey = process.env.MINIO_SECRET_KEY
|
||||
if (minioSecretKey === undefined) {
|
||||
console.error('please provide minio secret key')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const elasticUrl = process.env.ELASTIC_URL
|
||||
if (elasticUrl === undefined) {
|
||||
console.error('please provide elastic url')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
setMetadata(accountPlugin.metadata.Endpoint, transactorUrl)
|
||||
setMetadata(accountPlugin.metadata.Transactor, transactorUrl)
|
||||
|
||||
const minio = new Client({
|
||||
endPoint: minioEndpoint,
|
||||
port: 9000,
|
||||
useSSL: false,
|
||||
accessKey: minioAccessKey,
|
||||
secretKey: minioSecretKey
|
||||
})
|
||||
setMetadata(toolPlugin.metadata.Endpoint, transactorUrl)
|
||||
setMetadata(toolPlugin.metadata.Transactor, transactorUrl)
|
||||
|
||||
async function withDatabase (uri: string, f: (db: Db, client: MongoClient) => Promise<any>): Promise<void> {
|
||||
console.log(`connecting to database '${uri}'...`)
|
||||
@@ -139,7 +110,9 @@ program
|
||||
.command('upgrade-workspace <name>')
|
||||
.description('upgrade workspace')
|
||||
.action(async (workspace, cmd) => {
|
||||
await upgradeWorkspace(mongodbUri, workspace, transactorUrl, minio)
|
||||
return await withDatabase(mongodbUri, async (db) => {
|
||||
await upgradeWorkspace(db, workspace)
|
||||
})
|
||||
})
|
||||
|
||||
program
|
||||
@@ -158,6 +131,8 @@ program
|
||||
return await withDatabase(mongodbUri, async (db) => {
|
||||
const workspacesJSON = JSON.stringify(await listWorkspaces(db), null, 2)
|
||||
console.info(workspacesJSON)
|
||||
|
||||
console.log('latest model version:', JSON.stringify(version))
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
import {
|
||||
Doc,
|
||||
DocumentQuery,
|
||||
Domain,
|
||||
FindOptions,
|
||||
isOperator, Ref, SortingOrder
|
||||
} from '@anticrm/core'
|
||||
import { MigrationClient, MigrateUpdate, MigrationResult } from '@anticrm/model'
|
||||
import { Db, Document, Filter, Sort, UpdateFilter } from 'mongodb'
|
||||
|
||||
/**
|
||||
* Upgrade client implementation.
|
||||
*/
|
||||
export class MigrateClientImpl implements MigrationClient {
|
||||
constructor (readonly db: Db) {
|
||||
}
|
||||
|
||||
private translateQuery<T extends Doc>(query: DocumentQuery<T>): Filter<Document> {
|
||||
const translated: any = {}
|
||||
for (const key in query) {
|
||||
const value = (query as any)[key]
|
||||
if (value !== null && typeof value === 'object') {
|
||||
const keys = Object.keys(value)
|
||||
if (keys[0] === '$like') {
|
||||
const pattern = value.$like as string
|
||||
translated[key] = {
|
||||
$regex: `^${pattern.split('%').join('.*')}$`,
|
||||
$options: 'i'
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
translated[key] = value
|
||||
}
|
||||
return translated
|
||||
}
|
||||
|
||||
async find<T extends Doc>(
|
||||
domain: Domain,
|
||||
query: DocumentQuery<T>,
|
||||
options?: FindOptions<T> | undefined
|
||||
): Promise<T[]> {
|
||||
let cursor = this.db.collection(domain).find<T>(this.translateQuery(query))
|
||||
if (options?.limit !== undefined) {
|
||||
cursor = cursor.limit(options.limit)
|
||||
}
|
||||
if (options !== null && options !== undefined) {
|
||||
if (options.sort !== undefined) {
|
||||
const sort: Sort = {}
|
||||
for (const key in options.sort) {
|
||||
const order = options.sort[key] === SortingOrder.Ascending ? 1 : -1
|
||||
sort[key] = order
|
||||
}
|
||||
cursor = cursor.sort(sort)
|
||||
}
|
||||
}
|
||||
return await cursor.toArray()
|
||||
}
|
||||
|
||||
async update<T extends Doc>(domain: Domain, query: DocumentQuery<T>, operations: MigrateUpdate<T>): Promise<MigrationResult> {
|
||||
if (isOperator(operations)) {
|
||||
const result = await this.db
|
||||
.collection(domain)
|
||||
.updateMany(this.translateQuery(query), { ...operations } as unknown as UpdateFilter<any>)
|
||||
|
||||
return { matched: result.matchedCount, updated: result.modifiedCount }
|
||||
} else {
|
||||
const result = await this.db.collection(domain).updateMany(this.translateQuery(query), { $set: operations })
|
||||
return { matched: result.matchedCount, updated: result.modifiedCount }
|
||||
}
|
||||
}
|
||||
|
||||
async move <T extends Doc>(sourceDomain: Domain, query: DocumentQuery<T>, targetDomain: Domain): Promise<MigrationResult> {
|
||||
const q = this.translateQuery(query)
|
||||
const cursor = this.db.collection(sourceDomain).find<T>(q)
|
||||
const target = this.db.collection(targetDomain)
|
||||
const result: MigrationResult = {
|
||||
matched: 0,
|
||||
updated: 0
|
||||
}
|
||||
let doc: Document | null
|
||||
while ((doc = await cursor.next()) != null) {
|
||||
await target.insertOne(doc)
|
||||
result.matched++
|
||||
result.updated++
|
||||
}
|
||||
await this.db.collection(sourceDomain).deleteMany(q)
|
||||
return result
|
||||
}
|
||||
|
||||
async create <T extends Doc>(domain: Domain, doc: T): Promise<void> {
|
||||
await this.db.collection(domain).insertOne(doc as Document)
|
||||
}
|
||||
|
||||
async delete <T extends Doc>(domain: Domain, _id: Ref<T>): Promise<void> {
|
||||
await this.db.collection(domain).deleteOne({ _id })
|
||||
}
|
||||
}
|
||||
@@ -16,70 +16,16 @@
|
||||
|
||||
import contact from '@anticrm/contact'
|
||||
import core, { DOMAIN_TX, Tx } from '@anticrm/core'
|
||||
import builder, { migrateOperations } from '@anticrm/model-all'
|
||||
import builder, { version } from '@anticrm/model-all'
|
||||
import { upgradeModel } from '@anticrm/server-tool'
|
||||
import { existsSync } from 'fs'
|
||||
import { mkdir, open, readFile, writeFile } from 'fs/promises'
|
||||
import { Client } from 'minio'
|
||||
import { Document, MongoClient } from 'mongodb'
|
||||
import { join } from 'path'
|
||||
import { connect } from './connect'
|
||||
import { MigrateClientImpl } from './upgrade'
|
||||
import { rebuildElastic } from './elastic'
|
||||
import { generateModelDiff, printDiff } from './mdiff'
|
||||
import { listMinioObjects, MinioWorkspaceItem } from './minio'
|
||||
import { rebuildElastic } from './elastic'
|
||||
|
||||
const txes = JSON.parse(JSON.stringify(builder.getTxes())) as Tx[]
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function upgradeWorkspace (
|
||||
mongoUrl: string,
|
||||
dbName: string,
|
||||
transactorUrl: string,
|
||||
minio: Client
|
||||
): Promise<void> {
|
||||
if (txes.some((tx) => tx.objectSpace !== core.space.Model)) {
|
||||
throw Error('Model txes must target only core.space.Model')
|
||||
}
|
||||
|
||||
const client = new MongoClient(mongoUrl)
|
||||
try {
|
||||
await client.connect()
|
||||
const db = client.db(dbName)
|
||||
|
||||
console.log('removing model...')
|
||||
// we're preserving accounts (created by core.account.System).
|
||||
const result = await db.collection(DOMAIN_TX).deleteMany({
|
||||
objectSpace: core.space.Model,
|
||||
modifiedBy: core.account.System,
|
||||
objectClass: { $ne: contact.class.EmployeeAccount }
|
||||
})
|
||||
console.log(`${result.deletedCount} transactions deleted.`)
|
||||
|
||||
console.log('creating model...')
|
||||
const model = txes
|
||||
const insert = await db.collection(DOMAIN_TX).insertMany(model as Document[])
|
||||
console.log(`${insert.insertedCount} model transactions inserted.`)
|
||||
|
||||
const migrateClient = new MigrateClientImpl(db)
|
||||
for (const op of migrateOperations) {
|
||||
await op.migrate(migrateClient)
|
||||
}
|
||||
|
||||
console.log('Apply upgrade operations')
|
||||
|
||||
const connection = await connect(transactorUrl, dbName)
|
||||
for (const op of migrateOperations) {
|
||||
await op.upgrade(connection)
|
||||
}
|
||||
|
||||
await connection.close()
|
||||
} finally {
|
||||
await client.close()
|
||||
}
|
||||
}
|
||||
|
||||
interface CollectionInfo {
|
||||
name: string
|
||||
file: string
|
||||
@@ -107,7 +53,7 @@ export async function dumpWorkspace (mongoUrl: string, dbName: string, fileName:
|
||||
}
|
||||
|
||||
const workspaceInfo: WorkspaceInfo = {
|
||||
version: '0.6.0',
|
||||
version: `${version.major}.${version.minor}.${version.patch}`,
|
||||
collections: [],
|
||||
minioData: []
|
||||
}
|
||||
@@ -215,7 +161,7 @@ export async function restoreWorkspace (
|
||||
}
|
||||
}
|
||||
|
||||
await upgradeWorkspace(mongoUrl, dbName, transactorUrl, minio)
|
||||
await upgradeModel(dbName, transactorUrl)
|
||||
|
||||
await rebuildElastic(mongoUrl, dbName, minio, elasticUrl)
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user