diff --git a/server/routers/siteResource/listAllSiteResourcesByOrg.ts b/server/routers/siteResource/listAllSiteResourcesByOrg.ts index c7099de40..ac243328c 100644 --- a/server/routers/siteResource/listAllSiteResourcesByOrg.ts +++ b/server/routers/siteResource/listAllSiteResourcesByOrg.ts @@ -1,4 +1,14 @@ -import { db, DB_TYPE, SiteResource, siteNetworks, siteResources, sites } from "@server/db"; +import { + db, + DB_TYPE, + Label, + SiteResource, + siteNetworks, + siteResourceLabels, + siteResources, + sites, + labels +} from "@server/db"; import response from "@server/lib/response"; import logger from "@server/logger"; import { OpenAPITags, registry } from "@server/openApi"; @@ -9,6 +19,8 @@ import { NextFunction, Request, Response } from "express"; import createHttpError from "http-errors"; import { z } from "zod"; import { fromError } from "zod-validation-error"; +import { isLicensedOrSubscribed } from "#dynamic/lib/isLicencedOrSubscribed"; +import { tierMatrix } from "@server/lib/billing/tierMatrix"; const listAllSiteResourcesByOrgParamsSchema = z.strictObject({ orgId: z.string() @@ -69,16 +81,11 @@ const listAllSiteResourcesByOrgQuerySchema = z.object({ default: "asc", description: "Sort order" }), - siteId: z.coerce - .number() - .int() - .positive() - .optional() - .openapi({ - type: "integer", - description: - "When set, only site resources associated with this site (via network) are returned" - }) + siteId: z.coerce.number().int().positive().optional().openapi({ + type: "integer", + description: + "When set, only site resources associated with this site (via network) are returned" + }) }); export type ListAllSiteResourcesByOrgResponse = PaginatedResponse<{ @@ -88,6 +95,7 @@ export type ListAllSiteResourcesByOrgResponse = PaginatedResponse<{ siteNames: string[]; siteNiceIds: string[]; siteAddresses: (string | null)[]; + labels?: Array>; })[]; }>; @@ -234,6 +242,11 @@ export async function listAllSiteResourcesByOrg( const { page, pageSize, query, mode, sort_by, order, siteId } = parsedQuery.data; + const isLabelFeatureEnabled = await isLicensedOrSubscribed( + orgId, + tierMatrix.labels + ); + const conditions = [and(eq(siteResources.orgId, orgId))]; if (siteId != null) { @@ -258,41 +271,41 @@ export async function listAllSiteResourcesByOrg( inArray(siteResources.siteResourceId, resourcesForSite) ); } - if (query) { - conditions.push( - or( - like( - sql`LOWER(${siteResources.name})`, - "%" + query.toLowerCase() + "%" - ), - like( - sql`LOWER(${siteResources.niceId})`, - "%" + query.toLowerCase() + "%" - ), - like( - sql`LOWER(${siteResources.destination})`, - "%" + query.toLowerCase() + "%" - ), - like( - sql`LOWER(${siteResources.alias})`, - "%" + query.toLowerCase() + "%" - ), - like( - sql`LOWER(${siteResources.aliasAddress})`, - "%" + query.toLowerCase() + "%" - ), - like( - sql`LOWER(${sites.name})`, - "%" + query.toLowerCase() + "%" - ) - ) - ); - } if (mode) { conditions.push(eq(siteResources.mode, mode)); } + if (query) { + const q = "%" + query.toLowerCase() + "%"; + const queryList = [ + like(sql`LOWER(${siteResources.name})`, q), + like(sql`LOWER(${siteResources.niceId})`, q), + like(sql`LOWER(${siteResources.destination})`, q), + like(sql`LOWER(${siteResources.alias})`, q), + like(sql`LOWER(${siteResources.aliasAddress})`, q), + like(sql`LOWER(${sites.name})`, q) + ]; + + if (isLabelFeatureEnabled) { + queryList.push( + inArray( + siteResources.siteResourceId, + db + .select({ id: siteResourceLabels.siteResourceId }) + .from(siteResourceLabels) + .innerJoin( + labels, + eq(labels.labelId, siteResourceLabels.labelId) + ) + .where(like(sql`LOWER(${labels.name})`, q)) + ) + ); + } + + conditions.push(or(...queryList)); + } + const baseQuery = querySiteResourcesBase().where(and(...conditions)); const countQuery = db.$count( @@ -315,11 +328,51 @@ export async function listAllSiteResourcesByOrg( countQuery ]); - const siteResourcesList = siteResourcesRaw.map(transformSiteResourceRow); + const siteResourcesList = siteResourcesRaw.map( + transformSiteResourceRow + ); + + const siteResourceIdList = siteResourcesList.map( + (r) => r.siteResourceId + ); + + let labelsForSiteResources: Array<{ + labelId: number; + name: string; + color: string; + siteResourceId: number; + }> = []; + + if (isLabelFeatureEnabled && siteResourceIdList.length > 0) { + labelsForSiteResources = await db + .select({ + labelId: labels.labelId, + name: labels.name, + color: labels.color, + siteResourceId: siteResourceLabels.siteResourceId + }) + .from(labels) + .innerJoin( + siteResourceLabels, + eq(siteResourceLabels.labelId, labels.labelId) + ) + .where( + inArray( + siteResourceLabels.siteResourceId, + siteResourceIdList + ) + ) + .orderBy(asc(siteResourceLabels.siteResourceLabelId)); + } return response(res, { data: { - siteResources: siteResourcesList, + siteResources: siteResourcesList.map((r) => ({ + ...r, + labels: labelsForSiteResources.filter( + (l) => l.siteResourceId === r.siteResourceId + ) + })), pagination: { total: totalCount, pageSize, @@ -340,4 +393,4 @@ export async function listAllSiteResourcesByOrg( ) ); } -} \ No newline at end of file +}