chore(server): migrating fully to ESM (#5042)
* wip * some extra fixes * stuff kinda works? * need to figure out mocks * need to figure out mocks * fix db listener * gqlgen fix * minor gqlgen watch adjustment * lint fixes * delete old codegen file * converting migrations to ESM * getModuleDIrectory * vitest sort of works * added back ts-vitest * resolve gql double load * fixing test timeout configs * TSC lint fix * fix automate tests * moar debugging * debugging * more debugging * codegen update * server works * yargs migrated * chore(server): getting rid of global mocks for Server ESM (#5046) * got rid of email mock * got rid of comment mocks * got rid of multi region mocks * got rid of stripe mock * admin override mock updated * removed final mock * fixing import.meta.resolve calls * another import.meta.resolve fix * added requested test * nyc ESM fix * removed unneeded deps + linting * yarn lock forgot to commit * tryna fix flakyness * email capture util fix * sendEmail fix * fix TSX check * sender transporter fix + CR comments * merge main fix * test fixx * circleci fix * gqlgen bigint fix * error formatter fix * more error formatting improvements * esmloader added to Dockerfile * more dockerfile fixes * bg jobs fix
This commit is contained in:
committed by
GitHub
parent
520e931211
commit
bde148f286
+1
-17
@@ -527,10 +527,6 @@ jobs:
|
||||
- run:
|
||||
command: cp .env.test-example .env.test
|
||||
working_directory: 'packages/server'
|
||||
- run:
|
||||
name: 'Lint'
|
||||
command: yarn lint:ci
|
||||
working_directory: 'packages/server'
|
||||
- run:
|
||||
name: 'Run tests'
|
||||
# Extra formatting to get timestamps on each line in CI (for profiling purposes)
|
||||
@@ -546,18 +542,6 @@ jobs:
|
||||
no_output_timeout: 30m
|
||||
- codecov/upload:
|
||||
files: packages/server/coverage/lcov.info
|
||||
- run:
|
||||
name: Introspect GQL schema for subsequent checks
|
||||
command: 'IGNORE_MISSING_MIGRATIONS=true yarn cli graphql introspect'
|
||||
working_directory: 'packages/server'
|
||||
- run:
|
||||
name: Checking for GQL schema breakages against app.speckle.systems
|
||||
command: 'yarn rover graph check Speckle-Server@app-speckle-systems --schema ./introspected-schema.graphql'
|
||||
working_directory: 'packages/server'
|
||||
- run:
|
||||
name: Checking for GQL schema breakages against latest.speckle.systems
|
||||
command: 'yarn rover graph check Speckle-Server@latest-speckle-systems --schema ./introspected-schema.graphql'
|
||||
working_directory: 'packages/server'
|
||||
- store_test_results:
|
||||
path: packages/server/reports
|
||||
|
||||
@@ -591,7 +575,7 @@ jobs:
|
||||
test-server-multiregion:
|
||||
<<: *test-server-job
|
||||
docker:
|
||||
- image: cimg/node:18.19.0
|
||||
- image: cimg/node:22.6.0
|
||||
- image: cimg/redis:7.2.4
|
||||
- image: 'speckle/speckle-postgres'
|
||||
environment:
|
||||
|
||||
@@ -78,9 +78,11 @@ bin/
|
||||
!packages/monitor-deployment/bin
|
||||
!packages/preview-service/bin
|
||||
!packages/server/bin
|
||||
!packages/server/modules/cli/bin
|
||||
!packages/viewer/src/modules/loaders/OBJ
|
||||
|
||||
# Server
|
||||
multiregion.json
|
||||
multiregion.test.json
|
||||
packages/*/.tshy/
|
||||
.vite-node
|
||||
+2
-1
@@ -95,7 +95,8 @@
|
||||
"typescript": "^5.7.3",
|
||||
"typescript-eslint": "^8.20.0",
|
||||
"wait-on": ">=7.2.0",
|
||||
"vitest": "^3.0.7"
|
||||
"vitest": "^3.0.7",
|
||||
"@types/node": "22.16.2"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import { CameraController, ViewMode, VisualDiffMode } from '@speckle/viewer'
|
||||
import type { NumericPropertyInfo } from '@speckle/viewer'
|
||||
import type { PartialDeep } from 'type-fest'
|
||||
import type { SectionBoxData } from '@speckle/shared/dist/esm/viewer/helpers/state.js'
|
||||
import type { SectionBoxData } from '@speckle/shared/viewer/state'
|
||||
|
||||
type SerializedViewerState = SpeckleViewer.ViewerState.SerializedViewerState
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ import {
|
||||
import { useSynchronizedCookie } from '~~/lib/common/composables/reactiveCookie'
|
||||
import { buildManualPromise } from '@speckle/ui-components'
|
||||
import { PassReader } from '../extensions/PassReader'
|
||||
import type { SectionBoxData } from '@speckle/shared/dist/esm/viewer/helpers/state.js'
|
||||
import type { SectionBoxData } from '@speckle/shared/viewer/state'
|
||||
|
||||
export type LoadedModel = NonNullable<
|
||||
Get<ViewerLoadedResourcesQuery, 'project.models.items[0]'>
|
||||
|
||||
@@ -52,7 +52,7 @@ import {
|
||||
import { setupDebugMode } from '~~/lib/viewer/composables/setup/dev'
|
||||
import { useEmbed } from '~/lib/viewer/composables/setup/embed'
|
||||
import { useMixpanel } from '~~/lib/core/composables/mp'
|
||||
import type { SectionBoxData } from '@speckle/shared/dist/esm/viewer/helpers/state.js'
|
||||
import type { SectionBoxData } from '@speckle/shared/viewer/state'
|
||||
|
||||
function useViewerIsBusyEventHandler() {
|
||||
const state = useInjectedViewerState()
|
||||
|
||||
@@ -73,6 +73,8 @@ COPY --link --from=dependency-stage /speckle-server/node_modules ./node_modules
|
||||
|
||||
WORKDIR /speckle-server/packages/server
|
||||
COPY --link --from=build-stage /speckle-server/packages/server/package.json ./package.json
|
||||
COPY --link --from=build-stage /speckle-server/packages/server/esmLoader.js ./esmLoader.js
|
||||
COPY --link --from=build-stage /speckle-server/packages/server/root.js ./root.js
|
||||
COPY --link --from=build-stage /speckle-server/packages/server/dist ./dist
|
||||
COPY --link --from=build-stage /speckle-server/packages/server/assets ./assets
|
||||
COPY --link --from=build-stage /speckle-server/packages/server/bin ./bin
|
||||
@@ -86,4 +88,4 @@ ARG NODE_ENV
|
||||
ENV NODE_ENV=${NODE_ENV} \
|
||||
SPECKLE_SERVER_VERSION=${SPECKLE_SERVER_VERSION}
|
||||
|
||||
ENTRYPOINT [ "tini", "--", "/nodejs/bin/node", "./bin/www" ]
|
||||
ENTRYPOINT [ "tini", "--", "/nodejs/bin/node", "--import=./esmLoader.js", "./bin/www" ]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable camelcase */
|
||||
/* istanbul ignore file */
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import './bootstrap'
|
||||
import './bootstrap.js'
|
||||
import http from 'http'
|
||||
import express, { Express } from 'express'
|
||||
|
||||
@@ -54,7 +54,7 @@ import {
|
||||
import * as ModulesSetup from '@/modules/index'
|
||||
import { GraphQLContext, Optional } from '@/modules/shared/helpers/typeHelper'
|
||||
|
||||
import { get, has, isString } from 'lodash'
|
||||
import { get, has, isString } from 'lodash-es'
|
||||
import { corsMiddlewareFactory } from '@/modules/core/configs/cors'
|
||||
import {
|
||||
authContextMiddleware,
|
||||
@@ -103,17 +103,17 @@ const isWsServer = (server: http.Server | MockWsServer): server is MockWsServer
|
||||
* is that graphql-ws uses an entirely different protocol, so the client-side has to change as well, and so old clients
|
||||
* will be unable to use any WebSocket/subscriptions functionality with the updated server
|
||||
*/
|
||||
export function buildApolloSubscriptionServer(params: {
|
||||
export async function buildApolloSubscriptionServer(params: {
|
||||
server: http.Server | MockWsServer
|
||||
registers?: Registry[]
|
||||
}): SubscriptionServer {
|
||||
}): Promise<SubscriptionServer> {
|
||||
const { server, registers } = params
|
||||
const httpServer = isWsServer(server) ? undefined : server
|
||||
const mockServer = isWsServer(server) ? server : undefined
|
||||
|
||||
// we have to break the type here, cause its a mock
|
||||
const wsServer = mockServer ? (mockServer as unknown as ws.Server) : undefined
|
||||
const schema = ModulesSetup.graphSchema()
|
||||
const schema = await ModulesSetup.graphSchema()
|
||||
|
||||
const {
|
||||
metricConnectCounter,
|
||||
@@ -250,7 +250,7 @@ export async function buildApolloServer(options?: {
|
||||
}): Promise<ApolloServer<GraphQLContext>> {
|
||||
const includeStacktraceInErrorResponses = isDevEnv() || isTestEnv()
|
||||
const subscriptionServer = options?.subscriptionServer
|
||||
const schema = ModulesSetup.graphSchema(await buildMocksConfig())
|
||||
const schema = await ModulesSetup.graphSchema(await buildMocksConfig())
|
||||
|
||||
const server = new ApolloServer({
|
||||
schema,
|
||||
@@ -356,7 +356,7 @@ export async function init() {
|
||||
|
||||
// Init HTTP server & subscription server
|
||||
const server = http.createServer(app)
|
||||
const subscriptionServer = buildApolloSubscriptionServer({
|
||||
const subscriptionServer = await buildApolloSubscriptionServer({
|
||||
server,
|
||||
registers: [promRegister]
|
||||
})
|
||||
@@ -398,8 +398,7 @@ const shouldUseFrontendProxy = () => isDevEnv()
|
||||
async function createFrontendProxy() {
|
||||
const frontendHost = process.env.FRONTEND_HOST || '127.0.0.1'
|
||||
const frontendPort = process.env.FRONTEND_PORT || 8081
|
||||
const { createProxyMiddleware } =
|
||||
require('http-proxy-middleware') as typeof import('http-proxy-middleware')
|
||||
const { createProxyMiddleware } = await import('http-proxy-middleware')
|
||||
|
||||
// even tho it has default values, it fixes http-proxy setting `Connection: close` on each request
|
||||
// slowing everything down
|
||||
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env node
|
||||
import path from 'path'
|
||||
|
||||
/**
|
||||
* Find gqlgen and run it (we don't want to hardcode a specific node_modules path).
|
||||
* We use this so that we can pass in specific flags to node before it even begins to run
|
||||
*/
|
||||
const relativeBinPath = './bin.js'
|
||||
|
||||
const mochaPath = import.meta.resolve('@graphql-codegen/cli')
|
||||
const mochaPathDir = path.dirname(mochaPath)
|
||||
const mochaBinPath = path.join(mochaPathDir, relativeBinPath)
|
||||
await import(mochaBinPath)
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict'
|
||||
const path = require('path')
|
||||
import path from 'path'
|
||||
|
||||
/**
|
||||
* Find mocha and run it (we don't want to hardcode a specific node_modules path).
|
||||
@@ -8,7 +7,7 @@ const path = require('path')
|
||||
*/
|
||||
const relativeBinPath = './bin/mocha.js'
|
||||
|
||||
const mochaPath = require.resolve('mocha')
|
||||
const mochaPath = import.meta.resolve('mocha')
|
||||
const mochaPathDir = path.dirname(mochaPath)
|
||||
const mochaBinPath = path.join(mochaPathDir, relativeBinPath)
|
||||
require(mochaBinPath)
|
||||
await import(mochaBinPath)
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Same as 'www', but runs the app from source code directly through ts-node, so no need to build the app into /dist first.
|
||||
* Although ts-node with swc is pretty fast, in production environments you should use `www` and a built app.
|
||||
*/
|
||||
|
||||
require('ts-node/register')
|
||||
const { logger } = require('../observability/logging')
|
||||
const { init, startHttp } = require('../app')
|
||||
|
||||
init()
|
||||
.then(({ app, graphqlServer, registers, server, readinessCheck }) =>
|
||||
startHttp({ app, graphqlServer, registers, server, readinessCheck })
|
||||
)
|
||||
.catch((err) => {
|
||||
logger.error(err, 'Failed to start server. Exiting with non-zero exit code...')
|
||||
|
||||
// kill it with fire 🔥
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
// 💥
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
'use strict'
|
||||
|
||||
const { logger } = require('../dist/observability/logging')
|
||||
const { init, startHttp } = require('../dist/app')
|
||||
import { logger } from '../dist/observability/logging.js'
|
||||
import { init, startHttp } from '../dist/app.js'
|
||||
|
||||
init()
|
||||
.then(({ app, graphqlServer, registers, server, readinessCheck }) =>
|
||||
|
||||
Vendored
+16
-33
@@ -1,38 +1,28 @@
|
||||
/* istanbul ignore file */
|
||||
import dotenv from 'dotenv'
|
||||
import {
|
||||
isTestEnv,
|
||||
isDevEnv,
|
||||
isApolloMonitoringEnabled,
|
||||
getApolloServerVersion,
|
||||
getServerVersion
|
||||
} from '@/modules/shared/helpers/envHelper'
|
||||
import { logger } from '@/observability/logging'
|
||||
import { initOpenTelemetry } from '@/observability/otel'
|
||||
import { patchKnex } from '@/modules/core/patches/knex'
|
||||
import { appRoot, packageRoot } from '#/root.js'
|
||||
import inspector from 'node:inspector'
|
||||
|
||||
/**
|
||||
* Bootstrap module that should be imported at the very top of each entry point module
|
||||
*/
|
||||
|
||||
// Conditionally change appRoot and packageRoot according to whether we're running from /dist/ or not (ts-node)
|
||||
const path = require('path')
|
||||
const isTsNode = !!process[Symbol.for('ts-node.register.instance')]
|
||||
const appRoot = __dirname
|
||||
const packageRoot = isTsNode ? appRoot : path.resolve(__dirname, '../')
|
||||
|
||||
// Initializing module aliases for absolute import paths
|
||||
const moduleAlias = require('module-alias')
|
||||
moduleAlias.addAliases({
|
||||
'@': appRoot,
|
||||
'#': packageRoot
|
||||
})
|
||||
|
||||
// Initializing env vars
|
||||
const dotenv = require('dotenv')
|
||||
const {
|
||||
isTestEnv,
|
||||
isApolloMonitoringEnabled,
|
||||
getApolloServerVersion,
|
||||
getServerVersion,
|
||||
isDevEnv
|
||||
} = require('@/modules/shared/helpers/envHelper')
|
||||
const { logger } = require('@/observability/logging')
|
||||
|
||||
if (isApolloMonitoringEnabled() && !getApolloServerVersion()) {
|
||||
process.env.APOLLO_SERVER_USER_VERSION = getServerVersion()
|
||||
}
|
||||
|
||||
// If running in test env, load .env.test first
|
||||
// (appRoot necessary, cause env files aren't loaded through require() calls)
|
||||
// (appRoot necessary, cause env files aren't loaded through require()/import() calls)
|
||||
if (isTestEnv()) {
|
||||
const { error } = dotenv.config({ path: `${packageRoot}/.env.test` })
|
||||
if (error) {
|
||||
@@ -48,7 +38,6 @@ if (isTestEnv()) {
|
||||
// (e.g. due to various child processes capturing the --inspect flag)
|
||||
const startDebugger = process.env.START_DEBUGGER
|
||||
if ((isTestEnv() || isDevEnv()) && startDebugger) {
|
||||
const inspector = require('node:inspector')
|
||||
if (!inspector.url()) {
|
||||
console.log('Debugger starting on process ' + process.pid)
|
||||
inspector.open(0, undefined, true)
|
||||
@@ -58,13 +47,7 @@ if ((isTestEnv() || isDevEnv()) && startDebugger) {
|
||||
dotenv.config({ path: `${packageRoot}/.env` })
|
||||
|
||||
// knex is a singleton controlled by module so can't wait til app init
|
||||
const { initOpenTelemetry } = require('@/observability/otel')
|
||||
initOpenTelemetry()
|
||||
|
||||
const { patchKnex } = require('@/modules/core/patches/knex')
|
||||
patchKnex()
|
||||
|
||||
module.exports = {
|
||||
appRoot,
|
||||
packageRoot
|
||||
}
|
||||
export { appRoot, packageRoot }
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
import type { CodegenConfig } from '@graphql-codegen/cli'
|
||||
|
||||
const config: CodegenConfig = {
|
||||
schema: ['modules/core/graph/schema.ts'],
|
||||
overwrite: true,
|
||||
documents: undefined,
|
||||
generates: {
|
||||
'modules/core/graph/generated/graphql.ts': {
|
||||
plugins: ['typescript', 'typescript-resolvers'],
|
||||
config: {
|
||||
enumsAsConst: true,
|
||||
contextType: '@/modules/shared/helpers/typeHelper#GraphQLContext',
|
||||
mappers: {
|
||||
Stream: '@/modules/core/helpers/graphTypes#StreamGraphQLReturn',
|
||||
Commit: '@/modules/core/helpers/graphTypes#CommitGraphQLReturn',
|
||||
Project: '@/modules/core/helpers/graphTypes#ProjectGraphQLReturn',
|
||||
Object: '@/modules/core/helpers/graphTypes#ObjectGraphQLReturn',
|
||||
Version: '@/modules/core/helpers/graphTypes#VersionGraphQLReturn',
|
||||
ServerInvite:
|
||||
'@/modules/core/helpers/graphTypes#ServerInviteGraphQLReturnType',
|
||||
Model: '@/modules/core/helpers/graphTypes#ModelGraphQLReturn',
|
||||
ModelsTreeItem:
|
||||
'@/modules/core/helpers/graphTypes#ModelsTreeItemGraphQLReturn',
|
||||
StreamAccessRequest:
|
||||
'@/modules/accessrequests/helpers/graphTypes#StreamAccessRequestGraphQLReturn',
|
||||
ProjectAccessRequest:
|
||||
'@/modules/accessrequests/helpers/graphTypes#ProjectAccessRequestGraphQLReturn',
|
||||
ProjectAccessRequestMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
LimitedUser: '@/modules/core/helpers/graphTypes#LimitedUserGraphQLReturn',
|
||||
User: '@/modules/core/helpers/graphTypes#UserGraphQLReturn',
|
||||
EmbedToken: '@/modules/core/helpers/graphTypes#EmbedTokenGraphQLReturn',
|
||||
ActiveUserMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
UserMetaMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
UserEmailMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
ProjectMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
ProjectInviteMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
ModelMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
VersionMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
FileUploadMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
CommentMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
AutomateMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
AdminMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
AdminQueries: '@/modules/core/helpers/graphTypes#GraphQLEmptyReturn',
|
||||
ServerStatistics: '@/modules/core/helpers/graphTypes#GraphQLEmptyReturn',
|
||||
ServerStats: '@/modules/core/helpers/graphTypes#GraphQLEmptyReturn',
|
||||
CommentReplyAuthorCollection:
|
||||
'@/modules/comments/helpers/graphTypes#CommentReplyAuthorCollectionGraphQLReturn',
|
||||
Comment: '@/modules/comments/helpers/graphTypes#CommentGraphQLReturn',
|
||||
CommentPermissionChecks:
|
||||
'@/modules/comments/helpers/graphTypes#CommentPermissionChecksGraphQLReturn',
|
||||
PendingStreamCollaborator:
|
||||
'@/modules/serverinvites/helpers/graphTypes#PendingStreamCollaboratorGraphQLReturn',
|
||||
StreamCollaborator:
|
||||
'@/modules/core/helpers/graphTypes#StreamCollaboratorGraphQLReturn',
|
||||
ProjectCollaborator:
|
||||
'@/modules/core/helpers/graphTypes#ProjectCollaboratorGraphQLReturn',
|
||||
FileUpload: '@/modules/fileuploads/helpers/types#FileUploadGraphQLReturn',
|
||||
AutomateFunction:
|
||||
'@/modules/automate/helpers/graphTypes#AutomateFunctionGraphQLReturn',
|
||||
AutomateFunctionRelease:
|
||||
'@/modules/automate/helpers/graphTypes#AutomateFunctionReleaseGraphQLReturn',
|
||||
Automation: '@/modules/automate/helpers/graphTypes#AutomationGraphQLReturn',
|
||||
AutomationPermissionChecks:
|
||||
'@/modules/automate/helpers/graphTypes#AutomationPermissionChecksGraphQLReturn',
|
||||
AutomationRevision:
|
||||
'@/modules/automate/helpers/graphTypes#AutomationRevisionGraphQLReturn',
|
||||
AutomationRevisionFunction:
|
||||
'@/modules/automate/helpers/graphTypes#AutomationRevisionFunctionGraphQLReturn',
|
||||
AutomateRun: '@/modules/automate/helpers/graphTypes#AutomateRunGraphQLReturn',
|
||||
AutomationRunTrigger:
|
||||
'@/modules/automate/helpers/graphTypes#AutomationRunTriggerGraphQLReturn',
|
||||
VersionCreatedTrigger:
|
||||
'@/modules/automate/helpers/graphTypes#AutomationRunTriggerGraphQLReturn',
|
||||
AutomationRevisionTriggerDefinition:
|
||||
'@/modules/automate/helpers/graphTypes#AutomationRevisionTriggerDefinitionGraphQLReturn',
|
||||
VersionCreatedTriggerDefinition:
|
||||
'@/modules/automate/helpers/graphTypes#AutomationRevisionTriggerDefinitionGraphQLReturn',
|
||||
AutomateFunctionRun:
|
||||
'@/modules/automate/helpers/graphTypes#AutomateFunctionRunGraphQLReturn',
|
||||
TriggeredAutomationsStatus:
|
||||
'@/modules/automate/helpers/graphTypes#TriggeredAutomationsStatusGraphQLReturn',
|
||||
ProjectAutomationMutations:
|
||||
'@/modules/automate/helpers/graphTypes#ProjectAutomationMutationsGraphQLReturn',
|
||||
ProjectTriggeredAutomationsStatusUpdatedMessage:
|
||||
'@/modules/automate/helpers/graphTypes#ProjectTriggeredAutomationsStatusUpdatedMessageGraphQLReturn',
|
||||
ProjectAutomationsUpdatedMessage:
|
||||
'@/modules/automate/helpers/graphTypes#ProjectAutomationsUpdatedMessageGraphQLReturn',
|
||||
UserAutomateInfo:
|
||||
'@/modules/automate/helpers/graphTypes#UserAutomateInfoGraphQLReturn',
|
||||
Workspace:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#WorkspaceGraphQLReturn',
|
||||
WorkspaceSso:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#WorkspaceSsoGraphQLReturn',
|
||||
WorkspaceMutations:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#WorkspaceMutationsGraphQLReturn',
|
||||
WorkspaceJoinRequestMutations:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#WorkspaceJoinRequestMutationsGraphQLReturn',
|
||||
WorkspaceInviteMutations:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#WorkspaceInviteMutationsGraphQLReturn',
|
||||
WorkspacePlan:
|
||||
'@/modules/gatekeeperCore/helpers/graphTypes#WorkspacePlanGraphQLReturn',
|
||||
WorkspacePlanUsage:
|
||||
'@/modules/gatekeeperCore/helpers/graphTypes#WorkspacePlanUsageGraphQLReturn',
|
||||
WorkspaceProjectMutations:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#WorkspaceProjectMutationsGraphQLReturn',
|
||||
WorkspaceBillingMutations:
|
||||
'@/modules/gatekeeper/helpers/graphTypes#WorkspaceBillingMutationsGraphQLReturn',
|
||||
PendingWorkspaceCollaborator:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#PendingWorkspaceCollaboratorGraphQLReturn',
|
||||
WorkspaceCollaborator:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#WorkspaceCollaboratorGraphQLReturn',
|
||||
LimitedWorkspace:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#LimitedWorkspaceGraphQLReturn',
|
||||
LimitedWorkspaceCollaborator:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#LimitedWorkspaceCollaboratorGraphQLReturn',
|
||||
WorkspaceSubscriptionSeats:
|
||||
'@/modules/gatekeeper/helpers/graphTypes#WorkspaceSubscriptionSeatsGraphQLReturn',
|
||||
WorkspaceJoinRequest:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#WorkspaceJoinRequestGraphQLReturn',
|
||||
LimitedWorkspaceJoinRequest:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#LimitedWorkspaceJoinRequestGraphQLReturn',
|
||||
ProjectMoveToWorkspaceDryRun:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#ProjectMoveToWorkspaceDryRunGraphQLReturn',
|
||||
Webhook: '@/modules/webhooks/helpers/graphTypes#WebhookGraphQLReturn',
|
||||
SmartTextEditorValue:
|
||||
'@/modules/core/services/richTextEditorService#SmartTextEditorValueGraphQLReturn',
|
||||
BlobMetadata: '@/modules/blobstorage/domain/types#BlobStorageItem',
|
||||
ServerWorkspacesInfo: '@/modules/core/helpers/graphTypes#GraphQLEmptyReturn',
|
||||
ActivityCollection:
|
||||
'@/modules/activitystream/helpers/graphTypes#ActivityCollectionGraphQLReturn',
|
||||
ProjectRole:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#ProjectRoleGraphQLReturn',
|
||||
ServerApp: '@/modules/auth/helpers/graphTypes#ServerAppGraphQLReturn',
|
||||
ServerAppListItem:
|
||||
'@/modules/auth/helpers/graphTypes#ServerAppListItemGraphQLReturn',
|
||||
ServerInfo: '@/modules/core/helpers/graphTypes#ServerInfoGraphQLReturn',
|
||||
Branch: '@/modules/core/helpers/graphTypes#BranchGraphQLReturn',
|
||||
GendoAIRender:
|
||||
'@/modules/gendo/helpers/types/graphTypes#GendoAIRenderGraphQLReturn',
|
||||
ServerMultiRegionConfiguration:
|
||||
'@/modules/core/helpers/graphTypes#GraphQLEmptyReturn',
|
||||
ServerInfoMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
ServerRegionMutations:
|
||||
'@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn',
|
||||
ServerRegionItem:
|
||||
'@/modules/multiregion/helpers/graphTypes#ServerRegionItemGraphQLReturn',
|
||||
Price: '@/modules/gatekeeperCore/helpers/graphTypes#PriceGraphQLReturn',
|
||||
WorkspaceSubscription:
|
||||
'@/modules/gatekeeper/helpers/graphTypes#WorkspaceSubscriptionGraphQLReturn',
|
||||
UserMeta: '@/modules/core/helpers/graphTypes#UserMetaGraphQLReturn',
|
||||
ProjectPermissionChecks:
|
||||
'@/modules/core/helpers/graphTypes#ProjectPermissionChecksGraphQLReturn',
|
||||
ModelPermissionChecks:
|
||||
'@/modules/core/helpers/graphTypes#ModelPermissionChecksGraphQLReturn',
|
||||
VersionPermissionChecks:
|
||||
'@/modules/core/helpers/graphTypes#VersionPermissionChecksGraphQLReturn',
|
||||
RootPermissionChecks:
|
||||
'@/modules/core/helpers/graphTypes#RootPermissionChecksGraphQLReturn',
|
||||
WorkspacePermissionChecks:
|
||||
'@/modules/workspacesCore/helpers/graphTypes#WorkspacePermissionChecksGraphQLReturn'
|
||||
}
|
||||
}
|
||||
},
|
||||
'modules/cross-server-sync/graph/generated/graphql.ts': {
|
||||
plugins: ['typescript', 'typescript-operations'],
|
||||
documents: ['modules/cross-server-sync/**/*.{js,ts}'],
|
||||
config: {
|
||||
enumsAsConst: true,
|
||||
scalars: {
|
||||
JSONObject: 'Record<string, unknown>',
|
||||
DateTime: 'string'
|
||||
}
|
||||
}
|
||||
},
|
||||
'test/graphql/generated/graphql.ts': {
|
||||
plugins: ['typescript', 'typescript-operations', 'typed-document-node'],
|
||||
documents: [
|
||||
'test/graphql/*.{js,ts}',
|
||||
'modules/**/tests/helpers/graphql.ts',
|
||||
'modules/**/tests/helpers/*Graphql.ts',
|
||||
'modules/**/tests/helpers/graphql/*.ts'
|
||||
],
|
||||
config: {
|
||||
enumsAsConst: true,
|
||||
scalars: {
|
||||
JSONObject: 'Record<string, unknown>',
|
||||
DateTime: 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
config: {
|
||||
enumsAsConst: true,
|
||||
scalars: {
|
||||
JSONObject: 'Record<string, unknown>',
|
||||
DateTime: 'Date'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default config
|
||||
@@ -1,139 +0,0 @@
|
||||
overwrite: true
|
||||
schema:
|
||||
- 'modules/schema.ts'
|
||||
documents: null
|
||||
generates:
|
||||
modules/core/graph/generated/graphql.ts:
|
||||
plugins:
|
||||
- 'typescript'
|
||||
- 'typescript-resolvers'
|
||||
config:
|
||||
enumsAsConst: true
|
||||
contextType: '@/modules/shared/helpers/typeHelper#GraphQLContext'
|
||||
mappers:
|
||||
Stream: '@/modules/core/helpers/graphTypes#StreamGraphQLReturn'
|
||||
Commit: '@/modules/core/helpers/graphTypes#CommitGraphQLReturn'
|
||||
Project: '@/modules/core/helpers/graphTypes#ProjectGraphQLReturn'
|
||||
Object: '@/modules/core/helpers/graphTypes#ObjectGraphQLReturn'
|
||||
Version: '@/modules/core/helpers/graphTypes#VersionGraphQLReturn'
|
||||
ServerInvite: '@/modules/core/helpers/graphTypes#ServerInviteGraphQLReturnType'
|
||||
Model: '@/modules/core/helpers/graphTypes#ModelGraphQLReturn'
|
||||
ModelsTreeItem: '@/modules/core/helpers/graphTypes#ModelsTreeItemGraphQLReturn'
|
||||
StreamAccessRequest: '@/modules/accessrequests/helpers/graphTypes#StreamAccessRequestGraphQLReturn'
|
||||
ProjectAccessRequest: '@/modules/accessrequests/helpers/graphTypes#ProjectAccessRequestGraphQLReturn'
|
||||
ProjectAccessRequestMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
LimitedUser: '@/modules/core/helpers/graphTypes#LimitedUserGraphQLReturn'
|
||||
User: '@/modules/core/helpers/graphTypes#UserGraphQLReturn'
|
||||
EmbedToken: '@/modules/core/helpers/graphTypes#EmbedTokenGraphQLReturn'
|
||||
ActiveUserMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
UserMetaMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
UserEmailMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
ProjectMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
ProjectInviteMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
ModelMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
VersionMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
FileUploadMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
CommentMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
AutomateMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
AdminMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
AdminQueries: '@/modules/core/helpers/graphTypes#GraphQLEmptyReturn'
|
||||
ServerStatistics: '@/modules/core/helpers/graphTypes#GraphQLEmptyReturn'
|
||||
ServerStats: '@/modules/core/helpers/graphTypes#GraphQLEmptyReturn'
|
||||
CommentReplyAuthorCollection: '@/modules/comments/helpers/graphTypes#CommentReplyAuthorCollectionGraphQLReturn'
|
||||
Comment: '@/modules/comments/helpers/graphTypes#CommentGraphQLReturn'
|
||||
CommentPermissionChecks: '@/modules/comments/helpers/graphTypes#CommentPermissionChecksGraphQLReturn'
|
||||
PendingStreamCollaborator: '@/modules/serverinvites/helpers/graphTypes#PendingStreamCollaboratorGraphQLReturn'
|
||||
StreamCollaborator: '@/modules/core/helpers/graphTypes#StreamCollaboratorGraphQLReturn'
|
||||
ProjectCollaborator: '@/modules/core/helpers/graphTypes#ProjectCollaboratorGraphQLReturn'
|
||||
FileUpload: '@/modules/fileuploads/helpers/types#FileUploadGraphQLReturn'
|
||||
AutomateFunction: '@/modules/automate/helpers/graphTypes#AutomateFunctionGraphQLReturn'
|
||||
AutomateFunctionRelease: '@/modules/automate/helpers/graphTypes#AutomateFunctionReleaseGraphQLReturn'
|
||||
Automation: '@/modules/automate/helpers/graphTypes#AutomationGraphQLReturn'
|
||||
AutomationPermissionChecks: '@/modules/automate/helpers/graphTypes#AutomationPermissionChecksGraphQLReturn'
|
||||
AutomationRevision: '@/modules/automate/helpers/graphTypes#AutomationRevisionGraphQLReturn'
|
||||
AutomationRevisionFunction: '@/modules/automate/helpers/graphTypes#AutomationRevisionFunctionGraphQLReturn'
|
||||
AutomateRun: '@/modules/automate/helpers/graphTypes#AutomateRunGraphQLReturn'
|
||||
AutomationRunTrigger: '@/modules/automate/helpers/graphTypes#AutomationRunTriggerGraphQLReturn'
|
||||
VersionCreatedTrigger: '@/modules/automate/helpers/graphTypes#AutomationRunTriggerGraphQLReturn'
|
||||
AutomationRevisionTriggerDefinition: '@/modules/automate/helpers/graphTypes#AutomationRevisionTriggerDefinitionGraphQLReturn'
|
||||
VersionCreatedTriggerDefinition: '@/modules/automate/helpers/graphTypes#AutomationRevisionTriggerDefinitionGraphQLReturn'
|
||||
AutomateFunctionRun: '@/modules/automate/helpers/graphTypes#AutomateFunctionRunGraphQLReturn'
|
||||
TriggeredAutomationsStatus: '@/modules/automate/helpers/graphTypes#TriggeredAutomationsStatusGraphQLReturn'
|
||||
ProjectAutomationMutations: '@/modules/automate/helpers/graphTypes#ProjectAutomationMutationsGraphQLReturn'
|
||||
ProjectTriggeredAutomationsStatusUpdatedMessage: '@/modules/automate/helpers/graphTypes#ProjectTriggeredAutomationsStatusUpdatedMessageGraphQLReturn'
|
||||
ProjectAutomationsUpdatedMessage: '@/modules/automate/helpers/graphTypes#ProjectAutomationsUpdatedMessageGraphQLReturn'
|
||||
UserAutomateInfo: '@/modules/automate/helpers/graphTypes#UserAutomateInfoGraphQLReturn'
|
||||
Workspace: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceGraphQLReturn'
|
||||
WorkspaceSso: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceSsoGraphQLReturn'
|
||||
WorkspaceMutations: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceMutationsGraphQLReturn'
|
||||
WorkspaceJoinRequestMutations: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceJoinRequestMutationsGraphQLReturn'
|
||||
WorkspaceInviteMutations: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceInviteMutationsGraphQLReturn'
|
||||
WorkspacePlan: '@/modules/gatekeeperCore/helpers/graphTypes#WorkspacePlanGraphQLReturn'
|
||||
WorkspacePlanUsage: '@/modules/gatekeeperCore/helpers/graphTypes#WorkspacePlanUsageGraphQLReturn'
|
||||
WorkspaceProjectMutations: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceProjectMutationsGraphQLReturn'
|
||||
WorkspaceBillingMutations: '@/modules/gatekeeper/helpers/graphTypes#WorkspaceBillingMutationsGraphQLReturn'
|
||||
PendingWorkspaceCollaborator: '@/modules/workspacesCore/helpers/graphTypes#PendingWorkspaceCollaboratorGraphQLReturn'
|
||||
WorkspaceCollaborator: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceCollaboratorGraphQLReturn'
|
||||
LimitedWorkspace: '@/modules/workspacesCore/helpers/graphTypes#LimitedWorkspaceGraphQLReturn'
|
||||
LimitedWorkspaceCollaborator: '@/modules/workspacesCore/helpers/graphTypes#LimitedWorkspaceCollaboratorGraphQLReturn'
|
||||
WorkspaceSubscriptionSeats: '@/modules/gatekeeper/helpers/graphTypes#WorkspaceSubscriptionSeatsGraphQLReturn'
|
||||
WorkspaceJoinRequest: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceJoinRequestGraphQLReturn'
|
||||
LimitedWorkspaceJoinRequest: '@/modules/workspacesCore/helpers/graphTypes#LimitedWorkspaceJoinRequestGraphQLReturn'
|
||||
ProjectMoveToWorkspaceDryRun: '@/modules/workspacesCore/helpers/graphTypes#ProjectMoveToWorkspaceDryRunGraphQLReturn'
|
||||
Webhook: '@/modules/webhooks/helpers/graphTypes#WebhookGraphQLReturn'
|
||||
SmartTextEditorValue: '@/modules/core/services/richTextEditorService#SmartTextEditorValueGraphQLReturn'
|
||||
BlobMetadata: '@/modules/blobstorage/domain/types#BlobStorageItem'
|
||||
ServerWorkspacesInfo: '@/modules/core/helpers/graphTypes#GraphQLEmptyReturn'
|
||||
ActivityCollection: '@/modules/activitystream/helpers/graphTypes#ActivityCollectionGraphQLReturn'
|
||||
ProjectRole: '@/modules/workspacesCore/helpers/graphTypes#ProjectRoleGraphQLReturn'
|
||||
ServerApp: '@/modules/auth/helpers/graphTypes#ServerAppGraphQLReturn'
|
||||
ServerAppListItem: '@/modules/auth/helpers/graphTypes#ServerAppListItemGraphQLReturn'
|
||||
ServerInfo: '@/modules/core/helpers/graphTypes#ServerInfoGraphQLReturn'
|
||||
Branch: '@/modules/core/helpers/graphTypes#BranchGraphQLReturn'
|
||||
GendoAIRender: '@/modules/gendo/helpers/types/graphTypes#GendoAIRenderGraphQLReturn'
|
||||
ServerMultiRegionConfiguration: '@/modules/core/helpers/graphTypes#GraphQLEmptyReturn'
|
||||
ServerInfoMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
ServerRegionMutations: '@/modules/core/helpers/graphTypes#MutationsObjectGraphQLReturn'
|
||||
ServerRegionItem: '@/modules/multiregion/helpers/graphTypes#ServerRegionItemGraphQLReturn'
|
||||
Price: '@/modules/gatekeeperCore/helpers/graphTypes#PriceGraphQLReturn'
|
||||
WorkspaceSubscription: '@/modules/gatekeeper/helpers/graphTypes#WorkspaceSubscriptionGraphQLReturn'
|
||||
UserMeta: '@/modules/core/helpers/graphTypes#UserMetaGraphQLReturn'
|
||||
ProjectPermissionChecks: '@/modules/core/helpers/graphTypes#ProjectPermissionChecksGraphQLReturn'
|
||||
ModelPermissionChecks: '@/modules/core/helpers/graphTypes#ModelPermissionChecksGraphQLReturn'
|
||||
VersionPermissionChecks: '@/modules/core/helpers/graphTypes#VersionPermissionChecksGraphQLReturn'
|
||||
RootPermissionChecks: '@/modules/core/helpers/graphTypes#RootPermissionChecksGraphQLReturn'
|
||||
WorkspacePermissionChecks: '@/modules/workspacesCore/helpers/graphTypes#WorkspacePermissionChecksGraphQLReturn'
|
||||
modules/cross-server-sync/graph/generated/graphql.ts:
|
||||
plugins:
|
||||
- 'typescript'
|
||||
- 'typescript-operations'
|
||||
documents:
|
||||
- 'modules/cross-server-sync/**/*.{js,ts}'
|
||||
config:
|
||||
enumsAsConst: true
|
||||
scalars:
|
||||
JSONObject: Record<string, unknown>
|
||||
DateTime: string
|
||||
test/graphql/generated/graphql.ts:
|
||||
plugins:
|
||||
- 'typescript'
|
||||
- 'typescript-operations'
|
||||
- 'typed-document-node'
|
||||
documents:
|
||||
- 'test/graphql/*.{js,ts}'
|
||||
- 'modules/**/tests/helpers/graphql.ts'
|
||||
- 'modules/**/tests/helpers/*Graphql.ts'
|
||||
- 'modules/**/tests/helpers/graphql/*.ts'
|
||||
config:
|
||||
enumsAsConst: true
|
||||
scalars:
|
||||
JSONObject: Record<string, unknown>
|
||||
DateTime: string
|
||||
config:
|
||||
enumsAsConst: true
|
||||
scalars:
|
||||
JSONObject: Record<string, unknown>
|
||||
DateTime: Date
|
||||
require:
|
||||
- ts-node/register
|
||||
- tsconfig-paths/register
|
||||
@@ -13,24 +13,24 @@ const configs = [
|
||||
...baseConfigs,
|
||||
{
|
||||
languageOptions: {
|
||||
sourceType: 'commonjs',
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
...globals.node
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
files: ['**/*.mjs'],
|
||||
files: ['**/*.cjs', '**/*.cts'],
|
||||
languageOptions: {
|
||||
sourceType: 'module'
|
||||
sourceType: 'commonjs'
|
||||
}
|
||||
},
|
||||
...tseslint.configs.recommendedTypeChecked.map((c) => ({
|
||||
...c,
|
||||
files: [...(c.files || []), '**/*.ts', '**/*.d.ts']
|
||||
files: [...(c.files || []), '**/*.ts', '**/*.d.ts', '**/*.cts']
|
||||
})),
|
||||
{
|
||||
files: ['**/*.ts', '**/*.d.ts'],
|
||||
files: ['**/*.ts', '**/*.d.ts', '**/*.cts'],
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
tsconfigRootDir: getESMDirname(import.meta.url),
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import path from 'node:path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import { register } from 'node:module'
|
||||
import { appRoot, packageRoot } from './root.js'
|
||||
|
||||
/**
|
||||
* Must be invoked through --import when running the node app to set up the following:
|
||||
* - Custom path aliases for imports
|
||||
* - Extensionless imports
|
||||
* - Directory imports like in CJS
|
||||
*/
|
||||
|
||||
/**
|
||||
* PATH ALIAS DEFINITIONS
|
||||
*/
|
||||
const aliases = {
|
||||
'@/': appRoot + '/',
|
||||
'#/': packageRoot + '/'
|
||||
}
|
||||
|
||||
/**
|
||||
* EXTENSIONS TO EVALUATE FOR EXTENSIONLESS IMPORTS
|
||||
*/
|
||||
const extensions = ['.js', '.mjs', '.cjs', '.json']
|
||||
|
||||
// Register the module hooks
|
||||
register('./esmLoader.js', {
|
||||
parentURL: import.meta.url
|
||||
})
|
||||
|
||||
// Custom path resolver
|
||||
function resolveAlias(specifier) {
|
||||
for (const [alias, target] of Object.entries(aliases)) {
|
||||
if (specifier.startsWith(alias)) {
|
||||
const relativePath = specifier.replace(alias, target)
|
||||
return pathToFileURL(path.resolve(relativePath)).href
|
||||
}
|
||||
}
|
||||
return null // No alias found, fall back to default resolution
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjust global ESM resolution logic to allow for path/package aliases, dir imports and extensionless imports
|
||||
*/
|
||||
export async function resolve(specifier, _context, nextResolve) {
|
||||
// Resolve alias
|
||||
const aliasResolved = resolveAlias(specifier)
|
||||
specifier = aliasResolved || specifier
|
||||
|
||||
// Try to resolve as is
|
||||
let throwableError = undefined
|
||||
try {
|
||||
return await nextResolve(specifier)
|
||||
} catch (e) {
|
||||
throwableError = e
|
||||
}
|
||||
|
||||
const isDirImport = throwableError.code === 'ERR_UNSUPPORTED_DIR_IMPORT'
|
||||
|
||||
// Didn't work, try with extensions
|
||||
for (const ext of extensions) {
|
||||
try {
|
||||
return await nextResolve(specifier + ext)
|
||||
} catch (e) {
|
||||
if (!throwableError) {
|
||||
throwableError = e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If it was a dir import also, try that with extensions
|
||||
specifier = isDirImport ? path.join(specifier, 'index') : specifier
|
||||
for (const ext of extensions) {
|
||||
try {
|
||||
return await nextResolve(specifier + ext)
|
||||
} catch (e) {
|
||||
if (!throwableError) {
|
||||
throwableError = e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw throwableError
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ensureErrorOrWrapAsCause } from '@/modules/shared/errors/ensureError'
|
||||
import { join, merge } from 'lodash'
|
||||
import { MultiError } from 'verror'
|
||||
import { join, merge } from 'lodash-es'
|
||||
import VError from 'verror'
|
||||
import {
|
||||
FreeConnectionsCalculators,
|
||||
MultiDBCheck,
|
||||
@@ -30,7 +30,7 @@ export const handleLivenessFactory =
|
||||
', '
|
||||
)} is not available.`,
|
||||
{
|
||||
cause: new MultiError(
|
||||
cause: new VError.MultiError(
|
||||
Object.entries(allPostgresResults).map((kv) =>
|
||||
ensureErrorOrWrapAsCause(
|
||||
//HACK: kv[1] is not typed correctly as the filter does not narrow the type
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable no-restricted-imports */
|
||||
/* istanbul ignore file */
|
||||
import { packageRoot } from './bootstrap'
|
||||
import { packageRoot } from './bootstrap.js'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import {
|
||||
|
||||
@@ -268,4 +268,4 @@ const resolvers: Resolvers = {
|
||||
}
|
||||
}
|
||||
|
||||
export = resolvers
|
||||
export default resolvers
|
||||
|
||||
@@ -26,4 +26,4 @@ const ServerAccessRequestsModule: SpeckleModule = {
|
||||
}
|
||||
}
|
||||
|
||||
export = ServerAccessRequestsModule
|
||||
export default ServerAccessRequestsModule
|
||||
|
||||
@@ -51,15 +51,15 @@ import {
|
||||
} from '@/test/graphql/generated/graphql'
|
||||
import { testApolloServer, TestApolloServer } from '@/test/graphqlHelper'
|
||||
import { truncateTables } from '@/test/hooks'
|
||||
import { EmailSendingServiceMock } from '@/test/mocks/global'
|
||||
import {
|
||||
buildNotificationsStateTracker,
|
||||
NotificationsStateManager
|
||||
} from '@/test/notificationsHelper'
|
||||
import { getStreamActivities } from '@/test/speckle-helpers/activityStreamHelper'
|
||||
import { createEmailListener, TestEmailListener } from '@/test/speckle-helpers/email'
|
||||
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
|
||||
import { expect } from 'chai'
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
|
||||
const getUser = getUserFactory({ db })
|
||||
const getStream = getStreamFactory({ db })
|
||||
@@ -150,6 +150,8 @@ describe('Project access requests', () => {
|
||||
id: ''
|
||||
}
|
||||
|
||||
let emailListener: TestEmailListener
|
||||
|
||||
let quitters: (() => void)[] = []
|
||||
|
||||
before(async () => {
|
||||
@@ -164,15 +166,18 @@ describe('Project access requests', () => {
|
||||
authUserId: me.id
|
||||
})
|
||||
notificationsStateManager = buildNotificationsStateTracker()
|
||||
emailListener = await createEmailListener()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
emailListener.reset()
|
||||
quitters.forEach((q) => q())
|
||||
quitters = []
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
notificationsStateManager.destroy()
|
||||
await emailListener.destroy()
|
||||
})
|
||||
|
||||
const createReq = async (projectId: string) =>
|
||||
@@ -228,10 +233,8 @@ describe('Project access requests', () => {
|
||||
eventFired = true
|
||||
})
|
||||
)
|
||||
const sendEmailCall = EmailSendingServiceMock.hijackFunction(
|
||||
'sendEmail',
|
||||
async () => true
|
||||
)
|
||||
|
||||
const { getSends } = emailListener.listen({ times: 1 })
|
||||
|
||||
const waitForAck = notificationsStateManager.waitForAck(
|
||||
(e) => e.result?.type === NotificationType.NewStreamAccessRequest
|
||||
@@ -255,8 +258,9 @@ describe('Project access requests', () => {
|
||||
await waitForAck
|
||||
|
||||
// email gets sent out
|
||||
expect(sendEmailCall.args?.[0]?.[0]).to.be.ok
|
||||
const emailParams = sendEmailCall.args[0][0]
|
||||
const sentEmails = getSends()
|
||||
expect(sentEmails.length).to.eq(1)
|
||||
const emailParams = sentEmails[0]
|
||||
|
||||
expect(emailParams.subject).to.contain('A user requested access to your project')
|
||||
expect(emailParams.html).to.be.ok
|
||||
|
||||
@@ -52,15 +52,15 @@ import {
|
||||
import { StreamRole } from '@/test/graphql/generated/graphql'
|
||||
import { createAuthedTestContext, ServerAndContext } from '@/test/graphqlHelper'
|
||||
import { truncateTables } from '@/test/hooks'
|
||||
import { EmailSendingServiceMock } from '@/test/mocks/global'
|
||||
import {
|
||||
buildNotificationsStateTracker,
|
||||
NotificationsStateManager
|
||||
} from '@/test/notificationsHelper'
|
||||
import { getStreamActivities } from '@/test/speckle-helpers/activityStreamHelper'
|
||||
import { createEmailListener, TestEmailListener } from '@/test/speckle-helpers/email'
|
||||
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
|
||||
import { expect } from 'chai'
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
|
||||
const getUser = getUserFactory({ db })
|
||||
const getStreamCollaborators = getStreamCollaboratorsFactory({ db })
|
||||
@@ -154,6 +154,8 @@ describe('Stream access requests', () => {
|
||||
id: ''
|
||||
}
|
||||
|
||||
let emailListener: TestEmailListener
|
||||
|
||||
before(async () => {
|
||||
await cleanup()
|
||||
await createTestUsers([me, otherGuy, anotherGuy])
|
||||
@@ -167,10 +169,16 @@ describe('Stream access requests', () => {
|
||||
context: await createAuthedTestContext(me.id)
|
||||
}
|
||||
notificationsStateManager = buildNotificationsStateTracker()
|
||||
emailListener = await createEmailListener()
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
notificationsStateManager.destroy()
|
||||
await emailListener.destroy()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
emailListener.reset()
|
||||
})
|
||||
|
||||
const createReq = (streamId: string) =>
|
||||
@@ -202,10 +210,7 @@ describe('Stream access requests', () => {
|
||||
})
|
||||
|
||||
it('operation succeeds', async () => {
|
||||
const sendEmailCall = EmailSendingServiceMock.hijackFunction(
|
||||
'sendEmail',
|
||||
async () => true
|
||||
)
|
||||
const { getSends } = emailListener.listen({ times: 1 })
|
||||
|
||||
const waitForAck = notificationsStateManager.waitForAck(
|
||||
(e) => e.result?.type === NotificationType.NewStreamAccessRequest
|
||||
@@ -226,8 +231,9 @@ describe('Stream access requests', () => {
|
||||
await waitForAck
|
||||
|
||||
// email gets sent out
|
||||
expect(sendEmailCall.args?.[0]?.[0]).to.be.ok
|
||||
const emailParams = sendEmailCall.args[0][0]
|
||||
const sentEmails = getSends()
|
||||
expect(sentEmails.length).to.eq(1)
|
||||
const emailParams = sentEmails[0]
|
||||
|
||||
expect(emailParams.subject).to.contain('A user requested access to your project')
|
||||
expect(emailParams.html).to.be.ok
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import { CommentEvents, CommentEventsPayloads } from '@/modules/comments/domain/events'
|
||||
import { ReplyCreateInput } from '@/modules/core/graph/generated/graphql'
|
||||
import { EventBusListen } from '@/modules/shared/services/eventBus'
|
||||
import { has } from 'lodash'
|
||||
import { has } from 'lodash-es'
|
||||
import { OverrideProperties } from 'type-fest'
|
||||
|
||||
const addThreadCreatedActivityFactory =
|
||||
|
||||
@@ -65,7 +65,7 @@ const userTimelineQueryCore = async (
|
||||
return { items, cursor, totalCount }
|
||||
}
|
||||
|
||||
export = {
|
||||
export default {
|
||||
LimitedUser: {
|
||||
async activity(parent, args) {
|
||||
return await userActivityQueryCore(parent, args)
|
||||
|
||||
@@ -154,6 +154,6 @@ const activityModule: SpeckleModule = {
|
||||
}
|
||||
}
|
||||
|
||||
export = {
|
||||
export default {
|
||||
...activityModule
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// /* istanbul ignore file */
|
||||
exports.up = async (knex) => {
|
||||
const up = async (knex) => {
|
||||
await knex.schema.createTable('stream_activity', (table) => {
|
||||
// No foreign keys because the referenced objects may be deleted, but we want to keep their ids here in this table for future analysis
|
||||
table.string('streamId', 10)
|
||||
@@ -18,6 +18,8 @@ exports.up = async (knex) => {
|
||||
})
|
||||
}
|
||||
|
||||
exports.down = async (knex) => {
|
||||
const down = async (knex) => {
|
||||
await knex.schema.dropTableIfExists('stream_activity')
|
||||
}
|
||||
|
||||
export { up, down }
|
||||
|
||||
@@ -2,10 +2,11 @@ import { SpeckleModule } from '@/modules/shared/helpers/typeHelper'
|
||||
import { moduleLogger } from '@/observability/logging'
|
||||
import { readFile } from 'fs/promises'
|
||||
import { getFrontendOrigin } from '@/modules/shared/helpers/envHelper'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
async function getExplorerHtml() {
|
||||
const fileBaseContents = await readFile(
|
||||
require.resolve('#/assets/apiexplorer/templates/explorer.html'),
|
||||
fileURLToPath(import.meta.resolve('#/assets/apiexplorer/templates/explorer.html')),
|
||||
{ encoding: 'utf-8' }
|
||||
)
|
||||
return fileBaseContents.replace(
|
||||
|
||||
@@ -25,7 +25,7 @@ const revokeExistingAppCredentialsForUser = revokeExistingAppCredentialsForUserF
|
||||
db
|
||||
})
|
||||
|
||||
export = {
|
||||
export default {
|
||||
Query: {
|
||||
async app(_parent, args) {
|
||||
const app = await getApp({ id: args.id })
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getAuthStrategies } from '@/modules/auth'
|
||||
import { Resolvers } from '@/modules/core/graph/generated/graphql'
|
||||
|
||||
export = {
|
||||
export default {
|
||||
ServerInfo: {
|
||||
authStrategies() {
|
||||
return getAuthStrategies()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { expect } from 'chai'
|
||||
import { describe, it } from 'mocha'
|
||||
import { getNameFromUserInfo } from '@/modules/auth/helpers/oidc'
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
getFrontendOrigin,
|
||||
getSessionSecret
|
||||
} from '@/modules/shared/helpers/envHelper'
|
||||
import { isString, noop } from 'lodash'
|
||||
import { isString, noop } from 'lodash-es'
|
||||
import { CreateAuthorizationCode } from '@/modules/auth/domain/operations'
|
||||
import { ensureError, TIME_MS } from '@speckle/shared'
|
||||
import { LegacyGetUser } from '@/modules/core/domain/users/operations'
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
'use strict'
|
||||
|
||||
// Knex table migrations
|
||||
exports.up = async (knex) => {
|
||||
const up = async (knex) => {
|
||||
// Applications that integrate with this server.
|
||||
await knex.schema.createTable('server_apps', (table) => {
|
||||
table.string('id', 10).primary()
|
||||
@@ -93,7 +93,7 @@ exports.up = async (knex) => {
|
||||
// await knex( 'scopes' ).insert( appTokenScopes )
|
||||
}
|
||||
|
||||
exports.down = async (knex) => {
|
||||
const down = async (knex) => {
|
||||
await knex.schema.dropTableIfExists('server_apps_scopes')
|
||||
await knex.schema.dropTableIfExists('authorization_codes')
|
||||
await knex.schema.dropTableIfExists('refresh_tokens')
|
||||
@@ -101,3 +101,5 @@ exports.down = async (knex) => {
|
||||
|
||||
await knex.schema.dropTableIfExists('server_apps')
|
||||
}
|
||||
|
||||
export { up, down }
|
||||
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
import { ServerAppRecord, UserRecord } from '@/modules/core/helpers/types'
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
import { Knex } from 'knex'
|
||||
import { difference, omit } from 'lodash'
|
||||
import { difference, omit } from 'lodash-es'
|
||||
import { AppCreateError } from '@/modules/auth/errors'
|
||||
import { UserInputError } from '@/modules/core/errors/userinput'
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { InvalidArgumentError } from '@/modules/shared/errors'
|
||||
import { Nullable } from '@/modules/shared/helpers/typeHelper'
|
||||
import { ServerAppsScopesRecord } from '@/modules/auth/helpers/types'
|
||||
import { groupBy, mapValues } from 'lodash'
|
||||
import { groupBy, mapValues } from 'lodash-es'
|
||||
import { TokenScopeData } from '@/modules/shared/domain/rolesAndScopes/types'
|
||||
import { Knex } from 'knex'
|
||||
import {
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Strategy, AuthenticateOptions } from 'passport'
|
||||
import passport from 'passport'
|
||||
import type { Request, Response, NextFunction, RequestHandler } from 'express'
|
||||
import { ensureError, type Optional, throwUncoveredError } from '@speckle/shared'
|
||||
import { get, isArray, isObjectLike, isString } from 'lodash'
|
||||
import { get, isArray, isObjectLike, isString } from 'lodash-es'
|
||||
import type { PassportAuthenticateHandlerBuilder } from '@/modules/auth/domain/operations'
|
||||
import { ExpectedAuthFailure } from '@/modules/auth/domain/const'
|
||||
import type { ResolveAuthRedirectPath } from '@/modules/serverinvites/services/operations'
|
||||
|
||||
@@ -80,4 +80,4 @@ const setupStrategiesFactory =
|
||||
return authStrategies
|
||||
}
|
||||
|
||||
export = setupStrategiesFactory
|
||||
export default setupStrategiesFactory
|
||||
|
||||
@@ -223,4 +223,4 @@ const azureAdStrategyBuilderFactory =
|
||||
}
|
||||
}
|
||||
|
||||
export = azureAdStrategyBuilderFactory
|
||||
export default azureAdStrategyBuilderFactory
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
getServerOrigin
|
||||
} from '@/modules/shared/helpers/envHelper'
|
||||
import type { Request } from 'express'
|
||||
import { get } from 'lodash'
|
||||
import { get } from 'lodash-es'
|
||||
import { ensureError, Optional } from '@speckle/shared'
|
||||
import { ServerInviteRecord } from '@/modules/serverinvites/domain/types'
|
||||
import {
|
||||
@@ -200,4 +200,4 @@ const githubStrategyBuilderFactory =
|
||||
return strategy
|
||||
}
|
||||
|
||||
export = githubStrategyBuilderFactory
|
||||
export default githubStrategyBuilderFactory
|
||||
|
||||
@@ -212,4 +212,4 @@ const googleStrategyBuilderFactory =
|
||||
return strategy
|
||||
}
|
||||
|
||||
export = googleStrategyBuilderFactory
|
||||
export default googleStrategyBuilderFactory
|
||||
|
||||
@@ -162,4 +162,4 @@ const localStrategyBuilderFactory =
|
||||
return strategy
|
||||
}
|
||||
|
||||
export = localStrategyBuilderFactory
|
||||
export default localStrategyBuilderFactory
|
||||
|
||||
@@ -16,7 +16,7 @@ import { getNameFromUserInfo } from '@/modules/auth/helpers/oidc'
|
||||
import { ServerInviteResourceType } from '@/modules/serverinvites/domain/constants'
|
||||
import { getResourceTypeRole } from '@/modules/serverinvites/helpers/core'
|
||||
import { AuthStrategyBuilder } from '@/modules/auth/helpers/types'
|
||||
import { get } from 'lodash'
|
||||
import { get } from 'lodash-es'
|
||||
import { ensureError, Optional } from '@speckle/shared'
|
||||
import { ServerInviteRecord } from '@/modules/serverinvites/domain/types'
|
||||
import {
|
||||
@@ -203,4 +203,4 @@ const oidcStrategyBuilderFactory =
|
||||
}
|
||||
}
|
||||
|
||||
export = oidcStrategyBuilderFactory
|
||||
export default oidcStrategyBuilderFactory
|
||||
|
||||
@@ -62,7 +62,7 @@ import { RateLimiterMemory } from 'rate-limiter-flexible'
|
||||
import { TIME } from '@speckle/shared'
|
||||
import type { Application } from 'express'
|
||||
import { passportAuthenticationCallbackFactory } from '@/modules/auth/services/passportService'
|
||||
import { testLogger as logger } from '@/observability/logging'
|
||||
import { extendLoggerComponent, logger as baseLogger } from '@/observability/logging'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
@@ -188,6 +188,7 @@ const createUser = createUserFactory({
|
||||
})
|
||||
const getUserByEmail = legacyGetUserByEmailFactory({ db })
|
||||
const updateServerInfo = updateServerInfoFactory({ db })
|
||||
const logger = extendLoggerComponent(baseLogger, 'auth-tests')
|
||||
|
||||
const expect = chai.expect
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { faker } from '@faker-js/faker'
|
||||
import { RelativeURL } from '@speckle/shared'
|
||||
import { expect } from 'chai'
|
||||
import type { Express } from 'express'
|
||||
import { has, isString } from 'lodash'
|
||||
import { has, isString } from 'lodash-es'
|
||||
import request from 'supertest'
|
||||
|
||||
export const appId = 'spklwebapp' // same values as on FE
|
||||
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
TestApolloServer
|
||||
} from '@/test/graphqlHelper'
|
||||
import { beforeEachContext } from '@/test/hooks'
|
||||
import { EmailSendingServiceMock } from '@/test/mocks/global'
|
||||
import { captureCreatedInvite } from '@/test/speckle-helpers/inviteHelper'
|
||||
import {
|
||||
BasicTestStream,
|
||||
@@ -89,10 +88,6 @@ describe('Server registration', () => {
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
EmailSendingServiceMock.resetMockedFunctions()
|
||||
})
|
||||
|
||||
describe('with local strategy (email/pw)', () => {
|
||||
it('works', async () => {
|
||||
const challenge = 'asd123'
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
} from '@speckle/shared'
|
||||
import { randomUUID } from 'crypto'
|
||||
import { automateLogger, type Logger } from '@/observability/logging'
|
||||
import { has, isObjectLike, isEmpty } from 'lodash'
|
||||
import { has, isObjectLike, isEmpty } from 'lodash-es'
|
||||
import { getRequestLogger } from '@/observability/utils/requestContext'
|
||||
|
||||
export type AuthCodePayloadWithOrigin = AuthCodePayload & { origin: string }
|
||||
|
||||
@@ -17,7 +17,7 @@ import { getFeatureFlags } from '@/modules/shared/helpers/envHelper'
|
||||
import { faker } from '@faker-js/faker'
|
||||
import { Automate, isNullOrUndefined, SourceAppNames } from '@speckle/shared'
|
||||
import dayjs from 'dayjs'
|
||||
import { times } from 'lodash'
|
||||
import { times } from 'lodash-es'
|
||||
|
||||
const { FF_AUTOMATE_MODULE_ENABLED } = getFeatureFlags()
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ import {
|
||||
getFunctionInputsForFrontendFactory
|
||||
} from '@/modules/automate/services/encryption'
|
||||
import { buildDecryptor } from '@/modules/shared/utils/libsodium'
|
||||
import { keyBy } from 'lodash'
|
||||
import * as _ from 'lodash-es'
|
||||
import { redactWriteOnlyInputData } from '@/modules/automate/utils/jsonSchemaRedactor'
|
||||
import {
|
||||
ProjectSubscriptions,
|
||||
@@ -142,7 +142,7 @@ const createAppToken = createAppTokenFactory({
|
||||
storeUserServerAppToken: storeUserServerAppTokenFactory({ db })
|
||||
})
|
||||
|
||||
export = (FF_AUTOMATE_MODULE_ENABLED
|
||||
export default (FF_AUTOMATE_MODULE_ENABLED
|
||||
? {
|
||||
/**
|
||||
* If automate module is enabled
|
||||
@@ -443,7 +443,7 @@ export = (FF_AUTOMATE_MODULE_ENABLED
|
||||
const fns = await ctx.loaders
|
||||
.forRegion({ db: projectDb })
|
||||
.automations.getRevisionFunctions.load(parent.id)
|
||||
const fnsReleases = keyBy(
|
||||
const fnsReleases = _.keyBy(
|
||||
(
|
||||
await ctx.loaders
|
||||
.forRegion({ db: projectDb })
|
||||
|
||||
@@ -394,4 +394,4 @@ const automateModule: SpeckleModule = {
|
||||
}
|
||||
}
|
||||
|
||||
export = automateModule
|
||||
export default automateModule
|
||||
|
||||
@@ -83,7 +83,7 @@ import {
|
||||
import { Nullable, StreamRoles, isNullOrUndefined } from '@speckle/shared'
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
import { Knex } from 'knex'
|
||||
import _, { clamp, groupBy, keyBy, pick } from 'lodash'
|
||||
import { clamp, groupBy, keyBy, pick } from 'lodash-es'
|
||||
import { SetOptional, SetRequired } from 'type-fest'
|
||||
|
||||
const tables = {
|
||||
@@ -163,7 +163,7 @@ export const upsertAutomationFunctionRunFactory =
|
||||
await tables
|
||||
.automationFunctionRuns(deps.db)
|
||||
.insert(
|
||||
_.pick(automationFunctionRun, AutomationFunctionRuns.withoutTablePrefix.cols)
|
||||
pick(automationFunctionRun, AutomationFunctionRuns.withoutTablePrefix.cols)
|
||||
)
|
||||
.onConflict(AutomationFunctionRuns.withoutTablePrefix.col.id)
|
||||
.merge([
|
||||
@@ -185,7 +185,7 @@ export const upsertAutomationRunFactory =
|
||||
async (automationRun: InsertableAutomationRun) => {
|
||||
await tables
|
||||
.automationRuns(deps.db)
|
||||
.insert(_.pick(automationRun, AutomationRuns.withoutTablePrefix.cols))
|
||||
.insert(pick(automationRun, AutomationRuns.withoutTablePrefix.cols))
|
||||
.onConflict(AutomationRuns.withoutTablePrefix.col.id)
|
||||
.merge([
|
||||
AutomationRuns.withoutTablePrefix.col.status,
|
||||
@@ -198,7 +198,7 @@ export const upsertAutomationRunFactory =
|
||||
.insert(
|
||||
automationRun.triggers.map((t) => ({
|
||||
automationRunId: automationRun.id,
|
||||
..._.pick(t, AutomationRunTriggers.withoutTablePrefix.cols)
|
||||
...pick(t, AutomationRunTriggers.withoutTablePrefix.cols)
|
||||
}))
|
||||
)
|
||||
.onConflict()
|
||||
@@ -207,7 +207,7 @@ export const upsertAutomationRunFactory =
|
||||
.automationFunctionRuns(deps.db)
|
||||
.insert(
|
||||
automationRun.functionRuns.map((f) => ({
|
||||
..._.pick(f, AutomationFunctionRuns.withoutTablePrefix.cols),
|
||||
...pick(f, AutomationFunctionRuns.withoutTablePrefix.cols),
|
||||
runId: automationRun.id
|
||||
}))
|
||||
)
|
||||
@@ -372,7 +372,7 @@ export const storeAutomationRevisionFactory =
|
||||
(deps: { db: Knex }): StoreAutomationRevision =>
|
||||
async (revision: InsertableAutomationRevision) => {
|
||||
const id = revision.id || generateRevisionId()
|
||||
const rev = _.pick(revision, AutomationRevisions.withoutTablePrefix.cols)
|
||||
const rev = pick(revision, AutomationRevisions.withoutTablePrefix.cols)
|
||||
const [newRev] = await tables
|
||||
.automationRevisions(deps.db)
|
||||
.insert({
|
||||
|
||||
@@ -4,7 +4,7 @@ import { AutomateAuthCodeHandshakeError } from '@/modules/automate/errors/manage
|
||||
import { EventBus } from '@/modules/shared/services/eventBus'
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
import Redis from 'ioredis'
|
||||
import { get, has, isObjectLike } from 'lodash'
|
||||
import { get, has, isObjectLike } from 'lodash-es'
|
||||
import { Logger } from 'pino'
|
||||
import { WorkspaceEvents } from '@/modules/workspacesCore/domain/events'
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import {
|
||||
AutomationRunStatuses,
|
||||
VersionCreationTriggerType
|
||||
} from '@/modules/automate/helpers/types'
|
||||
import { keyBy, uniq } from 'lodash'
|
||||
import { keyBy, uniq } from 'lodash-es'
|
||||
import { resolveStatusFromFunctionRunStatuses } from '@/modules/automate/services/runsManagement'
|
||||
import { TriggeredAutomationsStatusGraphQLReturn } from '@/modules/automate/helpers/graphTypes'
|
||||
import { FunctionInputDecryptor } from '@/modules/automate/services/encryption'
|
||||
@@ -57,7 +57,7 @@ import { GetBranchesByIds } from '@/modules/core/domain/branches/operations'
|
||||
import { ValidateStreamAccess } from '@/modules/core/domain/streams/operations'
|
||||
import { EventBusEmit } from '@/modules/shared/services/eventBus'
|
||||
import { AutomationEvents } from '@/modules/automate/domain/events'
|
||||
import { UnformattableTriggerDefinitionSchemaError } from '@speckle/shared/dist/commonjs/automate/index.js'
|
||||
import { UnformattableTriggerDefinitionSchemaError } from '@speckle/shared/automate'
|
||||
|
||||
export type CreateAutomationDeps = {
|
||||
createAuthCode: CreateStoredAuthCode
|
||||
|
||||
@@ -2,7 +2,7 @@ import { getEncryptionKeysPath } from '@/modules/shared/helpers/envHelper'
|
||||
import { packageRoot } from '@/bootstrap'
|
||||
import path from 'node:path'
|
||||
import fs from 'node:fs/promises'
|
||||
import { has, isArray, isObjectLike } from 'lodash'
|
||||
import { has, isArray, isObjectLike } from 'lodash-es'
|
||||
import { Nullable, Optional } from '@speckle/shared'
|
||||
import { MisconfiguredEnvironmentError } from '@/modules/shared/errors'
|
||||
import { AutomationFunctionInputEncryptionError } from '@/modules/automate/errors/management'
|
||||
|
||||
@@ -46,7 +46,7 @@ import { getFunctionsMarketplaceUrl } from '@/modules/core/helpers/routeHelper'
|
||||
import type { Logger } from '@/observability/logging'
|
||||
import { CreateStoredAuthCode } from '@/modules/automate/domain/operations'
|
||||
import { GetUser } from '@/modules/core/domain/users/operations'
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
import { UnknownFunctionTemplateError } from '@/modules/automate/errors/functions'
|
||||
import { UserInputError } from '@/modules/core/errors/userinput'
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
import { BasicTestStream, createTestStreams } from '@/test/speckle-helpers/streamHelper'
|
||||
import { Automate, Roles } from '@speckle/shared'
|
||||
import { expect } from 'chai'
|
||||
import { times } from 'lodash'
|
||||
import { times } from 'lodash-es'
|
||||
import { getFeatureFlags } from '@/modules/shared/helpers/envHelper'
|
||||
import { db } from '@/db/knex'
|
||||
import {
|
||||
|
||||
@@ -7,4 +7,4 @@ const backgroundJobsModule: SpeckleModule = {
|
||||
}
|
||||
}
|
||||
|
||||
export = backgroundJobsModule
|
||||
export default backgroundJobsModule
|
||||
|
||||
@@ -19,22 +19,26 @@ import {
|
||||
GetSignedUrl
|
||||
} from '@/modules/blobstorage/domain/operations'
|
||||
|
||||
export type ObjectStorage = {
|
||||
client: S3Client
|
||||
bucket: string
|
||||
}
|
||||
|
||||
export type GetProjectObjectStorage = (args: {
|
||||
projectId: string
|
||||
}) => Promise<ObjectStorage>
|
||||
|
||||
export type GetObjectStorageParams = {
|
||||
credentials: S3ClientConfig['credentials']
|
||||
endpoint: S3ClientConfig['endpoint']
|
||||
region: S3ClientConfig['region']
|
||||
credentials: {
|
||||
accessKeyId: string
|
||||
secretAccessKey: string
|
||||
}
|
||||
endpoint: string
|
||||
region: string
|
||||
bucket: string
|
||||
}
|
||||
|
||||
export type ObjectStorage = {
|
||||
client: S3Client
|
||||
bucket: string
|
||||
params: GetObjectStorageParams
|
||||
}
|
||||
|
||||
/**
|
||||
* Get object storage client
|
||||
*/
|
||||
@@ -48,7 +52,7 @@ export const getObjectStorage = (params: GetObjectStorageParams): ObjectStorage
|
||||
forcePathStyle: true
|
||||
}
|
||||
const client = new S3Client(config)
|
||||
return { client, bucket }
|
||||
return { client, bucket, params }
|
||||
}
|
||||
|
||||
let mainObjectStorage: Optional<ObjectStorage> = undefined
|
||||
|
||||
@@ -65,7 +65,7 @@ const streamBlobResolvers = {
|
||||
}
|
||||
}
|
||||
|
||||
export = {
|
||||
export default {
|
||||
ServerInfo: {
|
||||
//deprecated
|
||||
blobSizeLimitBytes() {
|
||||
|
||||
@@ -6,7 +6,7 @@ const TABLE_NAME = 'blob_storage'
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.up = async (knex) => {
|
||||
const up = async (knex) => {
|
||||
await knex.schema.createTable(TABLE_NAME, (table) => {
|
||||
table.string('id', 10)
|
||||
// dont cascade on delete, cause it doesn't clean the object storage for the objs
|
||||
@@ -26,6 +26,8 @@ exports.up = async (knex) => {
|
||||
})
|
||||
}
|
||||
|
||||
exports.down = async (knex) => {
|
||||
const down = async (knex) => {
|
||||
await knex.schema.dropTableIfExists(TABLE_NAME)
|
||||
}
|
||||
|
||||
export { up, down }
|
||||
|
||||
+4
-2
@@ -7,14 +7,16 @@ const HASH_COLUMN_NAME = 'fileHash'
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.up = async (knex) => {
|
||||
const up = async (knex) => {
|
||||
await knex.schema.table(TABLE_NAME, (table) => {
|
||||
table.string(HASH_COLUMN_NAME)
|
||||
})
|
||||
}
|
||||
|
||||
exports.down = async (knex) => {
|
||||
const down = async (knex) => {
|
||||
await knex.schema.alterTable(TABLE_NAME, (table) => {
|
||||
table.dropColumn(HASH_COLUMN_NAME)
|
||||
})
|
||||
}
|
||||
|
||||
export { up, down }
|
||||
|
||||
+4
-2
@@ -2,7 +2,7 @@
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.up = async (knex) => {
|
||||
const up = async (knex) => {
|
||||
await knex.raw(
|
||||
'ALTER TABLE "blob_storage" ALTER COLUMN "id" SET DATA TYPE varchar(255);'
|
||||
)
|
||||
@@ -12,8 +12,10 @@ exports.up = async (knex) => {
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.down = async (knex) => {
|
||||
const down = async (knex) => {
|
||||
await knex.raw(
|
||||
'ALTER TABLE "blob_storage" ALTER COLUMN "id" SET DATA TYPE varchar(10);'
|
||||
)
|
||||
}
|
||||
|
||||
export { up, down }
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
import { Upload } from '@aws-sdk/lib-storage'
|
||||
import type { Command } from '@aws-sdk/smithy-client'
|
||||
import { ensureError } from '@speckle/shared'
|
||||
import { get } from 'lodash'
|
||||
import { get } from 'lodash-es'
|
||||
import type stream from 'stream'
|
||||
|
||||
const sendCommand = async <CommandOutput extends ServiceOutputTypes>(
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
streamReadPermissionsPipelineFactory
|
||||
} from '@/modules/shared/authz'
|
||||
import { authMiddlewareCreator } from '@/modules/shared/middleware'
|
||||
import { isArray } from 'lodash'
|
||||
import { isArray } from 'lodash-es'
|
||||
import { UnauthorizedError } from '@/modules/shared/errors'
|
||||
import {
|
||||
getAllStreamBlobIdsFactory,
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
AlreadyRegisteredBlobError,
|
||||
StoredBlobAccessError
|
||||
} from '@/modules/blobstorage/errors'
|
||||
import { isEmpty } from 'lodash'
|
||||
import { isEmpty } from 'lodash-es'
|
||||
import { MisconfiguredEnvironmentError } from '@/modules/shared/errors'
|
||||
// import { acceptedFileExtensions } from '@speckle/shared'
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { getProjectObjectStorage } from '@/modules/multiregion/utils/blobStorage
|
||||
import { getProjectDbClient } from '@/modules/multiregion/utils/dbSelector'
|
||||
import type { Logger } from '@/observability/logging'
|
||||
import type { Readable, Writable } from 'stream'
|
||||
import { get } from 'lodash'
|
||||
import { get } from 'lodash-es'
|
||||
import type { UploadResult, ProcessingResult } from '@/modules/blobstorage/domain/types'
|
||||
import type { Busboy } from 'busboy'
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ import { BasicTestUser } from '@/test/authHelper'
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
import type { BlobStorageItem } from '@/modules/blobstorage/domain/types'
|
||||
import { getEventBus } from '@/modules/shared/services/eventBus'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
|
||||
@@ -134,8 +135,8 @@ describe('Blobs integration @blobstorage', () => {
|
||||
const response = await request(app)
|
||||
.post(`/api/stream/${streamId}/blob`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.attach('blob1', require.resolve('@/readme.md'))
|
||||
.attach('blob2', require.resolve('@/package.json'))
|
||||
.attach('blob1', fileURLToPath(import.meta.resolve('@/readme.md')))
|
||||
.attach('blob2', fileURLToPath(import.meta.resolve('@/package.json')))
|
||||
expect(response.status).to.equal(201)
|
||||
expect(response.body.uploadResults).to.exist
|
||||
const uploadResults = response.body.uploadResults
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* istanbul ignore file */
|
||||
import crs from 'crypto-random-string'
|
||||
import { range } from 'lodash'
|
||||
import { range } from 'lodash-es'
|
||||
import { knex } from '@/db/knex'
|
||||
|
||||
const BlobStorage = () => knex('blob_storage')
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { beforeEachContext } from '@/test/hooks'
|
||||
import { NotFoundError, BadRequestError } from '@/modules/shared/errors'
|
||||
import { range } from 'lodash'
|
||||
import { range } from 'lodash-es'
|
||||
import { fakeIdGenerator, createBlobs } from '@/modules/blobstorage/tests/helpers'
|
||||
import {
|
||||
uploadFileStreamFactory,
|
||||
|
||||
@@ -25,7 +25,7 @@ import { Knex } from 'knex'
|
||||
import cryptoRandomString from 'crypto-random-string'
|
||||
import { expect } from 'chai'
|
||||
import { testLogger } from '@/observability/logging'
|
||||
import { put } from 'axios'
|
||||
import axios from 'axios'
|
||||
import { expectToThrow } from '@/test/assertionHelper'
|
||||
import {
|
||||
AlreadyRegisteredBlobError,
|
||||
@@ -148,7 +148,7 @@ const { FF_LARGE_FILE_IMPORTS_ENABLED } = getFeatureFlags()
|
||||
|
||||
const fileSize = 100
|
||||
|
||||
const response = await put(url, cryptoRandomString({ length: fileSize }))
|
||||
const response = await axios.put(url, cryptoRandomString({ length: fileSize }))
|
||||
expect(
|
||||
response.status,
|
||||
JSON.stringify({ statusText: response.statusText, body: response.data })
|
||||
@@ -205,7 +205,7 @@ const { FF_LARGE_FILE_IMPORTS_ENABLED } = getFeatureFlags()
|
||||
urlExpiryDurationSeconds: expiryDuration
|
||||
})
|
||||
|
||||
const response = await put(url, 'test content') // more than 1 byte long
|
||||
const response = await axios.put(url, 'test content') // more than 1 byte long
|
||||
expect(
|
||||
response.status,
|
||||
JSON.stringify({ statusText: response.statusText, body: response.data })
|
||||
@@ -245,7 +245,7 @@ const { FF_LARGE_FILE_IMPORTS_ENABLED } = getFeatureFlags()
|
||||
urlExpiryDurationSeconds: expiryDuration
|
||||
})
|
||||
|
||||
const response = await put(url, 'test content')
|
||||
const response = await axios.put(url, 'test content')
|
||||
expect(
|
||||
response.status,
|
||||
JSON.stringify({ statusText: response.statusText, body: response.data })
|
||||
@@ -291,7 +291,7 @@ const { FF_LARGE_FILE_IMPORTS_ENABLED } = getFeatureFlags()
|
||||
urlExpiryDurationSeconds: expiryDuration
|
||||
})
|
||||
|
||||
const response = await put(url, cryptoRandomString({ length: 100 })) // more than 1 byte long
|
||||
const response = await axios.put(url, cryptoRandomString({ length: 100 })) // more than 1 byte long
|
||||
expect(
|
||||
response.status,
|
||||
JSON.stringify({ statusText: response.statusText, body: response.data })
|
||||
@@ -337,7 +337,7 @@ const { FF_LARGE_FILE_IMPORTS_ENABLED } = getFeatureFlags()
|
||||
|
||||
const fileSize = 100
|
||||
|
||||
const response = await put(url, cryptoRandomString({ length: fileSize }))
|
||||
const response = await axios.put(url, cryptoRandomString({ length: fileSize }))
|
||||
expect(
|
||||
response.status,
|
||||
JSON.stringify({ statusText: response.statusText, body: response.data })
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
import { CommandModule } from 'yargs'
|
||||
|
||||
const command: CommandModule = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
import { CommandModule } from 'yargs'
|
||||
|
||||
const command: CommandModule = {
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
NOTIFICATIONS_QUEUE,
|
||||
buildNotificationsQueue
|
||||
} from '@/modules/notifications/services/queue'
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
import { cliLogger } from '@/observability/logging'
|
||||
|
||||
const PORT = 3032
|
||||
|
||||
@@ -2,7 +2,7 @@ import { cliLogger } from '@/observability/logging'
|
||||
import { NotificationType } from '@/modules/notifications/helpers/types'
|
||||
import { initializeConsumption } from '@/modules/notifications/index'
|
||||
import { EnvironmentResourceError } from '@/modules/shared/errors'
|
||||
import { get, noop } from 'lodash'
|
||||
import { get, noop } from 'lodash-es'
|
||||
import { CommandModule } from 'yargs'
|
||||
|
||||
const command: CommandModule = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
import { CommandModule } from 'yargs'
|
||||
|
||||
const command: CommandModule = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
import { CommandModule } from 'yargs'
|
||||
|
||||
const command: CommandModule = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
import { CommandModule } from 'yargs'
|
||||
|
||||
const command: CommandModule = {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { getUserFactory } from '@/modules/core/repositories/users'
|
||||
import { ForbiddenError } from '@/modules/shared/errors'
|
||||
import { BasicTestCommit, createTestCommits } from '@/test/speckle-helpers/commitHelper'
|
||||
import dayjs from 'dayjs'
|
||||
import { times } from 'lodash'
|
||||
import { times } from 'lodash-es'
|
||||
import { CommandModule } from 'yargs'
|
||||
import { ProjectRecordVisibility } from '@/modules/core/helpers/types'
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { cliLogger as logger } from '@/observability/logging'
|
||||
import { Users, ServerAcl } from '@/modules/core/dbSchema'
|
||||
import { Roles } from '@/modules/core/helpers/mainConstants'
|
||||
import { faker } from '@faker-js/faker'
|
||||
import { range } from 'lodash'
|
||||
import { range } from 'lodash-es'
|
||||
import { UniqueEnforcer } from 'enforce-unique'
|
||||
import { CommandModule } from 'yargs'
|
||||
import { UserRecord } from '@/modules/core/helpers/types'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
import { CommandModule } from 'yargs'
|
||||
|
||||
const command: CommandModule = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
import { CommandModule } from 'yargs'
|
||||
|
||||
const command: CommandModule = {
|
||||
|
||||
@@ -18,7 +18,7 @@ const command: CommandModule<unknown, { file: string }> = {
|
||||
},
|
||||
handler: async ({ file }) => {
|
||||
logger.info('Loading GQL schema...')
|
||||
const schema = ModulesSetup.graphSchema()
|
||||
const schema = await ModulesSetup.graphSchema()
|
||||
const schemaString = printSchema(schema)
|
||||
|
||||
logger.info(`Saving to "${file}"...`)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
import { CommandModule } from 'yargs'
|
||||
|
||||
const command: CommandModule = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
import { CommandModule } from 'yargs'
|
||||
|
||||
const command: CommandModule = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { noop } from 'lodash'
|
||||
import { noop } from 'lodash-es'
|
||||
import { CommandModule } from 'yargs'
|
||||
|
||||
const command: CommandModule = {
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
/* eslint-disable no-restricted-imports */
|
||||
import '../../bootstrap.js'
|
||||
import path from 'path'
|
||||
import yargs from 'yargs'
|
||||
import '../../bootstrap'
|
||||
import { hideBin } from 'yargs/helpers'
|
||||
import { cliLogger as logger } from '@/observability/logging'
|
||||
import { isTestEnv } from '@/modules/shared/helpers/envHelper'
|
||||
import { mochaHooks } from '@/test/hooks'
|
||||
import { beforeEntireTestRun } from '@/test/hooks'
|
||||
import { getModuleDirectory } from '@speckle/shared/environment/node'
|
||||
|
||||
const main = async () => {
|
||||
const execution = yargs
|
||||
await yargs(hideBin(process.argv))
|
||||
.scriptName('yarn cli')
|
||||
.usage('$0 <cmd> [args]')
|
||||
.commandDir(path.resolve(__dirname, './commands'), { extensions: ['js', 'ts'] })
|
||||
.commandDir(path.resolve(getModuleDirectory(import.meta), './commands'), {
|
||||
extensions: ['js', 'ts']
|
||||
})
|
||||
.option('beforeAll', {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
@@ -24,7 +28,7 @@ const main = async () => {
|
||||
// In test env, run beforeAll hooks to properly initialize everything first
|
||||
if (isBeforeAllSet && isTestEnv()) {
|
||||
logger.info('Running test beforeAll hooks...')
|
||||
await (mochaHooks.beforeAll as () => Promise<void>)()
|
||||
await beforeEntireTestRun()
|
||||
}
|
||||
})
|
||||
.fail((msg, err, yargs) => {
|
||||
@@ -40,12 +44,10 @@ const main = async () => {
|
||||
|
||||
process.exit(1)
|
||||
})
|
||||
.help().argv
|
||||
.help()
|
||||
.parseAsync()
|
||||
|
||||
return execution
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
void main().then(() => {
|
||||
// weird TS typing issue
|
||||
yargs.exit(0, undefined as unknown as Error)
|
||||
})
|
||||
await main()
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { defineRequestDataloaders } from '@/modules/shared/helpers/graphqlHelper'
|
||||
import { keyBy } from 'lodash'
|
||||
import { keyBy } from 'lodash-es'
|
||||
import { Nullable } from '@/modules/shared/helpers/typeHelper'
|
||||
import { ResourceIdentifier } from '@/modules/core/graph/generated/graphql'
|
||||
import {
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
ensureCommentSchema,
|
||||
validateInputAttachmentsFactory
|
||||
} from '@/modules/comments/services/commentTextService'
|
||||
import { has } from 'lodash'
|
||||
import { has } from 'lodash-es'
|
||||
import {
|
||||
documentToBasicString,
|
||||
SmartTextEditorValueSchema
|
||||
@@ -143,7 +143,7 @@ const getAuthorizedStreamCommentFactory =
|
||||
return comment
|
||||
}
|
||||
|
||||
export = {
|
||||
export default {
|
||||
Query: {
|
||||
async comment(_parent, args, context) {
|
||||
const projectId = args.streamId
|
||||
|
||||
@@ -60,4 +60,4 @@ const commentsModule: SpeckleModule = {
|
||||
}
|
||||
}
|
||||
|
||||
export = commentsModule
|
||||
export default commentsModule
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// /* istanbul ignore file */
|
||||
exports.up = async (knex) => {
|
||||
const up = async (knex) => {
|
||||
await knex.schema.createTable('comments', (table) => {
|
||||
table.string('id', 10).primary()
|
||||
table
|
||||
@@ -53,8 +53,10 @@ exports.up = async (knex) => {
|
||||
})
|
||||
}
|
||||
|
||||
exports.down = async (knex) => {
|
||||
const down = async (knex) => {
|
||||
await knex.schema.dropTableIfExists('comment_views')
|
||||
await knex.schema.dropTableIfExists('comment_links')
|
||||
await knex.schema.dropTableIfExists('comments')
|
||||
}
|
||||
|
||||
export { up, down }
|
||||
|
||||
+5
-3
@@ -1,4 +1,4 @@
|
||||
const { Users } = require('@/modules/core/dbSchema')
|
||||
import { Users } from '@/modules/core/dbSchema'
|
||||
|
||||
const COMMENTS_TABLE = 'comments'
|
||||
const COMMENT_VIEWS_TABLE = 'comment_views'
|
||||
@@ -7,7 +7,7 @@ const COMMENT_VIEWS_TABLE = 'comment_views'
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.up = async function (knex) {
|
||||
async function up(knex) {
|
||||
// Delete all orphaned comments, which can be there even though there was a FK there before for some reason
|
||||
await knex
|
||||
.table(COMMENTS_TABLE)
|
||||
@@ -41,7 +41,7 @@ exports.up = async function (knex) {
|
||||
* @param { import("knex").Knex } knex
|
||||
* @returns { Promise<void> }
|
||||
*/
|
||||
exports.down = async function (knex) {
|
||||
async function down(knex) {
|
||||
await knex.schema.alterTable(COMMENTS_TABLE, (table) => {
|
||||
table.dropForeign('authorId')
|
||||
table.foreign('authorId').references(Users.col.id).onDelete('NO ACTION')
|
||||
@@ -58,3 +58,5 @@ exports.down = async function (knex) {
|
||||
table.foreign('userId').references(Users.col.id).onDelete('NO ACTION')
|
||||
})
|
||||
}
|
||||
|
||||
export { up, down }
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
ResourceType
|
||||
} from '@/modules/core/graph/generated/graphql'
|
||||
import { Optional } from '@/modules/shared/helpers/typeHelper'
|
||||
import { clamp, keyBy, reduce } from 'lodash'
|
||||
import { clamp, keyBy, reduce } from 'lodash-es'
|
||||
import crs from 'crypto-random-string'
|
||||
import { executeBatchedSelect } from '@/modules/shared/helpers/dbHelper'
|
||||
import { Knex } from 'knex'
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
isDocEmpty,
|
||||
documentToBasicString
|
||||
} from '@/modules/core/services/richTextEditorService'
|
||||
import { isString, uniq } from 'lodash'
|
||||
import { isString, uniq } from 'lodash-es'
|
||||
import { InvalidAttachmentsError } from '@/modules/comments/errors'
|
||||
import { JSONContent } from '@tiptap/core'
|
||||
import { ValidateInputAttachments } from '@/modules/comments/domain/operations'
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
import { LegacyCommentViewerData } from '@/modules/core/graph/generated/graphql'
|
||||
import { viewerResourcesToString } from '@/modules/core/services/commit/viewerResources'
|
||||
import { Nullable, SpeckleViewer } from '@speckle/shared'
|
||||
import { has, get, intersection, isObjectLike } from 'lodash'
|
||||
import { has, get, intersection, isObjectLike } from 'lodash-es'
|
||||
|
||||
type SerializedViewerState = SpeckleViewer.ViewerState.SerializedViewerState
|
||||
|
||||
|
||||
@@ -62,7 +62,15 @@ export const createCommentFactory =
|
||||
emitEvent: EventBusEmit
|
||||
getViewerResourcesFromLegacyIdentifiers: GetViewerResourcesFromLegacyIdentifiers
|
||||
}) =>
|
||||
async ({ userId, input }: { userId: string; input: CommentCreateInput }) => {
|
||||
async (
|
||||
{ userId, input }: { userId: string; input: CommentCreateInput },
|
||||
options?: Partial<{
|
||||
/**
|
||||
* Used in tests to skip text validation & formatting - text is saved in DB as is
|
||||
*/
|
||||
skipTextValidation: boolean
|
||||
}>
|
||||
) => {
|
||||
if (input.resources.length < 1)
|
||||
throw new UserInputError(
|
||||
'Must specify at least one resource as the comment target'
|
||||
@@ -91,10 +99,12 @@ export const createCommentFactory =
|
||||
}
|
||||
|
||||
await deps.validateInputAttachments(input.streamId, input.blobIds)
|
||||
comment.text = buildCommentTextFromInput({
|
||||
doc: input.text,
|
||||
blobIds: input.blobIds
|
||||
})
|
||||
comment.text = options?.skipTextValidation
|
||||
? (input.text as SmartTextEditorValueSchema)
|
||||
: buildCommentTextFromInput({
|
||||
doc: input.text,
|
||||
blobIds: input.blobIds
|
||||
})
|
||||
|
||||
const id = crs({ length: 10 })
|
||||
const [newComment] = await deps.insertComments([
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CommentRecord } from '@/modules/comments/helpers/types'
|
||||
import { ensureCommentSchema } from '@/modules/comments/services/commentTextService'
|
||||
import type { JSONContent } from '@tiptap/core'
|
||||
import { iterateContentNodes } from '@/modules/core/services/richTextEditorService'
|
||||
import { difference, flatten } from 'lodash'
|
||||
import { difference, flatten } from 'lodash-es'
|
||||
import {
|
||||
NotificationPublisher,
|
||||
NotificationType
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Optional } from '@speckle/shared'
|
||||
import { isUndefined } from 'lodash'
|
||||
import { isUndefined } from 'lodash-es'
|
||||
import {
|
||||
GetPaginatedBranchCommentsFactory,
|
||||
GetPaginatedBranchCommentsPage,
|
||||
|
||||
@@ -11,13 +11,16 @@ import {
|
||||
editCommentFactory,
|
||||
archiveCommentFactory
|
||||
} from '@/modules/comments/services/index'
|
||||
import { convertBasicStringToDocument } from '@/modules/core/services/richTextEditorService'
|
||||
import {
|
||||
convertBasicStringToDocument,
|
||||
SmartTextEditorValueSchema
|
||||
} from '@/modules/core/services/richTextEditorService'
|
||||
import {
|
||||
ensureCommentSchema,
|
||||
buildCommentTextFromInput,
|
||||
validateInputAttachmentsFactory
|
||||
} from '@/modules/comments/services/commentTextService'
|
||||
import { get, range } from 'lodash'
|
||||
import { get, range } from 'lodash-es'
|
||||
import { buildApolloServer } from '@/app'
|
||||
import { AllScopes } from '@/modules/core/helpers/mainConstants'
|
||||
import { createAuthTokenForUser } from '@/test/authHelper'
|
||||
@@ -30,11 +33,6 @@ import {
|
||||
purgeNotifications
|
||||
} from '@/test/notificationsHelper'
|
||||
import { NotificationType } from '@/modules/notifications/helpers/types'
|
||||
import {
|
||||
EmailSendingServiceMock,
|
||||
CommentsRepositoryMock,
|
||||
StreamsRepositoryMock
|
||||
} from '@/test/mocks/global'
|
||||
import { createAuthedTestContext, ServerAndContext } from '@/test/graphqlHelper'
|
||||
import {
|
||||
checkStreamResourceAccessFactory,
|
||||
@@ -128,7 +126,6 @@ import {
|
||||
LegacyCommentViewerData,
|
||||
ReplyCreateInput
|
||||
} from '@/modules/core/graph/generated/graphql'
|
||||
import { CommentRecord } from '@/modules/comments/helpers/types'
|
||||
import { MaybeNullOrUndefined, TIME_MS } from '@speckle/shared'
|
||||
import { CommentEvents } from '@/modules/comments/domain/events'
|
||||
import {
|
||||
@@ -136,7 +133,6 @@ import {
|
||||
getViewerResourcesForCommentsFactory,
|
||||
getViewerResourcesFromLegacyIdentifiersFactory
|
||||
} from '@/modules/core/services/commit/viewerResources'
|
||||
import { StreamRecord } from '@/modules/core/helpers/types'
|
||||
import {
|
||||
processFinalizedProjectInviteFactory,
|
||||
validateProjectInviteBeforeFinalizationFactory
|
||||
@@ -146,11 +142,9 @@ import {
|
||||
validateStreamAccessFactory
|
||||
} from '@/modules/core/services/streams/access'
|
||||
import { authorizeResolver } from '@/modules/shared'
|
||||
|
||||
type LegacyCommentRecord = CommentRecord & {
|
||||
total_count: string
|
||||
resources: Array<{ resourceId: string; resourceType: string }>
|
||||
}
|
||||
import { createEmailListener, TestEmailListener } from '@/test/speckle-helpers/email'
|
||||
import { buildTestProject } from '@/modules/core/tests/helpers/creation'
|
||||
import { GetCommentsQueryVariables } from '@/test/graphql/generated/graphql'
|
||||
|
||||
const getServerInfo = getServerInfoFactory({ db })
|
||||
const getUser = getUserFactory({ db })
|
||||
@@ -285,33 +279,34 @@ const buildFinalizeProjectInvite = () =>
|
||||
getServerInfo
|
||||
})
|
||||
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord: createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
createAndSendInvite: createAndSendInviteFactory({
|
||||
findUserByTarget: findUserByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
buildInviteEmailContents: buildCoreInviteEmailContentsFactory({
|
||||
getStream
|
||||
}),
|
||||
emitEvent: ({ eventName, payload }) =>
|
||||
getEventBus().emit({
|
||||
eventName,
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
const createStreamReturnRecord = createStreamReturnRecordFactory({
|
||||
inviteUsersToProject: inviteUsersToProjectFactory({
|
||||
createAndSendInvite: createAndSendInviteFactory({
|
||||
findUserByTarget: findUserByTargetFactory({ db }),
|
||||
insertInviteAndDeleteOld: insertInviteAndDeleteOldFactory({ db }),
|
||||
collectAndValidateResourceTargets: collectAndValidateCoreTargetsFactory({
|
||||
getStream
|
||||
}),
|
||||
getUsers
|
||||
buildInviteEmailContents: buildCoreInviteEmailContentsFactory({
|
||||
getStream
|
||||
}),
|
||||
emitEvent: ({ eventName, payload }) =>
|
||||
getEventBus().emit({
|
||||
eventName,
|
||||
payload
|
||||
}),
|
||||
getUser,
|
||||
getServerInfo,
|
||||
finalizeInvite: buildFinalizeProjectInvite()
|
||||
}),
|
||||
createStream: createStreamFactory({ db }),
|
||||
createBranch: createBranchFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
getUsers
|
||||
}),
|
||||
createStream: createStreamFactory({ db }),
|
||||
createBranch: createBranchFactory({ db }),
|
||||
emitEvent: getEventBus().emit
|
||||
})
|
||||
const createStream = legacyCreateStreamFactory({
|
||||
createStreamReturnRecord
|
||||
})
|
||||
|
||||
const findEmail = findEmailFactory({ db })
|
||||
@@ -353,9 +348,8 @@ function generateRandomCommentText() {
|
||||
return buildCommentInputFromString(crs({ length: 10 }))
|
||||
}
|
||||
|
||||
const mailerMock = EmailSendingServiceMock
|
||||
const commentRepoMock = CommentsRepositoryMock
|
||||
const streamsRepoMock = StreamsRepositoryMock
|
||||
const buildTestStream = () =>
|
||||
buildTestProject({ workspaceId: undefined, regionKey: undefined })
|
||||
|
||||
describe('Comments @comments', () => {
|
||||
let app: express.Express
|
||||
@@ -434,13 +428,6 @@ describe('Comments @comments', () => {
|
||||
|
||||
after(() => {
|
||||
notificationsState.destroy()
|
||||
commentRepoMock.destroy()
|
||||
streamsRepoMock.destroy()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
commentRepoMock.disable()
|
||||
commentRepoMock.resetMockedFunctions()
|
||||
})
|
||||
|
||||
it('Should be able to create a comment and a reply', async () => {
|
||||
@@ -1498,7 +1485,7 @@ describe('Comments @comments', () => {
|
||||
)
|
||||
})
|
||||
|
||||
const createComment = (input = {}) =>
|
||||
const createCommentGql = (input = {}) =>
|
||||
CommentsGraphQLClient.createComment(apollo, {
|
||||
input: {
|
||||
streamId: stream.id,
|
||||
@@ -1509,7 +1496,7 @@ describe('Comments @comments', () => {
|
||||
}
|
||||
})
|
||||
|
||||
const createReply = (input?: ReplyCreateInput) =>
|
||||
const createReplyGql = (input?: ReplyCreateInput) =>
|
||||
CommentsGraphQLClient.createReply(apollo, {
|
||||
input: {
|
||||
streamId: stream.id,
|
||||
@@ -1528,7 +1515,7 @@ describe('Comments @comments', () => {
|
||||
await truncateTables([Comments.name])
|
||||
|
||||
// Create a single comment with a blob
|
||||
const createCommentResult = await createComment({
|
||||
const createCommentResult = await createCommentGql({
|
||||
text: generateRandomCommentText(),
|
||||
blobIds: [blob1.blobId]
|
||||
})
|
||||
@@ -1536,7 +1523,7 @@ describe('Comments @comments', () => {
|
||||
if (!parentCommentId) throw new Error('Comment creation failed!')
|
||||
|
||||
// Create a reply with a blob
|
||||
await createReply({
|
||||
await createReplyGql({
|
||||
text: generateRandomCommentText(),
|
||||
blobIds: [blob1.blobId],
|
||||
parentComment: parentCommentId,
|
||||
@@ -1544,7 +1531,7 @@ describe('Comments @comments', () => {
|
||||
})
|
||||
|
||||
// Create a reply with a blob, but no text
|
||||
const emptyCommentResult = await createReply({
|
||||
const emptyCommentResult = await createReplyGql({
|
||||
blobIds: [blob1.blobId],
|
||||
parentComment: parentCommentId,
|
||||
streamId: stream.id
|
||||
@@ -1559,7 +1546,7 @@ describe('Comments @comments', () => {
|
||||
...(input || { id: '' })
|
||||
})
|
||||
|
||||
const readComments = (input = {}) =>
|
||||
const readComments = (input: Partial<GetCommentsQueryVariables> = {}) =>
|
||||
CommentsGraphQLClient.getComments(apollo, {
|
||||
cursor: null,
|
||||
streamId: stream.id,
|
||||
@@ -1567,61 +1554,99 @@ describe('Comments @comments', () => {
|
||||
})
|
||||
|
||||
it('both legacy (string) comments and new (ProseMirror) documents are formatted as SmartTextEditorValue values', async () => {
|
||||
commentRepoMock.enable()
|
||||
commentRepoMock.mockFunction('getCommentsLegacyFactory', () => {
|
||||
return async () => ({
|
||||
items: [
|
||||
// Legacy
|
||||
{
|
||||
id: 'a',
|
||||
text: 'hey dude! welcome to my legacy-type comment!',
|
||||
streamId: stream.id
|
||||
},
|
||||
// New
|
||||
{
|
||||
id: 'b',
|
||||
const streamId = await createStream({ ...buildTestStream(), ownerId: user.id })
|
||||
|
||||
await Promise.all([
|
||||
// Legacy
|
||||
createComment(
|
||||
{
|
||||
userId: user.id,
|
||||
input: {
|
||||
streamId,
|
||||
resources: [
|
||||
{ resourceId: streamId, resourceType: ResourceType.Stream }
|
||||
],
|
||||
text: 'hey dude! welcome to my legacy-type comment!' as unknown as SmartTextEditorValueSchema,
|
||||
data: {},
|
||||
blobIds: []
|
||||
}
|
||||
},
|
||||
{ skipTextValidation: true }
|
||||
),
|
||||
// New
|
||||
createComment(
|
||||
{
|
||||
userId: user.id,
|
||||
input: {
|
||||
streamId,
|
||||
resources: [
|
||||
{ resourceId: streamId, resourceType: ResourceType.Stream }
|
||||
],
|
||||
text: JSON.stringify(
|
||||
buildCommentTextFromInput({
|
||||
doc: buildCommentInputFromString('new comment schema here')
|
||||
})
|
||||
),
|
||||
streamId: stream.id
|
||||
},
|
||||
// New, but for some reason the text object is already deserialized
|
||||
{
|
||||
id: 'c',
|
||||
) as unknown as SmartTextEditorValueSchema,
|
||||
data: {},
|
||||
blobIds: []
|
||||
}
|
||||
},
|
||||
{ skipTextValidation: true }
|
||||
),
|
||||
// New, but for some reason the text object is already deserialized
|
||||
createComment(
|
||||
{
|
||||
userId: user.id,
|
||||
input: {
|
||||
streamId,
|
||||
resources: [
|
||||
{ resourceId: streamId, resourceType: ResourceType.Stream }
|
||||
],
|
||||
text: buildCommentTextFromInput({
|
||||
doc: buildCommentInputFromString('another new comment schema here')
|
||||
}),
|
||||
streamId: stream.id
|
||||
data: {},
|
||||
blobIds: []
|
||||
}
|
||||
] as unknown as Array<LegacyCommentRecord>,
|
||||
cursor: new Date().toISOString(),
|
||||
totalCount: 3
|
||||
})
|
||||
})
|
||||
},
|
||||
{ skipTextValidation: true }
|
||||
)
|
||||
])
|
||||
|
||||
const { data, errors } = await readComments()
|
||||
const { data, errors } = await readComments({
|
||||
streamId
|
||||
})
|
||||
|
||||
expect(errors?.length || 0).to.eq(0)
|
||||
expect(data?.comments?.items?.length || 0).to.eq(3)
|
||||
})
|
||||
|
||||
it('legacy comment with a single link is formatted correctly', async () => {
|
||||
const streamId = await createStream({ ...buildTestStream(), ownerId: user.id })
|
||||
|
||||
// Low-level insert cause all we need are just the main DB entries
|
||||
const item = {
|
||||
id: '1',
|
||||
text: 'https://aaa.com:3000/h3ll0-world/_?a=1&b=2#aaa',
|
||||
streamId: stream.id
|
||||
} as unknown as LegacyCommentRecord
|
||||
text: 'https://aaa.com:3000/h3ll0-world/_?a=1&b=2#aaa' as unknown as SmartTextEditorValueSchema,
|
||||
streamId,
|
||||
authorId: user.id
|
||||
}
|
||||
await createComment(
|
||||
{
|
||||
userId: user.id,
|
||||
input: {
|
||||
streamId,
|
||||
resources: [{ resourceId: streamId, resourceType: ResourceType.Stream }],
|
||||
text: item.text,
|
||||
data: {},
|
||||
blobIds: []
|
||||
}
|
||||
},
|
||||
{ skipTextValidation: true }
|
||||
)
|
||||
|
||||
commentRepoMock.enable()
|
||||
commentRepoMock.mockFunction('getCommentsLegacyFactory', () => async () => ({
|
||||
items: [item],
|
||||
cursor: new Date().toISOString(),
|
||||
totalCount: 1
|
||||
}))
|
||||
|
||||
const { data, errors } = await readComments()
|
||||
const { data, errors } = await readComments({
|
||||
streamId
|
||||
})
|
||||
|
||||
expect(data?.comments?.items?.length || 0).to.eq(1)
|
||||
expect(errors?.length || 0).to.eq(0)
|
||||
@@ -1637,6 +1662,8 @@ describe('Comments @comments', () => {
|
||||
})
|
||||
|
||||
it('legacy comment with multiple links formats them correctly', async () => {
|
||||
const streamId = await createStream({ ...buildTestStream(), ownerId: user.id })
|
||||
|
||||
const textParts = [
|
||||
"Here's one ",
|
||||
// The period and comma def shouldn't belong to the following URL, but we have a pretty basic
|
||||
@@ -1648,20 +1675,26 @@ describe('Comments @comments', () => {
|
||||
'http://agag.com:3000'
|
||||
]
|
||||
|
||||
// Low-level insert cause all we need are just the main DB entries
|
||||
const item = {
|
||||
id: '1',
|
||||
text: textParts.join(''),
|
||||
streamId: stream.id
|
||||
} as unknown as LegacyCommentRecord
|
||||
|
||||
commentRepoMock.enable()
|
||||
commentRepoMock.mockFunction('getCommentsLegacyFactory', () => async () => ({
|
||||
items: [item],
|
||||
cursor: new Date().toISOString(),
|
||||
totalCount: 1
|
||||
}))
|
||||
|
||||
const { data, errors } = await readComments()
|
||||
text: textParts.join('') as unknown as SmartTextEditorValueSchema,
|
||||
streamId,
|
||||
authorId: user.id
|
||||
}
|
||||
await createComment(
|
||||
{
|
||||
userId: user.id,
|
||||
input: {
|
||||
streamId,
|
||||
resources: [{ resourceId: streamId, resourceType: ResourceType.Stream }],
|
||||
text: item.text,
|
||||
data: {},
|
||||
blobIds: []
|
||||
}
|
||||
},
|
||||
{ skipTextValidation: true }
|
||||
)
|
||||
const { data, errors } = await readComments({ streamId })
|
||||
|
||||
const runExpectationsOnTextNode = (idx: number, shouldBeLink: boolean) => {
|
||||
expect(textNodes[idx].text).to.eq(textParts[idx])
|
||||
@@ -1724,7 +1757,7 @@ describe('Comments @comments', () => {
|
||||
})
|
||||
|
||||
it('returns raw text correctly', async () => {
|
||||
const { data } = await createReply({
|
||||
const { data } = await createReplyGql({
|
||||
text: {
|
||||
type: 'doc',
|
||||
content: [
|
||||
@@ -1761,43 +1794,6 @@ describe('Comments @comments', () => {
|
||||
expect(data?.comment?.text?.doc).to.be.null
|
||||
expect(data?.comment?.text?.attachments?.length).to.be.greaterThan(0)
|
||||
})
|
||||
|
||||
const unexpectedValDataset = [
|
||||
{ display: 'number', value: 3 },
|
||||
{ display: 'random object', value: { a: 1, b: 2 } }
|
||||
]
|
||||
unexpectedValDataset.forEach(({ display, value }) => {
|
||||
it(`unexpected text value (${display}) in DB throw sanitized errors`, async () => {
|
||||
streamsRepoMock.enable()
|
||||
streamsRepoMock.mockFunction('getStreamsFactory', () => async () => [
|
||||
{
|
||||
id: stream.id,
|
||||
workspaceId: ''
|
||||
} as unknown as StreamRecord
|
||||
])
|
||||
const item = {
|
||||
id: '1',
|
||||
text: value,
|
||||
streamId: stream.id,
|
||||
createdAt: new Date()
|
||||
} as unknown as LegacyCommentRecord
|
||||
|
||||
commentRepoMock.enable()
|
||||
commentRepoMock.mockFunction('getCommentsLegacyFactory', () => async () => ({
|
||||
items: [item],
|
||||
cursor: new Date().toISOString(),
|
||||
totalCount: 1
|
||||
}))
|
||||
|
||||
const { errors } = await readComments()
|
||||
|
||||
expect((errors || []).map((e) => e.message).join(';')).to.contain(
|
||||
'Unexpected comment schema format'
|
||||
)
|
||||
streamsRepoMock.disable()
|
||||
streamsRepoMock.resetMockedFunctions()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const creatingOrReplyingDataSet = [
|
||||
@@ -1809,8 +1805,8 @@ describe('Comments @comments', () => {
|
||||
|
||||
const createOrReplyComment = (input = {}) =>
|
||||
creating
|
||||
? createComment(input)
|
||||
: createReply({
|
||||
? createCommentGql(input)
|
||||
: createReplyGql({
|
||||
parentComment: parentCommentId,
|
||||
blobIds: [],
|
||||
streamId: stream.id,
|
||||
@@ -1825,7 +1821,7 @@ describe('Comments @comments', () => {
|
||||
before(async () => {
|
||||
if (replying) {
|
||||
// Create comment for attaching replies to
|
||||
const { data } = await createComment({
|
||||
const { data } = await createCommentGql({
|
||||
text: generateRandomCommentText()
|
||||
})
|
||||
|
||||
@@ -1920,6 +1916,20 @@ describe('Comments @comments', () => {
|
||||
})
|
||||
|
||||
describe('and mentioning a user', () => {
|
||||
let emailListener: TestEmailListener
|
||||
|
||||
before(async () => {
|
||||
emailListener = await createEmailListener()
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
await emailListener.destroy()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
emailListener.reset()
|
||||
})
|
||||
|
||||
const createOrReplyCommentWithMention = (targetUserId: string, input = {}) =>
|
||||
createOrReplyComment({
|
||||
text: {
|
||||
@@ -1942,10 +1952,7 @@ describe('Comments @comments', () => {
|
||||
})
|
||||
|
||||
it('a valid mention triggers a notification', async () => {
|
||||
const sendEmailInvocations = mailerMock.hijackFunction(
|
||||
'sendEmail',
|
||||
async () => false
|
||||
)
|
||||
const { getSends } = emailListener.listen({ times: 2 })
|
||||
|
||||
const waitForAck = notificationsState.waitForAck(
|
||||
(e) => e.result?.type === NotificationType.MentionedInComment
|
||||
@@ -1960,7 +1967,8 @@ describe('Comments @comments', () => {
|
||||
// Wait for
|
||||
await waitForAck
|
||||
|
||||
const emailParams = sendEmailInvocations.args[0][0]
|
||||
const emailSends = getSends()
|
||||
const emailParams = emailSends[0]
|
||||
expect(emailParams).to.be.ok
|
||||
expect(emailParams.subject).to.contain('mentioned in a Speckle comment')
|
||||
expect(emailParams.to).to.eq(otherUser.email)
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Optional } from '@speckle/shared'
|
||||
import knex from '@/db/knex'
|
||||
import { BaseMetaRecord } from '@/modules/core/helpers/meta'
|
||||
import { Knex } from 'knex'
|
||||
import { reduce } from 'lodash'
|
||||
import { reduce } from 'lodash-es'
|
||||
|
||||
type BaseInnerSchemaConfig<T extends string, C extends string> = {
|
||||
/**
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
UserSubscriptions,
|
||||
WorkspaceSubscriptions
|
||||
} from '@/modules/shared/utils/subscriptions'
|
||||
import { chunk, flatten } from 'lodash'
|
||||
import { chunk, flatten } from 'lodash-es'
|
||||
|
||||
const reportModelCreatedFactory =
|
||||
(deps: { publish: PublishSubscription }) =>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user