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
Expand Up @@ -726,7 +726,14 @@ describe('updateStep mutation', () => {
flowId: mockFlowId,
trx: expect.anything(),
})
expect(addCollaboratorSpy).toHaveBeenCalledTimes(2)
expect(addCollaboratorSpy).toHaveBeenCalledTimes(3)
// this is called when the editor adds a tile to the flow
expect(addCollaboratorSpy).toHaveBeenCalledWith({
userId: owner.id,
tableId: mockTableId,
role: 'editor',
trx: expect.anything(),
})
expect(addCollaboratorSpy).toHaveBeenCalledWith({
userId: editor.id,
tableId: mockTableId,
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/graphql/mutations/delete-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const deleteFlow: MutationResolvers['deleteFlow'] = async (
await flow.$relatedQuery('executions').delete()
await flow.$relatedQuery('steps').delete()
await flow.$relatedQuery('pendingTransfer').delete()
await flow.$relatedQuery('flowConnections').delete()

// delete attachments from s3
// Note: specify object keys individually, cannot delete entire folder
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import FlowConnections from '@/models/flow-connections'
import TableCollaborator from '@/models/table-collaborators'
import TableMetadata from '@/models/table-metadata'

Expand All @@ -23,6 +24,12 @@ const deleteTable: MutationResolvers['deleteTable'] = async (
await TableCollaborator.query().where({ table_id: params.input.id }).patch({
deletedAt: new Date().toISOString(),
})

// remove the table from the flow connections table
await FlowConnections.query(trx)
.where({ connection_id: params.input.id, connection_type: 'table' })
.delete()

await table.$query(trx).delete()
})

Expand Down
46 changes: 18 additions & 28 deletions packages/backend/src/graphql/mutations/update-step.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,38 +104,28 @@ const updateStep: MutationResolvers['updateStep'] = async (
/**
* NOTE: we need to update flow connections for apps with connections:
* Tiles:
* 1. add the collaborator to the flow connections table
* 1. add the tile to the flow connections table
* 2. add the collaborator to the table collaborators table
*
* TODO (kevinkim-ogp): phase 2
* collaborator should be able to add their own tiles,
* and the owner will be added as an editor to the Tile
*
* Other connections:
* 1. add the collaborator to the flow connections table
*
* TODO (kevinkim-ogp): phase 2
* collaborator should be able to add their own connections,
* it will be tied to this specific flow only
* 1. add the connection to the flow connections table
*/
if (step.flow.role === 'owner') {
const appKey = updatedStep?.appKey

// tiles special handling
if (appKey === 'tiles' && updatedStep?.parameters?.tableId) {
await addFlowTableConnection({
flowId: updatedStep.flowId,
tableId: updatedStep.parameters.tableId as string,
addedBy: context.currentUser.id,
trx,
})
} else if (updatedStep?.connectionId) {
await addFlowConnection({
step: updatedStep,
addedBy: context.currentUser.id,
trx,
})
}
const appKey = updatedStep?.appKey

// tiles special handling
if (appKey === 'tiles' && updatedStep?.parameters?.tableId) {
await addFlowTableConnection({
flowId: updatedStep.flowId,
tableId: updatedStep.parameters.tableId as string,
addedBy: context.currentUser.id,
trx,
})
} else if (step.flow.role === 'owner' && updatedStep?.connectionId) {
await addFlowConnection({
step: updatedStep,
addedBy: context.currentUser.id,
trx,
})
}

// update the flow's last updated
Expand Down
46 changes: 30 additions & 16 deletions packages/backend/src/graphql/queries/get-dynamic-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,12 @@ const getDynamicData: QueryResolvers['getDynamicData'] = async (
app,
flow: step.flow,
step,
user: step.flow.user,
/**
* if the user is a viewer, use the flow owner's user so that they can see all the options.
* if the user is an editor or owner, use the current user so that they can see their own options.
* the editor would already have access to the options via the flow_connections table.
*/
user: step.flow.role === 'viewer' ? step.flow.user : context.currentUser,
})

const command = app.dynamicData.find((data) => data.key === dynamicDataKey)
Expand All @@ -55,13 +60,12 @@ const getDynamicData: QueryResolvers['getDynamicData'] = async (
* filter out the dynamic data to only show the options that have been shared with the user
*
* Tiles:
* - only show the Tiles that have been shared
* - show the Tiles that have been shared
* - show the user's own Tiles (can be added and shared)
*
* Other connections:
* - only show the connections that have been shared
*
* TODO (kevinkim-ogp): phase 2
* - collaborator should be able to add their own Tiles
*/
if (
step.flow.role !== 'owner' &&
Expand All @@ -70,18 +74,28 @@ const getDynamicData: QueryResolvers['getDynamicData'] = async (
) {
switch (step.appKey) {
case 'tiles': {
const flowConnections = await context.currentUser
.withAccessibleFlowConnections({ requiredRole: 'viewer' })
.where({
connection_type: 'table',
'flow_connections.flow_id': step.flowId,
})

return fetchedData.data.filter((data) =>
flowConnections.some(
(flowConnection) => flowConnection.connectionId === data.value,
),
)
/**
* we still need to filter the data for viewers as they should only
* see the Tiles that have been shared in this Pipe.
* they should not be able to see their own or the owner's Tiles
*/
if (step.flow.role === 'viewer') {
const flowConnections = await context.currentUser
.withAccessibleFlowConnections({ requiredRole: 'viewer' })
.where({
connection_type: 'table',
'flow_connections.flow_id': step.flowId,
})

return fetchedData.data.filter((data) =>
flowConnections.some(
(flowConnection) => flowConnection.connectionId === data.value,
),
)
}

// editors and owners should see both the shared and all their own Tiles
return fetchedData.data
}

default: {
Expand Down
11 changes: 11 additions & 0 deletions packages/backend/src/helpers/add-flow-connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { IStep } from '@plumber/types'

import { Transaction } from 'objection'

import Flow from '@/models/flow'
import FlowCollaborator from '@/models/flow-collaborators'
import FlowConnections from '@/models/flow-connections'
import TableCollaborator from '@/models/table-collaborators'
Expand Down Expand Up @@ -85,6 +86,16 @@ async function addFlowTableConnection({
trx,
})

// now that editors can also add their own tiles, we also need to add the flow owner
// as an editor to the tile
const flow = await Flow.query(trx).findOne({ id: flowId })
await TableCollaborator.upgradeOrInsertCollaborator({
userId: flow.userId,
tableId,
role: 'editor',
trx,
})

// use Promise.all so that we use the addCollaborator function, which checks
// if the collaborator already exists to avoid duplicates
await Promise.all(
Expand Down
9 changes: 9 additions & 0 deletions packages/backend/src/models/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { doesActionProcessFiles } from '@/helpers/actions'
import Base from './base'
import Execution from './execution'
import FlowCollaborator from './flow-collaborators'
import FlowConnections from './flow-connections'
import FlowTransfer from './flow-transfers'
import ExtendedQueryBuilder from './query-builder'
import Step from './step'
Expand Down Expand Up @@ -139,6 +140,14 @@ class Flow extends Base {
builder.whereNull('deleted_at')
},
},
flowConnections: {
relation: Base.HasManyRelation,
modelClass: FlowConnections,
join: {
from: `${this.tableName}.id`,
to: `${FlowConnections.tableName}.flow_id`,
},
},
})

static hasAccess = async (
Expand Down
Loading