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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { randomUUID } from 'crypto'
import { beforeEach, describe, expect, it, vi } from 'vitest'

import createTable from '@/graphql/mutations/tiles/create-table'
import Flow from '@/models/flow'
import TableCollaborator from '@/models/table-collaborators'
import * as ddbTableRowFunctions from '@/models/tiles/dynamodb/table-row/functions'
import * as pgTableFunctions from '@/models/tiles/pg/table-functions'
import * as pgTableRowFunctions from '@/models/tiles/pg/table-row-functions'
import { DatabaseType } from '@/models/tiles/types'
import User from '@/models/user'
import Context from '@/types/express/context'

import { generateMockCollaborator, generateMockUser } from '../flow.mock'

import { generateMockContext } from './table.mock'
import { checkIfTableExists } from './tiles-pg-helper'

Expand All @@ -22,6 +28,22 @@ vi.mock('@/helpers/launch-darkly', () => ({
getLdFlagValue: mocks.getLdFlagValue,
}))

async function generateFlowWithCollaborators(ownerId: string) {
const flow = await Flow.query().insert({
id: randomUUID(),
name: 'Test Flow',
userId: ownerId,
})

const editor = await generateMockUser('editor')
const viewer = await generateMockUser('viewer')

await generateMockCollaborator(flow.id, editor.id, ownerId, 'editor')
await generateMockCollaborator(flow.id, viewer.id, ownerId, 'viewer')

return { flow, editor, viewer }
}

describe.each([['pg'], ['ddb']])(
'create table mutation: %s',
(databaseType: DatabaseType) => {
Expand Down Expand Up @@ -94,5 +116,60 @@ describe.each([['pg'], ['ddb']])(
createTable(null, { input: { name: '', isBlank: false } }, context),
).rejects.toThrow()
})

describe('with flowId - table_collaborators updates', () => {
let testFlow: Flow
let flowEditor: User
let flowViewer: User

beforeEach(async () => {
const flowData = await generateFlowWithCollaborators(
context.currentUser.id,
)
testFlow = flowData.flow
flowEditor = flowData.editor
flowViewer = flowData.viewer
})

it('should add all flow collaborators to table_collaborators when flowId is provided', async () => {
mocks.getLdFlagValue.mockResolvedValueOnce(databaseType)

const table = await createTable(
null,
{
input: {
name: 'Flow Table',
isBlank: true,
flowId: testFlow.id,
},
},
context,
)

// Verify flow owner is added as table editor
const ownerCollab = await TableCollaborator.query().findOne({
user_id: testFlow.userId,
table_id: table.id,
})
expect(ownerCollab).toBeDefined()
expect(ownerCollab.role).toBe('owner')

// Verify flow editor is added as table editor
const editorCollab = await TableCollaborator.query().findOne({
user_id: flowEditor.id,
table_id: table.id,
})
expect(editorCollab).toBeDefined()
expect(editorCollab.role).toBe('editor')

// Verify flow viewer is added as table viewer
const viewerCollab = await TableCollaborator.query().findOne({
user_id: flowViewer.id,
table_id: table.id,
})
expect(viewerCollab).toBeDefined()
expect(viewerCollab.role).toBe('viewer')
})
})
},
)
19 changes: 18 additions & 1 deletion packages/backend/src/graphql/mutations/tiles/create-table.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { MutationResolvers } from '@/graphql/__generated__/types.generated'
import { addFlowTableConnection } from '@/helpers/add-flow-connection'
import { getLdFlagValue } from '@/helpers/launch-darkly'
import TableMetadata from '@/models/table-metadata'
import { getTableOperations } from '@/models/tiles/factory'
Expand Down Expand Up @@ -28,7 +29,7 @@ const createTable: MutationResolvers['createTable'] = async (
params,
context,
) => {
const { name: tableName, isBlank: isBlankTable } = params.input
const { name: tableName, isBlank: isBlankTable, flowId } = params.input

if (!tableName) {
throw new Error('Table name is required')
Expand Down Expand Up @@ -70,6 +71,22 @@ const createTable: MutationResolvers['createTable'] = async (
})
}

/**
* COLLABORATORS
* Owners have the ability to create tables from within the Pipe Editor.
*
* If the Pipe has collaborators, we need to:
* 1. Add the Pipe collaborators as Tile collaborators
* 2. Add the Tile to the `flow_connections` table
*/
if (flowId) {
await addFlowTableConnection({
flowId,
tableId: table.id,
addedBy: context.currentUser.id,
})
}

return table
}

Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -940,6 +940,7 @@ enum DatabaseType {
input CreateTableInput {
name: String!
isBlank: Boolean!
flowId: String
}

input TableColumnConfigInput {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,20 @@ interface CreateNewOptionProps {
inputValue: string
parameters: Record<string, unknown>
addNewId?: DropdownAddNewId
flowId?: string
}

export function useCreateNewOption(setValue: (newValue: string) => void) {
const [createTable] = useMutation(CREATE_TABLE)
const [updateTable] = useMutation(UPDATE_TABLE)
const [isCreatingNewOption, setIsCreatingNewOption] = useState(false)
const createNewOption = useCallback(
async ({ inputValue, addNewId, parameters }: CreateNewOptionProps) => {
async ({
inputValue,
addNewId,
parameters,
flowId,
}: CreateNewOptionProps) => {
if (!inputValue.trim() || !addNewId) {
return
}
Expand All @@ -50,6 +56,7 @@ export function useCreateNewOption(setValue: (newValue: string) => void) {
input: {
name: inputValue.trim(),
isBlank: true,
flowId,
},
},
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ function ControlledAutocomplete(
isSearchable,
variableTypes = null,
} = props
const { allApps, readOnly } = useContext(EditorContext)
const { allApps, readOnly, flowId } = useContext(EditorContext)
const { priorExecutionSteps } = useContext(StepExecutionsContext)
/**
* allow extraction of specific variables to a dropdown, e.g., files from FormSG
Expand Down Expand Up @@ -144,9 +144,16 @@ function ControlledAutocomplete(
inputValue,
addNewId: addNewOption?.id,
parameters: getValues('parameters'),
flowId,
})
},
[addNewOption?.id, createNewOption, getValues, onNewOptionModalClose],
[
addNewOption?.id,
createNewOption,
getValues,
onNewOptionModalClose,
flowId,
],
)

const onNewOptionInlineSelected = useCallback(
Expand Down
Loading