chore(server): core IoC 7 - getProjectTopLevelModelsTreeFactory
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
import { Branch, ModelTreeItem } from '@/modules/core/domain/branches/types'
|
||||
import { BranchLatestCommit } from '@/modules/core/domain/commits/types'
|
||||
import { ProjectModelsArgs } from '@/modules/core/graph/generated/graphql'
|
||||
import {
|
||||
ModelsTreeItemCollection,
|
||||
ProjectModelsArgs,
|
||||
ProjectModelsTreeArgs
|
||||
} from '@/modules/core/graph/generated/graphql'
|
||||
import { ModelsTreeItemGraphQLReturn } from '@/modules/core/helpers/graphTypes'
|
||||
import { Nullable, Optional } from '@speckle/shared'
|
||||
import { Merge } from 'type-fest'
|
||||
|
||||
export type GenerateBranchId = () => string
|
||||
|
||||
@@ -63,3 +69,34 @@ export type GetPaginatedProjectModels = (
|
||||
items: Branch[]
|
||||
cursor: string | null
|
||||
}>
|
||||
|
||||
export type GetModelTreeItemsFiltered = (
|
||||
projectId: string,
|
||||
args: ProjectModelsTreeArgs,
|
||||
options?: Partial<{
|
||||
filterOutEmptyMain: boolean
|
||||
}>
|
||||
) => Promise<ModelsTreeItemGraphQLReturn[]>
|
||||
|
||||
export type GetModelTreeItemsFilteredTotalCount = (
|
||||
projectId: string,
|
||||
args: ProjectModelsTreeArgs,
|
||||
options?: Partial<{
|
||||
filterOutEmptyMain: boolean
|
||||
}>
|
||||
) => Promise<number>
|
||||
|
||||
export type GetProjectTopLevelModelsTree = (
|
||||
projectId: string,
|
||||
args: ProjectModelsTreeArgs,
|
||||
options?: Partial<{
|
||||
filterOutEmptyMain: boolean
|
||||
}>
|
||||
) => Promise<
|
||||
Merge<
|
||||
ModelsTreeItemCollection,
|
||||
{
|
||||
items: ModelsTreeItemGraphQLReturn[]
|
||||
}
|
||||
>
|
||||
>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from '@/modules/core/services/branch/management'
|
||||
import {
|
||||
getPaginatedProjectModelsFactory,
|
||||
getProjectTopLevelModelsTree
|
||||
getProjectTopLevelModelsTreeFactory
|
||||
} from '@/modules/core/services/branch/retrieval'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
import { getServerOrigin } from '@/modules/shared/helpers/envHelper'
|
||||
@@ -25,6 +25,8 @@ import {
|
||||
import {
|
||||
getBranchLatestCommitsFactory,
|
||||
getModelTreeItems,
|
||||
getModelTreeItemsFilteredFactory,
|
||||
getModelTreeItemsFilteredTotalCountFactory,
|
||||
getPaginatedProjectModelsItemsFactory,
|
||||
getPaginatedProjectModelsTotalCountFactory,
|
||||
getStreamBranchesByNameFactory
|
||||
@@ -52,6 +54,12 @@ const getPaginatedProjectModels = getPaginatedProjectModelsFactory({
|
||||
db
|
||||
})
|
||||
})
|
||||
const getProjectTopLevelModelsTree = getProjectTopLevelModelsTreeFactory({
|
||||
getModelTreeItemsFiltered: getModelTreeItemsFilteredFactory({ db }),
|
||||
getModelTreeItemsFilteredTotalCount: getModelTreeItemsFilteredTotalCountFactory({
|
||||
db
|
||||
})
|
||||
})
|
||||
|
||||
export = {
|
||||
User: {
|
||||
|
||||
@@ -20,6 +20,8 @@ import {
|
||||
GetBranchById,
|
||||
GetBranchesByIds,
|
||||
GetBranchLatestCommits,
|
||||
GetModelTreeItemsFiltered,
|
||||
GetModelTreeItemsFilteredTotalCount,
|
||||
GetPaginatedProjectModelsItems,
|
||||
GetPaginatedProjectModelsTotalCount,
|
||||
GetStreamBranchByName,
|
||||
@@ -360,114 +362,125 @@ export const getPaginatedProjectModelsTotalCountFactory =
|
||||
return parseInt(res?.count || '0')
|
||||
}
|
||||
|
||||
function getModelTreeItemsFilteredBaseQuery(
|
||||
projectId: string,
|
||||
args: ProjectModelsTreeArgs,
|
||||
options?: Partial<{ filterOutEmptyMain: boolean }>
|
||||
) {
|
||||
const search = args.filter?.search || ''
|
||||
const sourceApps = args.filter?.sourceApps || []
|
||||
const contributors = args.filter?.contributors || []
|
||||
const isFiltering = search.length || sourceApps.length || contributors.length
|
||||
const filterOutEmptyMain = !isFiltering && (options?.filterOutEmptyMain ?? true)
|
||||
const getModelTreeItemsFilteredBaseQueryFactory =
|
||||
(deps: { db: Knex }) =>
|
||||
(
|
||||
projectId: string,
|
||||
args: ProjectModelsTreeArgs,
|
||||
options?: Partial<{ filterOutEmptyMain: boolean }>
|
||||
) => {
|
||||
const search = args.filter?.search || ''
|
||||
const sourceApps = args.filter?.sourceApps || []
|
||||
const contributors = args.filter?.contributors || []
|
||||
const isFiltering = search.length || sourceApps.length || contributors.length
|
||||
const filterOutEmptyMain = !isFiltering && (options?.filterOutEmptyMain ?? true)
|
||||
|
||||
const BranchesJoin = Branches.with({ withCustomTablePrefix: 'b2' })
|
||||
const BranchesJoin = Branches.with({ withCustomTablePrefix: 'b2' })
|
||||
|
||||
const q = Branches.knex()
|
||||
.select<Array<{ name: string; updatedAt: Date; hasChildren: boolean }>>([
|
||||
Branches.col.name,
|
||||
Branches.col.updatedAt,
|
||||
knex.raw(`COUNT(??) > 0 as "hasChildren"`, [BranchesJoin.col.id])
|
||||
])
|
||||
.leftJoin(BranchesJoin.name, (lj) => {
|
||||
lj.on(
|
||||
BranchesJoin.col.name,
|
||||
'ilike',
|
||||
knex.raw(`(?? || '/%')`, [Branches.col.name])
|
||||
)
|
||||
.andOn(BranchesJoin.col.name, '!=', Branches.col.name)
|
||||
.andOn(BranchesJoin.col.streamId, '=', Branches.col.streamId)
|
||||
})
|
||||
.where(Branches.col.streamId, projectId)
|
||||
.groupBy(Branches.col.id)
|
||||
.orderBy(Branches.col.updatedAt, 'desc')
|
||||
const q = tables
|
||||
.branches(deps.db)
|
||||
.select<Array<{ name: string; updatedAt: Date; hasChildren: boolean }>>([
|
||||
Branches.col.name,
|
||||
Branches.col.updatedAt,
|
||||
knex.raw(`COUNT(??) > 0 as "hasChildren"`, [BranchesJoin.col.id])
|
||||
])
|
||||
.leftJoin(BranchesJoin.name, (lj) => {
|
||||
lj.on(
|
||||
BranchesJoin.col.name,
|
||||
'ilike',
|
||||
knex.raw(`(?? || '/%')`, [Branches.col.name])
|
||||
)
|
||||
.andOn(BranchesJoin.col.name, '!=', Branches.col.name)
|
||||
.andOn(BranchesJoin.col.streamId, '=', Branches.col.streamId)
|
||||
})
|
||||
.where(Branches.col.streamId, projectId)
|
||||
.groupBy(Branches.col.id)
|
||||
.orderBy(Branches.col.updatedAt, 'desc')
|
||||
|
||||
if (filterOutEmptyMain) {
|
||||
q.andWhere((w) => {
|
||||
w.whereNot(Branches.col.name, 'main').orWhere(
|
||||
0,
|
||||
'<',
|
||||
BranchCommits.knex()
|
||||
.count()
|
||||
.where(BranchCommits.col.branchId, knex.raw(Branches.col.id))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
if (search.length) {
|
||||
q.andWhereILike(Branches.col.name, `%${search}%`)
|
||||
}
|
||||
|
||||
if (sourceApps.length || contributors.length) {
|
||||
q.leftJoin(
|
||||
BranchCommits.name,
|
||||
BranchCommits.col.branchId,
|
||||
Branches.col.id
|
||||
).leftJoin(Commits.name, Commits.col.id, BranchCommits.col.commitId)
|
||||
|
||||
if (contributors.length) {
|
||||
q.whereIn(Commits.col.author, contributors)
|
||||
if (filterOutEmptyMain) {
|
||||
q.andWhere((w) => {
|
||||
w.whereNot(Branches.col.name, 'main').orWhere(
|
||||
0,
|
||||
'<',
|
||||
BranchCommits.knex()
|
||||
.count()
|
||||
.where(BranchCommits.col.branchId, knex.raw(Branches.col.id))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
if (sourceApps.length) {
|
||||
q.whereRaw(
|
||||
knex.raw(`?? ~* ?`, [Commits.col.sourceApplication, sourceApps.join('|')])
|
||||
)
|
||||
if (search.length) {
|
||||
q.andWhereILike(Branches.col.name, `%${search}%`)
|
||||
}
|
||||
|
||||
if (sourceApps.length || contributors.length) {
|
||||
q.leftJoin(
|
||||
BranchCommits.name,
|
||||
BranchCommits.col.branchId,
|
||||
Branches.col.id
|
||||
).leftJoin(Commits.name, Commits.col.id, BranchCommits.col.commitId)
|
||||
|
||||
if (contributors.length) {
|
||||
q.whereIn(Commits.col.author, contributors)
|
||||
}
|
||||
|
||||
if (sourceApps.length) {
|
||||
q.whereRaw(
|
||||
knex.raw(`?? ~* ?`, [Commits.col.sourceApplication, sourceApps.join('|')])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return q
|
||||
}
|
||||
|
||||
return q
|
||||
}
|
||||
export const getModelTreeItemsFilteredFactory =
|
||||
(deps: { db: Knex }): GetModelTreeItemsFiltered =>
|
||||
async (
|
||||
projectId: string,
|
||||
args: ProjectModelsTreeArgs,
|
||||
options?: Partial<{ filterOutEmptyMain: boolean }>
|
||||
) => {
|
||||
const limit = clamp(args.limit || 25, 0, 100)
|
||||
const q = getModelTreeItemsFilteredBaseQueryFactory(deps)(projectId, args, options)
|
||||
q.limit(limit)
|
||||
|
||||
export async function getModelTreeItemsFiltered(
|
||||
projectId: string,
|
||||
args: ProjectModelsTreeArgs,
|
||||
options?: Partial<{ filterOutEmptyMain: boolean }>
|
||||
) {
|
||||
const limit = clamp(args.limit || 25, 0, 100)
|
||||
const q = getModelTreeItemsFilteredBaseQuery(projectId, args, options)
|
||||
q.limit(limit)
|
||||
if (args.cursor) {
|
||||
q.andWhere(Branches.col.updatedAt, '<', args.cursor)
|
||||
}
|
||||
|
||||
if (args.cursor) {
|
||||
q.andWhere(Branches.col.updatedAt, '<', args.cursor)
|
||||
const res = await q
|
||||
const items = res.map((i): ModelsTreeItemGraphQLReturn => {
|
||||
const displayName = last(i.name.split('/')) as string
|
||||
return {
|
||||
id: `${projectId}-${i.name}`,
|
||||
projectId,
|
||||
name: displayName,
|
||||
fullName: i.name,
|
||||
updatedAt: i.updatedAt,
|
||||
hasChildren: i.hasChildren
|
||||
}
|
||||
})
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
const res = await q
|
||||
const items = res.map((i): ModelsTreeItemGraphQLReturn => {
|
||||
const displayName = last(i.name.split('/')) as string
|
||||
return {
|
||||
id: `${projectId}-${i.name}`,
|
||||
export const getModelTreeItemsFilteredTotalCountFactory =
|
||||
(deps: { db: Knex }): GetModelTreeItemsFilteredTotalCount =>
|
||||
async (
|
||||
projectId: string,
|
||||
args: ProjectModelsTreeArgs,
|
||||
options?: Partial<{ filterOutEmptyMain: boolean }>
|
||||
) => {
|
||||
const baseQ = getModelTreeItemsFilteredBaseQueryFactory(deps)(
|
||||
projectId,
|
||||
name: displayName,
|
||||
fullName: i.name,
|
||||
updatedAt: i.updatedAt,
|
||||
hasChildren: i.hasChildren
|
||||
}
|
||||
})
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
export async function getModelTreeItemsFilteredTotalCount(
|
||||
projectId: string,
|
||||
args: ProjectModelsTreeArgs,
|
||||
options?: Partial<{ filterOutEmptyMain: boolean }>
|
||||
) {
|
||||
const baseQ = getModelTreeItemsFilteredBaseQuery(projectId, args, options)
|
||||
const q = knex.count<{ count: string }[]>().from(baseQ.as('sq1'))
|
||||
const [row] = await q
|
||||
return parseInt(row.count || '0')
|
||||
}
|
||||
args,
|
||||
options
|
||||
)
|
||||
const q = deps.db().count<{ count: string }[]>().from(baseQ.as('sq1'))
|
||||
const [row] = await q
|
||||
return parseInt(row.count || '0')
|
||||
}
|
||||
|
||||
function getModelTreeItemsBaseQuery(
|
||||
projectId: string,
|
||||
|
||||
@@ -6,9 +6,7 @@ import {
|
||||
} from '@/modules/core/graph/generated/graphql'
|
||||
import { getBranchesByStreamId } from '@/modules/core/services/branches'
|
||||
import {
|
||||
getModelTreeItemsFiltered,
|
||||
getModelTreeItems,
|
||||
getModelTreeItemsFilteredTotalCount,
|
||||
getModelTreeItemsTotalCount
|
||||
} from '@/modules/core/repositories/branches'
|
||||
import { last } from 'lodash'
|
||||
@@ -17,8 +15,11 @@ import { ModelsTreeItemGraphQLReturn } from '@/modules/core/helpers/graphTypes'
|
||||
import { getMaximumProjectModelsPerPage } from '@/modules/shared/helpers/envHelper'
|
||||
import { BadRequestError } from '@/modules/shared/errors'
|
||||
import {
|
||||
GetModelTreeItemsFiltered,
|
||||
GetModelTreeItemsFilteredTotalCount,
|
||||
GetPaginatedProjectModelsItems,
|
||||
GetPaginatedProjectModelsTotalCount
|
||||
GetPaginatedProjectModelsTotalCount,
|
||||
GetProjectTopLevelModelsTree
|
||||
} from '@/modules/core/domain/branches/operations'
|
||||
|
||||
export async function getPaginatedStreamBranches(
|
||||
@@ -56,40 +57,47 @@ export const getPaginatedProjectModelsFactory =
|
||||
}
|
||||
}
|
||||
|
||||
export async function getProjectTopLevelModelsTree(
|
||||
projectId: string,
|
||||
args: ProjectModelsTreeArgs,
|
||||
options?: Partial<{ filterOutEmptyMain: boolean }>
|
||||
): Promise<Merge<ModelsTreeItemCollection, { items: ModelsTreeItemGraphQLReturn[] }>> {
|
||||
let items: ModelsTreeItemGraphQLReturn[] = []
|
||||
let totalCount = 0
|
||||
export const getProjectTopLevelModelsTreeFactory =
|
||||
(deps: {
|
||||
getModelTreeItemsFiltered: GetModelTreeItemsFiltered
|
||||
getModelTreeItemsFilteredTotalCount: GetModelTreeItemsFilteredTotalCount
|
||||
}): GetProjectTopLevelModelsTree =>
|
||||
async (
|
||||
projectId: string,
|
||||
args: ProjectModelsTreeArgs,
|
||||
options?: Partial<{ filterOutEmptyMain: boolean }>
|
||||
): Promise<
|
||||
Merge<ModelsTreeItemCollection, { items: ModelsTreeItemGraphQLReturn[] }>
|
||||
> => {
|
||||
let items: ModelsTreeItemGraphQLReturn[] = []
|
||||
let totalCount = 0
|
||||
|
||||
if (
|
||||
args.filter?.search ||
|
||||
args.filter?.contributors?.length ||
|
||||
args.filter?.sourceApps?.length
|
||||
) {
|
||||
const [filteredItems, filteredTotalCount] = await Promise.all([
|
||||
getModelTreeItemsFiltered(projectId, args, options),
|
||||
getModelTreeItemsFilteredTotalCount(projectId, args, options)
|
||||
])
|
||||
if (
|
||||
args.filter?.search ||
|
||||
args.filter?.contributors?.length ||
|
||||
args.filter?.sourceApps?.length
|
||||
) {
|
||||
const [filteredItems, filteredTotalCount] = await Promise.all([
|
||||
deps.getModelTreeItemsFiltered(projectId, args, options),
|
||||
deps.getModelTreeItemsFilteredTotalCount(projectId, args, options)
|
||||
])
|
||||
|
||||
items = filteredItems
|
||||
totalCount = filteredTotalCount
|
||||
} else {
|
||||
const [unfilteredItems, unfilteredTotalCount] = await Promise.all([
|
||||
getModelTreeItems(projectId, args, options),
|
||||
getModelTreeItemsTotalCount(projectId, options)
|
||||
])
|
||||
items = filteredItems
|
||||
totalCount = filteredTotalCount
|
||||
} else {
|
||||
const [unfilteredItems, unfilteredTotalCount] = await Promise.all([
|
||||
getModelTreeItems(projectId, args, options),
|
||||
getModelTreeItemsTotalCount(projectId, options)
|
||||
])
|
||||
|
||||
items = unfilteredItems
|
||||
totalCount = unfilteredTotalCount
|
||||
items = unfilteredItems
|
||||
totalCount = unfilteredTotalCount
|
||||
}
|
||||
|
||||
const lastItem = last(items)
|
||||
return {
|
||||
items,
|
||||
totalCount,
|
||||
cursor: lastItem ? lastItem.updatedAt.toISOString() : null
|
||||
}
|
||||
}
|
||||
|
||||
const lastItem = last(items)
|
||||
return {
|
||||
items,
|
||||
totalCount,
|
||||
cursor: lastItem ? lastItem.updatedAt.toISOString() : null
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user