mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-12 19:30:52 +00:00
Improve calendar schema (#7156)
Signed-off-by: Denis Bykhov <bykhov.denis@gmail.com>
This commit is contained in:
parent
e0ca033405
commit
76bcd3c52c
@ -68,6 +68,7 @@ export { calendarId } from '@hcengineering/calendar'
|
||||
export { calendarOperation } from './migration'
|
||||
|
||||
export const DOMAIN_CALENDAR = 'calendar' as Domain
|
||||
export const DOMAIN_EVENT = 'event' as Domain
|
||||
|
||||
@Model(calendar.class.Calendar, core.class.Doc, DOMAIN_CALENDAR)
|
||||
@UX(calendar.string.Calendar, calendar.icon.Calendar)
|
||||
@ -85,7 +86,7 @@ export class TExternalCalendar extends TCalendar implements ExternalCalendar {
|
||||
externalUser!: string
|
||||
}
|
||||
|
||||
@Model(calendar.class.Event, core.class.AttachedDoc, DOMAIN_CALENDAR)
|
||||
@Model(calendar.class.Event, core.class.AttachedDoc, DOMAIN_EVENT)
|
||||
@UX(calendar.string.Event, calendar.icon.Calendar)
|
||||
export class TEvent extends TAttachedDoc implements Event {
|
||||
declare space: Ref<SystemSpace>
|
||||
|
@ -24,7 +24,7 @@ import {
|
||||
type MigrationUpgradeClient
|
||||
} from '@hcengineering/model'
|
||||
import { DOMAIN_SPACE } from '@hcengineering/model-core'
|
||||
import { DOMAIN_CALENDAR } from '.'
|
||||
import { DOMAIN_CALENDAR, DOMAIN_EVENT } from '.'
|
||||
import calendar from './plugin'
|
||||
|
||||
async function migrateCalendars (client: MigrationClient): Promise<void> {
|
||||
@ -136,6 +136,16 @@ export const calendarOperation: MigrateOperation = {
|
||||
{
|
||||
state: 'migrate_calendars',
|
||||
func: migrateCalendars
|
||||
},
|
||||
{
|
||||
state: 'move-events',
|
||||
func: async (client) => {
|
||||
await client.move(
|
||||
DOMAIN_CALENDAR,
|
||||
{ _class: { $in: client.hierarchy.getDescendants(calendar.class.Event) } },
|
||||
DOMAIN_EVENT
|
||||
)
|
||||
}
|
||||
}
|
||||
])
|
||||
},
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import calendar from '@hcengineering/calendar'
|
||||
import calendar, { Calendar } from '@hcengineering/calendar'
|
||||
import type { Contact, PersonAccount, Organization, Person } from '@hcengineering/contact'
|
||||
import contact from '@hcengineering/contact'
|
||||
import core, {
|
||||
@ -41,7 +41,6 @@
|
||||
import recruit from '../../plugin'
|
||||
import IconCompany from '../icons/Company.svelte'
|
||||
import { Analytics } from '@hcengineering/analytics'
|
||||
import { getCandidateIdentifier } from '../../utils'
|
||||
|
||||
// export let space: Ref<Project>
|
||||
export let candidate: Ref<Person>
|
||||
@ -86,7 +85,8 @@
|
||||
title,
|
||||
participants: [currentUser.person],
|
||||
eventId: '',
|
||||
dueDate: 0
|
||||
dueDate: 0,
|
||||
calendar: '' as Ref<Calendar>
|
||||
}
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
@ -139,7 +139,7 @@
|
||||
access: 'reader',
|
||||
allDay: false,
|
||||
eventId: '',
|
||||
calendar: undefined
|
||||
calendar: '' as Ref<Calendar>
|
||||
}
|
||||
)
|
||||
|
||||
|
5
server/postgres/migrations/calendarSchema.sql
Normal file
5
server/postgres/migrations/calendarSchema.sql
Normal file
@ -0,0 +1,5 @@
|
||||
ALTER TABLE calendar
|
||||
ADD "hidden" bool;
|
||||
|
||||
UPDATE calendar
|
||||
SET "hidden" = (data->>'hidden')::boolean;
|
19
server/postgres/migrations/eventSchema.sql
Normal file
19
server/postgres/migrations/eventSchema.sql
Normal file
@ -0,0 +1,19 @@
|
||||
ALTER TABLE event
|
||||
ADD "date" bigint,
|
||||
ADD "dueDate" bigint,
|
||||
add calendar text,
|
||||
ADD "participants" text[];
|
||||
|
||||
UPDATE event
|
||||
SET "date" = (data->>'date')::bigint;
|
||||
|
||||
UPDATE event
|
||||
SET "dueDate" = (data->>'dueDate')::bigint;
|
||||
|
||||
UPDATE calendar
|
||||
SET "calendar" = (data->>'calendar');
|
||||
|
||||
UPDATE event
|
||||
SET "participants" = array(
|
||||
SELECT jsonb_array_elements_text(data->'participants')
|
||||
);
|
17
server/postgres/migrations/timeSchema.sql
Normal file
17
server/postgres/migrations/timeSchema.sql
Normal file
@ -0,0 +1,17 @@
|
||||
ALTER TABLE time
|
||||
ADD "workslots" bigint,
|
||||
ADD "doneOn" bigint,
|
||||
add rank text,
|
||||
ADD "user" text;
|
||||
|
||||
UPDATE time
|
||||
SET "workslots" = (data->>'workslots')::bigint;
|
||||
|
||||
UPDATE time
|
||||
SET "doneOn" = (data->>'doneOn')::bigint;
|
||||
|
||||
UPDATE time
|
||||
SET "rank" = (data->>'rank');
|
||||
|
||||
UPDATE time
|
||||
SET "user" = (data->>'user');
|
@ -155,6 +155,63 @@ const docIndexStateSchema: Schema = {
|
||||
}
|
||||
}
|
||||
|
||||
const timeSchema: Schema = {
|
||||
...baseSchema,
|
||||
workslots: {
|
||||
type: 'bigint',
|
||||
notNull: false,
|
||||
index: true
|
||||
},
|
||||
doneOn: {
|
||||
type: 'bigint',
|
||||
notNull: false,
|
||||
index: true
|
||||
},
|
||||
user: {
|
||||
type: 'text',
|
||||
notNull: true,
|
||||
index: true
|
||||
},
|
||||
rank: {
|
||||
type: 'text',
|
||||
notNull: true,
|
||||
index: false
|
||||
}
|
||||
}
|
||||
|
||||
const calendarSchema: Schema = {
|
||||
...baseSchema,
|
||||
hidden: {
|
||||
type: 'bool',
|
||||
notNull: true,
|
||||
index: true
|
||||
}
|
||||
}
|
||||
|
||||
const eventSchema: Schema = {
|
||||
...defaultSchema,
|
||||
calendar: {
|
||||
type: 'text',
|
||||
notNull: true,
|
||||
index: true
|
||||
},
|
||||
date: {
|
||||
type: 'bigint',
|
||||
notNull: true,
|
||||
index: true
|
||||
},
|
||||
dueDate: {
|
||||
type: 'bigint',
|
||||
notNull: true,
|
||||
index: true
|
||||
},
|
||||
participants: {
|
||||
type: 'text[]',
|
||||
notNull: true,
|
||||
index: true
|
||||
}
|
||||
}
|
||||
|
||||
export function addSchema (domain: string, schema: Schema): void {
|
||||
domainSchemas[translateDomain(domain)] = schema
|
||||
}
|
||||
@ -166,6 +223,9 @@ export function translateDomain (domain: string): string {
|
||||
export const domainSchemas: Record<string, Schema> = {
|
||||
[DOMAIN_SPACE]: spaceSchema,
|
||||
[DOMAIN_TX]: txSchema,
|
||||
[translateDomain('time')]: timeSchema,
|
||||
[translateDomain('calendar')]: calendarSchema,
|
||||
[translateDomain('event')]: eventSchema,
|
||||
[translateDomain(DOMAIN_DOC_INDEX_STATE)]: docIndexStateSchema,
|
||||
notification: notificationSchema,
|
||||
[translateDomain('notification-dnc')]: dncSchema,
|
||||
|
@ -210,7 +210,7 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
await close(cursorName)
|
||||
return null
|
||||
}
|
||||
return result.map((p) => parseDoc(p as any))
|
||||
return result.map((p) => parseDoc(p as any, _domain))
|
||||
}
|
||||
|
||||
await init()
|
||||
@ -254,7 +254,7 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
}
|
||||
const finalSql: string = [select, ...sqlChunks].join(' ')
|
||||
const result = await this.client.unsafe(finalSql)
|
||||
return result.map((p) => parseDocWithProjection(p as any, options?.projection))
|
||||
return result.map((p) => parseDocWithProjection(p as any, domain, options?.projection))
|
||||
}
|
||||
|
||||
buildRawOrder<T extends Doc>(domain: string, sort: SortingQuery<T>): string {
|
||||
@ -307,7 +307,7 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
const res = await client.unsafe(
|
||||
`SELECT * FROM ${translateDomain(domain)} WHERE ${translatedQuery} FOR UPDATE`
|
||||
)
|
||||
const docs = res.map((p) => parseDoc(p as any))
|
||||
const docs = res.map((p) => parseDoc(p as any, domain))
|
||||
for (const doc of docs) {
|
||||
if (doc === undefined) continue
|
||||
const prevAttachedTo = (doc as any).attachedTo
|
||||
@ -460,11 +460,11 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
const result = await connection.unsafe(finalSql)
|
||||
if (options?.lookup === undefined && options?.domainLookup === undefined) {
|
||||
return toFindResult(
|
||||
result.map((p) => parseDocWithProjection(p as any, options?.projection)),
|
||||
result.map((p) => parseDocWithProjection(p as any, domain, options?.projection)),
|
||||
total
|
||||
)
|
||||
} else {
|
||||
const res = this.parseLookup<T>(result, joins, options?.projection)
|
||||
const res = this.parseLookup<T>(result, joins, options?.projection, domain)
|
||||
return toFindResult(res, total)
|
||||
}
|
||||
} catch (err) {
|
||||
@ -493,7 +493,8 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
private parseLookup<T extends Doc>(
|
||||
rows: any[],
|
||||
joins: JoinProps[],
|
||||
projection: Projection<T> | undefined
|
||||
projection: Projection<T> | undefined,
|
||||
domain: string
|
||||
): WithLookup<T>[] {
|
||||
const map = new Map<Ref<T>, WithLookup<T>>()
|
||||
const modelJoins: JoinProps[] = []
|
||||
@ -526,7 +527,7 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
if (res === undefined) continue
|
||||
const { obj, key } = res
|
||||
|
||||
const parsed = row[column].map(parseDoc)
|
||||
const parsed = row[column].map((p: any) => parseDoc(p, domain))
|
||||
obj[key] = parsed
|
||||
}
|
||||
} else if (column.startsWith('lookup_')) {
|
||||
@ -982,7 +983,11 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
const val = value[operator]
|
||||
switch (operator) {
|
||||
case '$ne':
|
||||
res.push(`${tkey} != '${val}'`)
|
||||
if (val === null) {
|
||||
res.push(`${tkey} IS NOT NULL`)
|
||||
} else {
|
||||
res.push(`${tkey} != '${val}'`)
|
||||
}
|
||||
break
|
||||
case '$gt':
|
||||
res.push(`${tkey} > '${val}'`)
|
||||
@ -1131,7 +1136,7 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
if (result.length === 0) {
|
||||
return []
|
||||
}
|
||||
return result.filter((it) => it != null).map((it) => parseDoc(it as any))
|
||||
return result.filter((it) => it != null).map((it) => parseDoc(it as any, domain))
|
||||
}
|
||||
|
||||
const flush = async (flush = false): Promise<void> => {
|
||||
@ -1221,7 +1226,7 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
const connection = (await this.getConnection(ctx)) ?? this.client
|
||||
const res =
|
||||
await connection`SELECT * FROM ${connection(translateDomain(domain))} WHERE _id = ANY(${docs}) AND "workspaceId" = ${this.workspaceId.name}`
|
||||
return res.map((p) => parseDocWithProjection(p as any))
|
||||
return res.map((p) => parseDocWithProjection(p as any, domain))
|
||||
})
|
||||
}
|
||||
|
||||
@ -1306,7 +1311,7 @@ abstract class PostgresAdapterBase implements DbAdapter {
|
||||
try {
|
||||
const res =
|
||||
await client`SELECT * FROM ${client(translateDomain(domain))} WHERE _id = ANY(${ids}) AND "workspaceId" = ${this.workspaceId.name} FOR UPDATE`
|
||||
const docs = res.map((p) => parseDoc(p as any))
|
||||
const docs = res.map((p) => parseDoc(p as any, domain))
|
||||
const map = new Map(docs.map((d) => [d._id, d]))
|
||||
for (const [_id, ops] of operations) {
|
||||
const doc = map.get(_id)
|
||||
@ -1581,7 +1586,8 @@ class PostgresAdapter extends PostgresAdapterBase {
|
||||
forUpdate ? client` FOR UPDATE` : client``
|
||||
}`
|
||||
const dbDoc = res[0]
|
||||
return dbDoc !== undefined ? parseDoc(dbDoc as any) : undefined
|
||||
const domain = this.hierarchy.getDomain(_class)
|
||||
return dbDoc !== undefined ? parseDoc(dbDoc as any, domain) : undefined
|
||||
})
|
||||
}
|
||||
|
||||
@ -1621,7 +1627,7 @@ class PostgresTxAdapter extends PostgresAdapterBase implements TxAdapter {
|
||||
const res = await this
|
||||
.client`SELECT * FROM ${this.client(translateDomain(DOMAIN_TX))} WHERE "workspaceId" = ${this.workspaceId.name} AND "objectSpace" = ${core.space.Model} ORDER BY _id ASC, "modifiedOn" ASC`
|
||||
|
||||
const model = res.map((p) => parseDoc<Tx>(p as any))
|
||||
const model = res.map((p) => parseDoc<Tx>(p as any, DOMAIN_TX))
|
||||
// We need to put all core.account.System transactions first
|
||||
const systemTx: Tx[] = []
|
||||
const userTx: Tx[] = []
|
||||
|
@ -326,8 +326,8 @@ export function parseUpdate<T extends Doc> (
|
||||
const remainingData: Partial<T> = {}
|
||||
|
||||
for (const key in ops) {
|
||||
if (key === '$push' || key === '$pull') {
|
||||
const val = (ops as any)[key]
|
||||
const val = (ops as any)[key]
|
||||
if (key.startsWith('$')) {
|
||||
for (const k in val) {
|
||||
if (getDocFieldsByDomains(domain).includes(k)) {
|
||||
;(extractedFields as any)[k] = val[key]
|
||||
@ -337,9 +337,9 @@ export function parseUpdate<T extends Doc> (
|
||||
}
|
||||
} else {
|
||||
if (getDocFieldsByDomains(domain).includes(key)) {
|
||||
;(extractedFields as any)[key] = (ops as any)[key]
|
||||
;(extractedFields as any)[key] = val
|
||||
} else {
|
||||
;(remainingData as any)[key] = (ops as any)[key]
|
||||
;(remainingData as any)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -396,8 +396,13 @@ export class DBCollectionHelper implements DomainHelperOperations {
|
||||
}
|
||||
}
|
||||
|
||||
export function parseDocWithProjection<T extends Doc> (doc: DBDoc, projection?: Projection<T> | undefined): T {
|
||||
export function parseDocWithProjection<T extends Doc> (
|
||||
doc: DBDoc,
|
||||
domain: string,
|
||||
projection?: Projection<T> | undefined
|
||||
): T {
|
||||
const { workspaceId, data, ...rest } = doc
|
||||
const schema = getSchema(domain)
|
||||
for (const key in rest) {
|
||||
if ((rest as any)[key] === 'NULL' || (rest as any)[key] === null) {
|
||||
if (key === 'attachedTo') {
|
||||
@ -407,7 +412,7 @@ export function parseDocWithProjection<T extends Doc> (doc: DBDoc, projection?:
|
||||
;(rest as any)[key] = null
|
||||
}
|
||||
}
|
||||
if (key === 'modifiedOn' || key === 'createdOn') {
|
||||
if (schema[key] !== undefined && schema[key].type === 'bigint') {
|
||||
;(rest as any)[key] = Number.parseInt((rest as any)[key])
|
||||
}
|
||||
}
|
||||
@ -427,7 +432,8 @@ export function parseDocWithProjection<T extends Doc> (doc: DBDoc, projection?:
|
||||
return res
|
||||
}
|
||||
|
||||
export function parseDoc<T extends Doc> (doc: DBDoc): T {
|
||||
export function parseDoc<T extends Doc> (doc: DBDoc, domain: string): T {
|
||||
const schema = getSchema(domain)
|
||||
const { workspaceId, data, ...rest } = doc
|
||||
for (const key in rest) {
|
||||
if ((rest as any)[key] === 'NULL' || (rest as any)[key] === null) {
|
||||
@ -438,7 +444,7 @@ export function parseDoc<T extends Doc> (doc: DBDoc): T {
|
||||
;(rest as any)[key] = null
|
||||
}
|
||||
}
|
||||
if (key === 'modifiedOn' || key === 'createdOn') {
|
||||
if (schema[key] !== undefined && schema[key].type === 'bigint') {
|
||||
;(rest as any)[key] = Number.parseInt((rest as any)[key])
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user