Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions packages/core/src/memdb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ export abstract class MemDb extends TxProcessor implements Storage {

/**
* Only in model find without lookups and sorting.
* Do not clone results, so be aware modifications are not allowed.
*/
findAllSync<T extends Doc>(_class: Ref<Class<T>>, query: DocumentQuery<T>, options?: FindOptions<T>): FindResult<T> {
let result: WithLookup<Doc>[]
Expand All @@ -210,9 +211,13 @@ export abstract class MemDb extends TxProcessor implements Storage {
}
const total = result.length
result = result.slice(0, options?.limit)
const tresult = this.hierarchy.clone(result) as WithLookup<T>[]
const res = tresult.map((it) => this.hierarchy.updateLookupMixin(_class, it, options))
return toFindResult(res, total)

return toFindResult(
result.map((it) => {
return baseClass !== _class ? this.hierarchy.as(it, _class) : it
}) as WithLookup<T>[],
total
)
}

addDoc (doc: Doc): void {
Expand Down
41 changes: 41 additions & 0 deletions packages/core/src/proxy.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PlatformError, unknownError } from '@hcengineering/platform'
import { Ref } from '.'
import type { Doc, Mixin } from './classes'

Expand Down Expand Up @@ -26,6 +27,46 @@ export function _createMixinProxy (mixin: Mixin<Doc>, ancestorProxy: ProxyHandle
}
}

export function freeze (value: any): any {
if (value != null && typeof value === 'object') {
if (Array.isArray(value)) {
return value.map((it) => freeze(it))
}
if (value instanceof Map) {
throw new PlatformError(unknownError('Map is not allowed in model'))
}
if (value instanceof Set) {
throw new PlatformError(unknownError('Set is not allowed in model'))
}
return new Proxy(value, _createFreezeProxy(value))
}
return value
}
/**
* @internal
*/
export function _createFreezeProxy (doc: Doc): ProxyHandler<Doc> {
return {
get (target: any, property: string, receiver: any): any {
const value = target[property]
return freeze(value)
},
set (target, p, newValue, receiver): any {
throw new PlatformError(unknownError('Modification is not allowed'))
},
defineProperty (target, property, attributes): any {
throw new PlatformError(unknownError('Modification is not allowed'))
},

deleteProperty (target, p): any {
throw new PlatformError(unknownError('Modification is not allowed'))
},
setPrototypeOf (target, v): any {
throw new PlatformError(unknownError('Modification is not allowed'))
}
}
}

/**
* @internal
*/
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,9 @@ export function escapeLikeForRegexp (value: string): string {
*/
export function toFindResult<T extends Doc> (docs: T[], total?: number, lookupMap?: Record<string, Doc>): FindResult<T> {
const length = total ?? docs.length
if (Object.keys(lookupMap ?? {}).length === 0) {
lookupMap = undefined
}
return Object.assign(docs, { total: length, lookupMap })
}

Expand Down
14 changes: 9 additions & 5 deletions plugins/tracker-resources/src/components/CreateIssue.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -352,10 +352,14 @@
attr: client.getHierarchy().getAttribute(tracker.class.Issue, 'labels')
}

$: spaceQuery.query(tracker.class.Project, { _id: _space }, (res) => {
resetDefaultAssigneeId()
currentProject = res[0]
})
$: if (_space !== undefined) {
spaceQuery.query(tracker.class.Project, { _id: _space }, (res) => {
resetDefaultAssigneeId()
currentProject = res[0]
})
} else {
currentProject = undefined
}

const docCreateManager = DocCreateExtensionManager.create(tracker.class.Issue)

Expand Down Expand Up @@ -758,7 +762,7 @@
label={tracker.string.Project}
bind:space={_space}
on:object={(evt) => {
currentProject = evt.detail
currentProject = evt.detail ?? undefined
}}
kind={'regular'}
size={'small'}
Expand Down
4 changes: 2 additions & 2 deletions pods/server/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM node:20

WORKDIR /usr/src/app
RUN npm install --ignore-scripts=false --verbose bufferutil utf-8-validate @mongodb-js/zstd snappy --unsafe-perm
RUN npm install --ignore-scripts=false --verbose bufferutil utf-8-validate @mongodb-js/zstd snappy msgpackr msgpackr-extract --unsafe-perm
RUN npm install --ignore-scripts=false --verbose uNetworking/uWebSockets.js#v20.47.0

RUN apt-get update
Expand All @@ -16,4 +16,4 @@ COPY bundle/bundle.js.map ./

EXPOSE 8080
ENV UWS_HTTP_MAX_HEADERS_SIZE 32768
CMD node --enable-source-maps --inspect bundle.js
CMD node --enable-source-maps --inspect=0.0.0.0:9229 bundle.js
2 changes: 1 addition & 1 deletion pods/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"_phase:bundle": "rushx bundle",
"_phase:docker-build": "rushx docker:build",
"_phase:docker-staging": "rushx docker:staging",
"bundle": "mkdir -p bundle && esbuild src/__start.ts --sourcemap=inline --bundle --keep-names --platform=node --external:*.node --external:bufferutil --external:snappy --external:utf-8-validate --define:process.env.MODEL_VERSION=$(node ../../common/scripts/show_version.js) --define:process.env.GIT_REVISION=$(../../common/scripts/git_version.sh) --outfile=bundle/bundle.js --log-level=error --sourcemap=external",
"bundle": "mkdir -p bundle && esbuild src/__start.ts --sourcemap=inline --bundle --keep-names --platform=node --external:*.node --external:bufferutil --external:snappy --external:utf-8-validate --external:msgpackr-extract --define:process.env.MODEL_VERSION=$(node ../../common/scripts/show_version.js) --define:process.env.GIT_REVISION=$(../../common/scripts/git_version.sh) --outfile=bundle/bundle.js --log-level=error --sourcemap=external",
"docker:build": "../../common/scripts/docker_build.sh hardcoreeng/transactor",
"docker:tbuild": "docker build -t hardcoreeng/transactor . --platform=linux/amd64 && ../../common/scripts/docker_tag_push.sh hardcoreeng/transactor",
"docker:abuild": "docker build -t hardcoreeng/transactor . --platform=linux/arm64 && ../../common/scripts/docker_tag_push.sh hardcoreeng/transactor",
Expand Down
29 changes: 22 additions & 7 deletions server/middleware/src/lookup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ export class LookupMiddleware extends BaseMiddleware implements Middleware {

for (const d of result) {
if (d.$lookup !== undefined) {
const newDoc = clone(d)
const newDoc: any = { ...d }
newDoc.$lookup = clone(d.$lookup)
newResult.push(newDoc)
for (const [k, v] of Object.entries(d.$lookup)) {
if (!Array.isArray(v)) {
Expand All @@ -83,22 +84,36 @@ export class LookupMiddleware extends BaseMiddleware implements Middleware {
}
}
const lookupMap = Object.fromEntries(Array.from(Object.values(idClassMap)).map((it) => [it.id, it.doc]))
if (Object.keys(lookupMap).length > 0) {
return toFindResult(newResult, result.total, lookupMap)
}
return this.cleanQuery<T>(toFindResult(newResult, result.total, lookupMap), query, lookupMap)
}

// We need to get rid of simple query parameters matched in documents
return this.cleanQuery<T>(result, query)
}

private cleanQuery<T extends Doc>(
result: FindResult<T>,
query: DocumentQuery<T>,
lookupMap?: Record<string, Doc>
): FindResult<T> {
const newResult: T[] = []
for (const doc of result) {
let _doc = doc
let cloned = false
for (const [k, v] of Object.entries(query)) {
if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {
if ((doc as any)[k] === v) {
if ((_doc as any)[k] === v) {
if (!cloned) {
_doc = { ...doc } as any
cloned = true
}
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete (doc as any)[k]
delete (_doc as any)[k]
}
}
}
newResult.push(_doc)
}
return result
return toFindResult(newResult, result.total, lookupMap)
}
}
2 changes: 1 addition & 1 deletion server/rpc/src/rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export interface Response<R> {
}

export class RPCHandler {
packr = new Packr({ structuredClone: true, bundleStrings: true, copyBuffers: true })
packr = new Packr({ structuredClone: true, bundleStrings: true, copyBuffers: false })
protoSerialize (object: object, binary: boolean): any {
if (!binary) {
return JSON.stringify(object, replacer)
Expand Down
5 changes: 3 additions & 2 deletions services/github/pod-github/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ FROM node:20
WORKDIR /usr/src/app

RUN npm install --ignore-scripts=false --verbose bufferutil utf-8-validate @mongodb-js/zstd snappy --unsafe-perm
COPY bundle/bundle.js ./
COPY bundle/bundle.js.map ./

RUN apt-get update
RUN apt-get install libjemalloc2

ENV LD_PRELOAD=libjemalloc.so.2
ENV MALLOC_CONF=dirty_decay_ms:1000,narenas:2,background_thread:true

COPY bundle/bundle.js ./
COPY bundle/bundle.js.map ./

EXPOSE 3078
CMD [ "node", "--inspect", "--async-stack-traces", "--enable-source-maps", "bundle.js" ]
2 changes: 1 addition & 1 deletion services/print/pod-print/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ RUN apt-get update && \

WORKDIR /usr/src/app

COPY bundle/bundle.js ./
RUN npm install --ignore-scripts=false --verbose bufferutil utf-8-validate @mongodb-js/zstd snappy --unsafe-perm
COPY bundle/bundle.js ./

CMD [ "dumb-init", "node", "bundle.js" ]
18 changes: 10 additions & 8 deletions tests/sanity/tests/model/tracker/issues-page.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { expect, type Locator } from '@playwright/test'
import path from 'path'
import { createIssue, toTime } from '../../tracker/tracker.utils'
import { attachScreenshot, iterateLocator } from '../../utils'
import { CommonTrackerPage } from './common-tracker-page'
import { NewIssue } from './types'
import { createIssue, toTime } from '../../tracker/tracker.utils'

const retryOptions = { intervals: [1000, 1500, 2500], timeout: 60000 }
export class IssuesPage extends CommonTrackerPage {
modelSelectorAll = (): Locator => this.page.locator('label[data-id="tab-all"]')
issues = (): Locator => this.page.locator('text="Issues"')
Expand Down Expand Up @@ -492,15 +493,14 @@ export class IssuesPage extends CommonTrackerPage {
}

async searchIssueByName (issueName: string): Promise<void> {
for (let i = 0; i < 5; i++) {
await expect(async () => {
await this.inputSearchIcon().click()
await this.inputSearch().fill(issueName)
const v = await this.inputSearch().inputValue()
if (v === issueName) {
await this.inputSearch().press('Enter')
break
}
}
}).toPass(retryOptions)
}

async openIssueByName (issueName: string): Promise<void> {
Expand Down Expand Up @@ -557,9 +557,9 @@ export class IssuesPage extends CommonTrackerPage {
}

async checkIssuesCount (issueName: string, count: number, timeout?: number): Promise<void> {
await expect(this.issueAnchorByName(issueName)).toHaveCount(count, {
timeout: timeout !== undefined ? timeout * 1000 : undefined
})
await expect(async () => {
await expect(this.issueAnchorByName(issueName)).toHaveCount(count)
}).toPass(retryOptions)
}

async selectTemplate (templateName: string): Promise<void> {
Expand All @@ -573,7 +573,9 @@ export class IssuesPage extends CommonTrackerPage {
}

async checkAttachmentsCount (issueName: string, count: string): Promise<void> {
await expect(this.attachmentContentButton(issueName)).toHaveText(count)
await expect(async () => {
await expect(this.attachmentContentButton(issueName)).toHaveText(count)
}).toPass(retryOptions)
}

async addAttachmentToIssue (issueName: string, filePath: string): Promise<void> {
Expand Down