From 4d5f96bb8ecd72731560d4dba3012da4edbb1bf5 Mon Sep 17 00:00:00 2001 From: Chuck Driesler Date: Sun, 12 Jan 2025 00:03:14 +0000 Subject: [PATCH 01/56] feat(regions): repo functions for copying project branches and commits --- .../typedefs/workspaces.graphql | 1 + .../modules/core/repositories/objects.ts | 882 +++++++++--------- .../server/modules/shared/helpers/dbHelper.ts | 4 +- .../modules/workspaces/domain/operations.ts | 8 + .../workspaces/repositories/regions.ts | 174 +++- .../modules/workspaces/services/projects.ts | 277 +++--- 6 files changed, 751 insertions(+), 595 deletions(-) diff --git a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql index 226b28a18..8c5d69997 100644 --- a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql +++ b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql @@ -170,6 +170,7 @@ type WorkspaceProjectMutations { updateRole(input: ProjectUpdateRoleInput!): Project! @hasStreamRole(role: STREAM_OWNER) @hasWorkspaceRole(role: MEMBER) + moveToRegion(projectId: String!, regionKey: String!): Project! moveToWorkspace(projectId: String!, workspaceId: String!): Project! create(input: WorkspaceProjectCreateInput!): Project! } diff --git a/packages/server/modules/core/repositories/objects.ts b/packages/server/modules/core/repositories/objects.ts index 851bf78fa..bdf8a82ef 100644 --- a/packages/server/modules/core/repositories/objects.ts +++ b/packages/server/modules/core/repositories/objects.ts @@ -40,298 +40,105 @@ const tables = { export const getStreamObjectsFactory = (deps: { db: Knex }): GetStreamObjects => - async (streamId: string, objectIds: string[]): Promise => { - if (!objectIds?.length) return [] + async (streamId: string, objectIds: string[]): Promise => { + if (!objectIds?.length) return [] - const q = tables - .objects(deps.db) - .where(Objects.col.streamId, streamId) - .whereIn(Objects.col.id, objectIds) + const q = tables + .objects(deps.db) + .where(Objects.col.streamId, streamId) + .whereIn(Objects.col.id, objectIds) - return await q - } + return await q + } export const getObjectFactory = (deps: { db: Knex }): GetObject => - async (objectId: string, streamId: string): Promise> => { - return await tables - .objects(deps.db) - .where(Objects.col.id, objectId) - .andWhere(Objects.col.streamId, streamId) - .first() - } + async (objectId: string, streamId: string): Promise> => { + return await tables + .objects(deps.db) + .where(Objects.col.id, objectId) + .andWhere(Objects.col.streamId, streamId) + .first() + } export const getFormattedObjectFactory = (deps: { db: Knex }): GetFormattedObject => - async ({ streamId, objectId }) => { - const res = await tables - .objects(deps.db) - .where({ streamId, id: objectId }) - .select('*') - .first() - if (!res) return null + async ({ streamId, objectId }) => { + const res = await tables + .objects(deps.db) + .where({ streamId, id: objectId }) + .select('*') + .first() + if (!res) return null - // TODO: Why tho? A lot if not most of places already just use getObjectFactory, - const finalRes: SetOptional = res - if (finalRes.data) finalRes.data.totalChildrenCount = res.totalChildrenCount // move this back - delete finalRes.streamId // backwards compatibility + // TODO: Why tho? A lot if not most of places already just use getObjectFactory, + const finalRes: SetOptional = res + if (finalRes.data) finalRes.data.totalChildrenCount = res.totalChildrenCount // move this back + delete finalRes.streamId // backwards compatibility - return finalRes - } + return finalRes + } export const getBatchedStreamObjectsFactory = (deps: { db: Knex }): GetBatchedStreamObjects => - (streamId: string, options?: Partial) => { - const baseQuery = tables - .objects(deps.db) - .select('*') - .where(Objects.col.streamId, streamId) - .orderBy(Objects.col.id) + (streamId: string, options?: Partial) => { + const baseQuery = tables + .objects(deps.db) + .select('*') + .where(Objects.col.streamId, streamId) + .orderBy(Objects.col.id) - return executeBatchedSelect(baseQuery, options) - } + return executeBatchedSelect(baseQuery, options) + } export const insertObjectsFactory = (deps: { db: Knex }): StoreObjects => - async (objects: ObjectRecord[], options?: Partial<{ trx: Knex.Transaction }>) => { - const q = tables.objects(deps.db).insert(objects) - if (options?.trx) q.transacting(options.trx) - return await q - } + async (objects: ObjectRecord[], options?: Partial<{ trx: Knex.Transaction }>) => { + const q = tables.objects(deps.db).insert(objects) + if (options?.trx) q.transacting(options.trx) + return await q + } export const storeSingleObjectIfNotFoundFactory = (deps: { db: Knex }): StoreSingleObjectIfNotFound => - async (insertionObject) => { - await tables - .objects(deps.db) - .insert( - // knex is bothered by string being inserted into jsonb, which is actually fine - insertionObject as SpeckleObject - ) - .onConflict() - .ignore() - } + async (insertionObject) => { + await tables + .objects(deps.db) + .insert( + // knex is bothered by string being inserted into jsonb, which is actually fine + insertionObject as SpeckleObject + ) + .onConflict() + .ignore() + } export const storeObjectsIfNotFoundFactory = (deps: { db: Knex }): StoreObjectsIfNotFound => - async (batch) => { - await tables - .objects(deps.db) - .insert( - // knex is bothered by string being inserted into jsonb, which is actually fine - batch as SpeckleObject[] - ) - .onConflict() - .ignore() - } + async (batch) => { + await tables + .objects(deps.db) + .insert( + // knex is bothered by string being inserted into jsonb, which is actually fine + batch as SpeckleObject[] + ) + .onConflict() + .ignore() + } export const storeClosuresIfNotFoundFactory = (deps: { db: Knex }): StoreClosuresIfNotFound => - async (closuresBatch) => { - await tables - .objectChildrenClosure(deps.db) - .insert(closuresBatch) - .onConflict() - .ignore() - } + async (closuresBatch) => { + await tables + .objectChildrenClosure(deps.db) + .insert(closuresBatch) + .onConflict() + .ignore() + } export const getObjectChildrenStreamFactory = (deps: { db: Knex }): GetObjectChildrenStream => - async ({ streamId, objectId }) => { - const q = deps.db.with( - 'object_children_closure', - knex.raw( - `SELECT objects.id as parent, d.key as child, d.value as mindepth, ? as "streamId" - FROM objects - JOIN jsonb_each_text(objects.data->'__closure') d ON true - where objects.id = ?`, - [streamId, objectId] - ) - ) - q.select('id') - q.select(knex.raw('data::text as "dataText"')) - q.from('object_children_closure') - - q.rightJoin('objects', function () { - this.on('objects.streamId', '=', 'object_children_closure.streamId').andOn( - 'objects.id', - '=', - 'object_children_closure.child' - ) - }) - .where( - knex.raw('object_children_closure."streamId" = ? AND parent = ?', [ - streamId, - objectId - ]) - ) - .orderBy('objects.id') - return q.stream({ highWaterMark: 500 }) - } - -export const getObjectsStreamFactory = - (deps: { db: Knex }): GetObjectsStream => - async ({ streamId, objectIds }) => { - const res = tables - .objects(deps.db) - .whereIn('id', objectIds) - .andWhere('streamId', streamId) - .orderBy('id') - .select( - knex.raw( - '"id", "speckleType", "totalChildrenCount", "totalChildrenCountByDepth", "createdAt", data::text as "dataText"' - ) - ) - return res.stream({ highWaterMark: 500 }) - } - -export const hasObjectsFactory = - (deps: { db: Knex }): HasObjects => - async ({ streamId, objectIds }) => { - const dbRes = await tables - .objects(deps.db) - .whereIn('id', objectIds) - .andWhere('streamId', streamId) - .select('id') - - const res: Record = {} - // eslint-disable-next-line @typescript-eslint/no-for-in-array - for (const i in objectIds) { - res[objectIds[i]] = false - } - // eslint-disable-next-line @typescript-eslint/no-for-in-array - for (const i in dbRes) { - res[dbRes[i].id] = true - } - return res - } - -export const getObjectChildrenFactory = - (deps: { db: Knex }): GetObjectChildren => - async ({ streamId, objectId, limit, depth, select, cursor }) => { - limit = toNumber(limit || 0) || 50 - depth = toNumber(depth || 0) || 1000 - - let fullObjectSelect = false - - const q = deps.db.with( - 'object_children_closure', - knex.raw( - `SELECT objects.id as parent, d.key as child, d.value as mindepth, ? as "streamId" - FROM objects - JOIN jsonb_each_text(objects.data->'__closure') d ON true - where objects.id = ?`, - [streamId, objectId] - ) - ) - - if (Array.isArray(select)) { - select.forEach((field, index) => { - q.select( - knex.raw('jsonb_path_query(data, :path) as :name:', { - path: '$.' + field, - name: '' + index - }) - ) - }) - } else { - fullObjectSelect = true - q.select('data') - } - - q.select('id') - q.select('createdAt') - q.select('speckleType') - q.select('totalChildrenCount') - - q.from('object_children_closure') - - q.rightJoin('objects', function () { - this.on('objects.streamId', '=', 'object_children_closure.streamId').andOn( - 'objects.id', - '=', - 'object_children_closure.child' - ) - }) - .where( - knex.raw('object_children_closure."streamId" = ? AND parent = ?', [ - streamId, - objectId - ]) - ) - .andWhere(knex.raw('object_children_closure.mindepth < ?', [depth])) - .andWhere(knex.raw('id > ?', [cursor ? cursor : '0'])) - .orderBy('objects.id') - .limit(limit) - - const rows = await q - - if (rows.length === 0) { - return { objects: rows, cursor: null } - } - - if (!fullObjectSelect) - rows.forEach((o, i, arr) => { - const no = { - id: o.id, - createdAt: o.createdAt, - speckleType: o.speckleType, - totalChildrenCount: o.totalChildrenCount, - data: {} - } - let k = 0 - for (const field of select || []) { - set(no.data, field, o[k++]) - } - arr[i] = no - }) - - const lastId = rows[rows.length - 1].id - return { objects: rows, cursor: lastId } - } - -/** - * This query is inefficient on larger sets (n * 10k objects) as we need to return the total count on an arbitrarily (user) defined selection of objects. - * A possible future optimisation route would be to cache the total count of a query (as objects are immutable, it will not change) on a first run, and, if found on a subsequent round, do a simpler query and merge the total count result. - */ -export const getObjectChildrenQueryFactory = - (deps: { db: Knex }): GetObjectChildrenQuery => - async (params) => { - const { streamId, objectId, select, query } = params - - const limit = toNumber(params.limit || 0) || 50 - const depth = toNumber(params.depth || 0) || 1000 - const orderBy = params.orderBy || { field: 'id', direction: 'asc' } - - // Cursors received by this service should be base64 encoded. They are generated on first entry query by this service; They should never be client-side generated. - const cursor: Optional<{ - value: unknown - operator: string - field: string - lastSeenId?: string - }> = params.cursor - ? JSON.parse(Buffer.from(params.cursor, 'base64').toString('binary')) - : undefined - - // Flag that keeps track of whether we select the whole "data" part of an object or not - let fullObjectSelect = false - if (Array.isArray(select)) { - // if we order by a field that we do not select, select it! - if (orderBy && select.indexOf(orderBy.field) === -1) { - select.push(orderBy.field) - } - // // always add the id! - // if ( select.indexOf( 'id' ) === -1 ) select.unshift( 'id' ) - } else { - fullObjectSelect = true - } - - const additionalIdOrderBy = orderBy.field !== 'id' - - const operatorsWhitelist = ['=', '>', '>=', '<', '<=', '!='] - - const mainQuery = deps.db - .with( + async ({ streamId, objectId }) => { + const q = deps.db.with( 'object_children_closure', knex.raw( `SELECT objects.id as parent, d.key as child, d.value as mindepth, ? as "streamId" @@ -341,201 +148,394 @@ export const getObjectChildrenQueryFactory = [streamId, objectId] ) ) - .with('objs', (cteInnerQuery) => { - // always select the id - cteInnerQuery.select('id').from('object_children_closure') - cteInnerQuery.select('createdAt') - cteInnerQuery.select('speckleType') - cteInnerQuery.select('totalChildrenCount') + q.select('id') + q.select(knex.raw('data::text as "dataText"')) + q.from('object_children_closure') - // if there are any select fields, add them - if (Array.isArray(select)) { - select.forEach((field, index) => { - cteInnerQuery.select( - knex.raw('jsonb_path_query(data, :path) as :name:', { - path: '$.' + field, - name: '' + index - }) - ) - }) - // otherwise, get the whole object, as stored in the jsonb column - } else { - cteInnerQuery.select('data') + q.rightJoin('objects', function () { + this.on('objects.streamId', '=', 'object_children_closure.streamId').andOn( + 'objects.id', + '=', + 'object_children_closure.child' + ) + }) + .where( + knex.raw('object_children_closure."streamId" = ? AND parent = ?', [ + streamId, + objectId + ]) + ) + .orderBy('objects.id') + return q.stream({ highWaterMark: 500 }) + } + +export const getObjectsStreamFactory = + (deps: { db: Knex }): GetObjectsStream => + async ({ streamId, objectIds }) => { + const res = tables + .objects(deps.db) + .whereIn('id', objectIds) + .andWhere('streamId', streamId) + .orderBy('id') + .select( + knex.raw( + '"id", "speckleType", "totalChildrenCount", "totalChildrenCountByDepth", "createdAt", data::text as "dataText"' + ) + ) + return res.stream({ highWaterMark: 500 }) + } + +export const hasObjectsFactory = + (deps: { db: Knex }): HasObjects => + async ({ streamId, objectIds }) => { + const dbRes = await tables + .objects(deps.db) + .whereIn('id', objectIds) + .andWhere('streamId', streamId) + .select('id') + + const res: Record = {} + // eslint-disable-next-line @typescript-eslint/no-for-in-array + for (const i in objectIds) { + res[objectIds[i]] = false + } + // eslint-disable-next-line @typescript-eslint/no-for-in-array + for (const i in dbRes) { + res[dbRes[i].id] = true + } + return res + } + +export const getObjectChildrenFactory = + (deps: { db: Knex }): GetObjectChildren => + async ({ streamId, objectId, limit, depth, select, cursor }) => { + limit = toNumber(limit || 0) || 50 + depth = toNumber(depth || 0) || 1000 + + let fullObjectSelect = false + + const q = deps.db.with( + 'object_children_closure', + knex.raw( + `SELECT objects.id as parent, d.key as child, d.value as mindepth, ? as "streamId" + FROM objects + JOIN jsonb_each_text(objects.data->'__closure') d ON true + where objects.id = ?`, + [streamId, objectId] + ) + ) + + if (Array.isArray(select)) { + select.forEach((field, index) => { + q.select( + knex.raw('jsonb_path_query(data, :path) as :name:', { + path: '$.' + field, + name: '' + index + }) + ) + }) + } else { + fullObjectSelect = true + q.select('data') + } + + q.select('id') + q.select('createdAt') + q.select('speckleType') + q.select('totalChildrenCount') + + q.from('object_children_closure') + + q.rightJoin('objects', function () { + this.on('objects.streamId', '=', 'object_children_closure.streamId').andOn( + 'objects.id', + '=', + 'object_children_closure.child' + ) + }) + .where( + knex.raw('object_children_closure."streamId" = ? AND parent = ?', [ + streamId, + objectId + ]) + ) + .andWhere(knex.raw('object_children_closure.mindepth < ?', [depth])) + .andWhere(knex.raw('id > ?', [cursor ? cursor : '0'])) + .orderBy('objects.id') + .limit(limit) + + const rows = await q + + if (rows.length === 0) { + return { objects: rows, cursor: null } + } + + if (!fullObjectSelect) + rows.forEach((o, i, arr) => { + const no = { + id: o.id, + createdAt: o.createdAt, + speckleType: o.speckleType, + totalChildrenCount: o.totalChildrenCount, + data: {} + } + let k = 0 + for (const field of select || []) { + set(no.data, field, o[k++]) + } + arr[i] = no + }) + + const lastId = rows[rows.length - 1].id + return { objects: rows, cursor: lastId } + } + +/** + * This query is inefficient on larger sets (n * 10k objects) as we need to return the total count on an arbitrarily (user) defined selection of objects. + * A possible future optimisation route would be to cache the total count of a query (as objects are immutable, it will not change) on a first run, and, if found on a subsequent round, do a simpler query and merge the total count result. + */ +export const getObjectChildrenQueryFactory = + (deps: { db: Knex }): GetObjectChildrenQuery => + async (params) => { + const { streamId, objectId, select, query } = params + + const limit = toNumber(params.limit || 0) || 50 + const depth = toNumber(params.depth || 0) || 1000 + const orderBy = params.orderBy || { field: 'id', direction: 'asc' } + + // Cursors received by this service should be base64 encoded. They are generated on first entry query by this service; They should never be client-side generated. + const cursor: Optional<{ + value: unknown + operator: string + field: string + lastSeenId?: string + }> = params.cursor + ? JSON.parse(Buffer.from(params.cursor, 'base64').toString('binary')) + : undefined + + // Flag that keeps track of whether we select the whole "data" part of an object or not + let fullObjectSelect = false + if (Array.isArray(select)) { + // if we order by a field that we do not select, select it! + if (orderBy && select.indexOf(orderBy.field) === -1) { + select.push(orderBy.field) } + // // always add the id! + // if ( select.indexOf( 'id' ) === -1 ) select.unshift( 'id' ) + } else { + fullObjectSelect = true + } - // join on objects table - cteInnerQuery - .join('objects', function () { - this.on('objects.streamId', '=', 'object_children_closure.streamId').andOn( - 'objects.id', - '=', - 'object_children_closure.child' - ) - }) - .where('object_children_closure.streamId', streamId) - .andWhere('parent', objectId) - .andWhere('mindepth', '<', depth) + const additionalIdOrderBy = orderBy.field !== 'id' - // Add user provided filters/queries. - if (Array.isArray(query) && query.length > 0) { - cteInnerQuery.andWhere((nestedWhereQuery) => { - query.forEach((statement, index) => { - let castType = 'text' - if (typeof statement.value === 'string') castType = 'text' - if (typeof statement.value === 'boolean') castType = 'boolean' - if (typeof statement.value === 'number') castType = 'numeric' + const operatorsWhitelist = ['=', '>', '>=', '<', '<=', '!='] - if (operatorsWhitelist.indexOf(statement.operator) === -1) - throw new Error('Invalid operator for query') + const mainQuery = deps.db + .with( + 'object_children_closure', + knex.raw( + `SELECT objects.id as parent, d.key as child, d.value as mindepth, ? as "streamId" + FROM objects + JOIN jsonb_each_text(objects.data->'__closure') d ON true + where objects.id = ?`, + [streamId, objectId] + ) + ) + .with('objs', (cteInnerQuery) => { + // always select the id + cteInnerQuery.select('id').from('object_children_closure') + cteInnerQuery.select('createdAt') + cteInnerQuery.select('speckleType') + cteInnerQuery.select('totalChildrenCount') - // Determine the correct where clause (where, and where, or where) - let whereClause: keyof typeof nestedWhereQuery - if (index === 0) whereClause = 'where' - else if (statement.verb && statement.verb.toLowerCase() === 'or') - whereClause = 'orWhere' - else whereClause = 'andWhere' - - // Note: castType is generated from the statement's value and operators are matched against a whitelist. - // If comparing with strings, the jsonb_path_query(_first) func returns json encoded strings (ie, `bar` is actually `"bar"`), hence we need to add the quotes manually to the raw provided comparison value. - nestedWhereQuery[whereClause]( - knex.raw( - `jsonb_path_query_first( data, ? )::${castType} ${statement.operator} ? `, - [ - '$.' + statement.field, - castType === 'text' ? `"${statement.value}"` : statement.value - ] - ) + // if there are any select fields, add them + if (Array.isArray(select)) { + select.forEach((field, index) => { + cteInnerQuery.select( + knex.raw('jsonb_path_query(data, :path) as :name:', { + path: '$.' + field, + name: '' + index + }) ) }) - }) - } + // otherwise, get the whole object, as stored in the jsonb column + } else { + cteInnerQuery.select('data') + } - // Order by clause; validate direction! - const direction = - orderBy.direction && orderBy.direction.toLowerCase() === 'desc' - ? 'desc' - : 'asc' - if (orderBy.field === 'id') { - cteInnerQuery.orderBy('id', direction) + // join on objects table + cteInnerQuery + .join('objects', function () { + this.on('objects.streamId', '=', 'object_children_closure.streamId').andOn( + 'objects.id', + '=', + 'object_children_closure.child' + ) + }) + .where('object_children_closure.streamId', streamId) + .andWhere('parent', objectId) + .andWhere('mindepth', '<', depth) + + // Add user provided filters/queries. + if (Array.isArray(query) && query.length > 0) { + cteInnerQuery.andWhere((nestedWhereQuery) => { + query.forEach((statement, index) => { + let castType = 'text' + if (typeof statement.value === 'string') castType = 'text' + if (typeof statement.value === 'boolean') castType = 'boolean' + if (typeof statement.value === 'number') castType = 'numeric' + + if (operatorsWhitelist.indexOf(statement.operator) === -1) + throw new Error('Invalid operator for query') + + // Determine the correct where clause (where, and where, or where) + let whereClause: keyof typeof nestedWhereQuery + if (index === 0) whereClause = 'where' + else if (statement.verb && statement.verb.toLowerCase() === 'or') + whereClause = 'orWhere' + else whereClause = 'andWhere' + + // Note: castType is generated from the statement's value and operators are matched against a whitelist. + // If comparing with strings, the jsonb_path_query(_first) func returns json encoded strings (ie, `bar` is actually `"bar"`), hence we need to add the quotes manually to the raw provided comparison value. + nestedWhereQuery[whereClause]( + knex.raw( + `jsonb_path_query_first( data, ? )::${castType} ${statement.operator} ? `, + [ + '$.' + statement.field, + castType === 'text' ? `"${statement.value}"` : statement.value + ] + ) + ) + }) + }) + } + + // Order by clause; validate direction! + const direction = + orderBy.direction && orderBy.direction.toLowerCase() === 'desc' + ? 'desc' + : 'asc' + if (orderBy.field === 'id') { + cteInnerQuery.orderBy('id', direction) + } else { + cteInnerQuery.orderByRaw( + knex.raw(`jsonb_path_query_first( data, ? ) ${direction}, id asc`, [ + '$.' + orderBy.field + ]) + ) + } + }) + .select('*') + .from('objs') + .joinRaw('RIGHT JOIN ( SELECT count(*) FROM "objs" ) c(total_count) ON TRUE') + + // Set cursor clause, if present. If it's not present, it's an entry query; this method will return a cursor based on its given query. + // We have implemented keyset pagination for more efficient searches on larger sets. This approach depends on an order by value provided by the user and a (hidden) primary key. + // logger.debug( cursor ) + if (cursor) { + let castType = 'text' + if (typeof cursor.value === 'string') castType = 'text' + if (typeof cursor.value === 'boolean') castType = 'boolean' + if (typeof cursor.value === 'number') castType = 'numeric' + + // When strings are used inside an order clause, as mentioned above, we need to add quotes around the comparison value, as the jsonb_path_query funcs return json encoded strings (`{"test":"foo"}` => test is returned as `"foo"`) + if (castType === 'text') cursor.value = `"${cursor.value}"` + + if (operatorsWhitelist.indexOf(cursor.operator) === -1) + throw new Error('Invalid operator for cursor') + + // Unwrapping the tuple comparison of ( userOrderByField, id ) > ( lastValueOfUserOrderBy, lastSeenId ) + if (fullObjectSelect) { + if (cursor.field === 'id') { + mainQuery.where(knex.raw(`id ${cursor.operator} ? `, [cursor.value])) + } else { + mainQuery.where( + knex.raw( + `jsonb_path_query_first( data, ? )::${castType} ${cursor.operator}= ? `, + ['$.' + cursor.field, cursor.value] + ) + ) + } } else { - cteInnerQuery.orderByRaw( - knex.raw(`jsonb_path_query_first( data, ? ) ${direction}, id asc`, [ - '$.' + orderBy.field + mainQuery.where( + knex.raw(`??::${castType} ${cursor.operator}= ? `, [ + (select || []).indexOf(cursor.field).toString(), + cursor.value ]) ) } - }) - .select('*') - .from('objs') - .joinRaw('RIGHT JOIN ( SELECT count(*) FROM "objs" ) c(total_count) ON TRUE') - // Set cursor clause, if present. If it's not present, it's an entry query; this method will return a cursor based on its given query. - // We have implemented keyset pagination for more efficient searches on larger sets. This approach depends on an order by value provided by the user and a (hidden) primary key. - // logger.debug( cursor ) - if (cursor) { - let castType = 'text' - if (typeof cursor.value === 'string') castType = 'text' - if (typeof cursor.value === 'boolean') castType = 'boolean' - if (typeof cursor.value === 'number') castType = 'numeric' - - // When strings are used inside an order clause, as mentioned above, we need to add quotes around the comparison value, as the jsonb_path_query funcs return json encoded strings (`{"test":"foo"}` => test is returned as `"foo"`) - if (castType === 'text') cursor.value = `"${cursor.value}"` - - if (operatorsWhitelist.indexOf(cursor.operator) === -1) - throw new Error('Invalid operator for cursor') - - // Unwrapping the tuple comparison of ( userOrderByField, id ) > ( lastValueOfUserOrderBy, lastSeenId ) - if (fullObjectSelect) { - if (cursor.field === 'id') { - mainQuery.where(knex.raw(`id ${cursor.operator} ? `, [cursor.value])) - } else { - mainQuery.where( - knex.raw( - `jsonb_path_query_first( data, ? )::${castType} ${cursor.operator}= ? `, - ['$.' + cursor.field, cursor.value] - ) - ) + if (cursor.lastSeenId) { + mainQuery.andWhere((qb) => { + // Dunno what the TS issue here is, the JS code itself seemed to work fine as JS + // eslint-disable-next-line @typescript-eslint/no-explicit-any + qb.where('id' as any, '>', cursor!.lastSeenId as any) + if (fullObjectSelect) + qb.orWhere( + knex.raw( + `jsonb_path_query_first( data, ? )::${castType} ${cursor!.operator} ? `, + ['$.' + cursor!.field, cursor!.value] + ) + ) + else + qb.orWhere( + knex.raw(`??::${castType} ${cursor!.operator} ? `, [ + (select || []).indexOf(cursor!.field).toString(), + cursor!.value + ]) + ) + }) } - } else { - mainQuery.where( - knex.raw(`??::${castType} ${cursor.operator}= ? `, [ - (select || []).indexOf(cursor.field).toString(), - cursor.value - ]) - ) } - if (cursor.lastSeenId) { - mainQuery.andWhere((qb) => { - // Dunno what the TS issue here is, the JS code itself seemed to work fine as JS - // eslint-disable-next-line @typescript-eslint/no-explicit-any - qb.where('id' as any, '>', cursor!.lastSeenId as any) - if (fullObjectSelect) - qb.orWhere( - knex.raw( - `jsonb_path_query_first( data, ? )::${castType} ${cursor!.operator} ? `, - ['$.' + cursor!.field, cursor!.value] - ) - ) - else - qb.orWhere( - knex.raw(`??::${castType} ${cursor!.operator} ? `, [ - (select || []).indexOf(cursor!.field).toString(), - cursor!.value - ]) - ) + mainQuery.limit(limit) + // logger.debug( mainQuery.toString() ) + // Finally, execute the query + const rows = await mainQuery + const totalCount = rows && rows.length > 0 ? parseInt(rows[0].total_count) : 0 + + // Return early + if (totalCount === 0) return { totalCount, objects: [], cursor: null } + + // Reconstruct the object based on the provided select paths. + if (!fullObjectSelect) { + rows.forEach((o, i, arr) => { + const no = { + id: o.id, + createdAt: o.createdAt, + speckleType: o.speckleType, + totalChildrenCount: o.totalChildrenCount, + data: {} + } + let k = 0 + for (const field of select || []) { + set(no.data, field, o[k++]) + } + arr[i] = no }) } + + // Assemble the cursor for an eventual next call + const cursorObj: typeof cursor = { + field: cursor?.field || orderBy.field, + operator: + cursor?.operator || + (orderBy.direction && orderBy.direction.toLowerCase() === 'desc' ? '<' : '>'), + value: get(rows[rows.length - 1], `data.${orderBy.field}`) + } + + // If we're not ordering by id (default case, where no order by argument is provided), we need to add the last seen id of this query in order to enable keyset pagination. + if (additionalIdOrderBy) { + cursorObj.lastSeenId = rows[rows.length - 1].id + } + + // Cursor objects should be client-side opaque, hence we encode them to base64. + const cursorEncoded = Buffer.from(JSON.stringify(cursorObj), 'binary').toString( + 'base64' + ) + return { + totalCount, + objects: rows, + cursor: rows.length === limit ? cursorEncoded : null + } } - - mainQuery.limit(limit) - // logger.debug( mainQuery.toString() ) - // Finally, execute the query - const rows = await mainQuery - const totalCount = rows && rows.length > 0 ? parseInt(rows[0].total_count) : 0 - - // Return early - if (totalCount === 0) return { totalCount, objects: [], cursor: null } - - // Reconstruct the object based on the provided select paths. - if (!fullObjectSelect) { - rows.forEach((o, i, arr) => { - const no = { - id: o.id, - createdAt: o.createdAt, - speckleType: o.speckleType, - totalChildrenCount: o.totalChildrenCount, - data: {} - } - let k = 0 - for (const field of select || []) { - set(no.data, field, o[k++]) - } - arr[i] = no - }) - } - - // Assemble the cursor for an eventual next call - const cursorObj: typeof cursor = { - field: cursor?.field || orderBy.field, - operator: - cursor?.operator || - (orderBy.direction && orderBy.direction.toLowerCase() === 'desc' ? '<' : '>'), - value: get(rows[rows.length - 1], `data.${orderBy.field}`) - } - - // If we're not ordering by id (default case, where no order by argument is provided), we need to add the last seen id of this query in order to enable keyset pagination. - if (additionalIdOrderBy) { - cursorObj.lastSeenId = rows[rows.length - 1].id - } - - // Cursor objects should be client-side opaque, hence we encode them to base64. - const cursorEncoded = Buffer.from(JSON.stringify(cursorObj), 'binary').toString( - 'base64' - ) - return { - totalCount, - objects: rows, - cursor: rows.length === limit ? cursorEncoded : null - } - } diff --git a/packages/server/modules/shared/helpers/dbHelper.ts b/packages/server/modules/shared/helpers/dbHelper.ts index 5fa9e8659..d8fac4a0b 100644 --- a/packages/server/modules/shared/helpers/dbHelper.ts +++ b/packages/server/modules/shared/helpers/dbHelper.ts @@ -22,7 +22,7 @@ export async function* executeBatchedSelect< >( selectQuery: Knex.QueryBuilder, options?: Partial -): AsyncGenerator { +): AsyncGenerator, void, unknown> { const { batchSize = 100, trx } = options || {} if (trx) selectQuery.transacting(trx) @@ -33,7 +33,7 @@ export async function* executeBatchedSelect< let currentOffset = 0 while (hasMorePages) { const q = selectQuery.clone().offset(currentOffset) - const results = (await q) as TResult + const results = (await q) as Awaited<(typeof selectQuery)> if (!results.length) { hasMorePages = false diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index 66e39b30f..eb54b5a10 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -308,3 +308,11 @@ export type GetWorkspaceCreationState = (params: { export type UpsertWorkspaceCreationState = (params: { workspaceCreationState: WorkspaceCreationState }) => Promise + +/** + * Project regions + */ + +export type CopyProjects = (params: { projectIds: string[] }) => Promise +export type CopyProjectModels = (params: { projectIds: string[] }) => Promise> +export type CopyProjectVersions = (params: { projectIds: string[] }) => Promise> diff --git a/packages/server/modules/workspaces/repositories/regions.ts b/packages/server/modules/workspaces/repositories/regions.ts index 20c267c62..c6688386d 100644 --- a/packages/server/modules/workspaces/repositories/regions.ts +++ b/packages/server/modules/workspaces/repositories/regions.ts @@ -1,7 +1,15 @@ -import { buildTableHelper } from '@/modules/core/dbSchema' +import { BranchCommits, Branches, buildTableHelper, Commits, StreamCommits, StreamFavorites, Streams, StreamsMeta } from '@/modules/core/dbSchema' +import { Branch } from '@/modules/core/domain/branches/types' +import { Commit } from '@/modules/core/domain/commits/types' +import { Stream } from '@/modules/core/domain/streams/types' +import { BranchCommitRecord, StreamCommitRecord, StreamFavoriteRecord } from '@/modules/core/helpers/types' import { RegionRecord } from '@/modules/multiregion/helpers/types' import { Regions } from '@/modules/multiregion/repositories' +import { executeBatchedSelect } from '@/modules/shared/helpers/dbHelper' import { + CopyProjectModels, + CopyProjects, + CopyProjectVersions, GetDefaultRegion, UpsertRegionAssignment } from '@/modules/workspaces/domain/operations' @@ -15,32 +23,156 @@ export const WorkspaceRegions = buildTableHelper('workspace_regions', [ const tables = { workspaceRegions: (db: Knex) => db(WorkspaceRegions.name), - regions: (db: Knex) => db(Regions.name) + regions: (db: Knex) => db(Regions.name), + projects: (db: Knex) => db(Streams.name), + models: (db: Knex) => db(Branches.name), + versions: (db: Knex) => db(Commits.name), + branchCommits: (db: Knex) => db(BranchCommits.name), + streamCommits: (db: Knex) => db(StreamCommits.name), + streamFavorites: (db: Knex) => db(StreamFavorites.name), + streamsMeta: (db: Knex) => db(StreamsMeta.name) } export const upsertRegionAssignmentFactory = (deps: { db: Knex }): UpsertRegionAssignment => - async (params) => { - const { workspaceId, regionKey } = params - const [row] = await tables - .workspaceRegions(deps.db) - .insert({ workspaceId, regionKey }, '*') - .onConflict(['workspaceId', 'regionKey']) - .merge() + async (params) => { + const { workspaceId, regionKey } = params + const [row] = await tables + .workspaceRegions(deps.db) + .insert({ workspaceId, regionKey }, '*') + .onConflict(['workspaceId', 'regionKey']) + .merge() - return row - } + return row + } export const getDefaultRegionFactory = (deps: { db: Knex }): GetDefaultRegion => - async (params) => { - const { workspaceId } = params - const row = await tables - .regions(deps.db) - .select(Regions.cols) - .join(WorkspaceRegions.name, WorkspaceRegions.col.regionKey, Regions.col.key) - .where({ [WorkspaceRegions.col.workspaceId]: workspaceId }) - .first() + async (params) => { + const { workspaceId } = params + const row = await tables + .regions(deps.db) + .select(Regions.cols) + .join(WorkspaceRegions.name, WorkspaceRegions.col.regionKey, Regions.col.key) + .where({ [WorkspaceRegions.col.workspaceId]: workspaceId }) + .first() + + return row + } + + +export const copyProjects = + (deps: { sourceDb: Knex, targetDb: Knex }): CopyProjects => + async ({ projectIds }) => { + const selectProjects = tables.projects(deps.sourceDb).select('*').whereIn(Streams.col.id, projectIds) + const copiedProjectIds: string[] = [] + + // Copy project record + for await (const projects of executeBatchedSelect(selectProjects)) { + for (const project of projects) { + // Store copied project id + copiedProjectIds.push(project.id) + + // Copy `streams` row to target db + await tables.projects(deps.targetDb) + .insert(project) + .onConflict() + .ignore() + } + + const projectIds = projects.map((project) => project.id) + + // Fetch `stream_favorites` rows for projects in batch + const selectStreamFavorites = tables.streamFavorites(deps.sourceDb).select('*').whereIn(StreamFavorites.col.streamId, projectIds) + + for await (const streamFavorites of executeBatchedSelect(selectStreamFavorites)) { + for (const streamFavorite of streamFavorites) { + // Copy `stream_favorites` row to target db + await tables.streamFavorites(deps.targetDb).insert(streamFavorite).onConflict().ignore() + } + } + + // Fetch `streams_meta` rows for projects in batch + const selectStreamsMetadata = tables.streamsMeta(deps.sourceDb).select('*').whereIn(StreamsMeta.col.streamId, projectIds) + + for await (const streamsMetadataBatch of executeBatchedSelect(selectStreamsMetadata)) { + for (const streamMetadata of streamsMetadataBatch) { + // Copy `streams_meta` row to target db + await tables.streamsMeta(deps.targetDb).insert(streamMetadata).onConflict().ignore() + } + } + } + + return copiedProjectIds + } + +export const copyProjectModels = + (deps: { sourceDb: Knex, targetDb: Knex }): CopyProjectModels => + async ({ projectIds }) => { + const copiedModelIds: Record = projectIds.reduce((result, id) => ({ ...result, [id]: [] }), {}) + + for (const projectId of projectIds) { + const selectModels = tables.models(deps.sourceDb).select('*').where({ streamId: projectId }) + + for await (const models of executeBatchedSelect(selectModels)) { + for (const model of models) { + // Store copied model ids + copiedModelIds[projectId].push(model.id) + + // Copy `branches` row to target db + await tables.models(deps.targetDb).insert(model).onConflict().ignore() + } + } + } + + return copiedModelIds + } + +export const copyProjectVersions = + (deps: { sourceDb: Knex, targetDb: Knex }): CopyProjectVersions => + async ({ projectIds }) => { + const copiedVersionIds: Record = projectIds.reduce((result, id) => ({ ...result, [id]: [] }), {}) + + for (const projectId of projectIds) { + const selectVersions = tables.streamCommits(deps.sourceDb).select('*') + .join(Commits.name, Commits.col.id, StreamCommits.col.commitId) + .where({ streamId: projectId }) + + for await (const versions of executeBatchedSelect(selectVersions)) { + for (const version of versions) { + const { commitId, ...commit } = version + + // Store copied version id + copiedVersionIds[projectId].push(commitId) + + // Copy `commits` row to target db + await tables.versions(deps.targetDb).insert(commit).onConflict().ignore() + } + + const commitIds = versions.map((version) => version.commitId) + + // Fetch `branch_commits` rows for versions in batch + const selectBranchCommits = tables.branchCommits(deps.sourceDb).select('*').whereIn(BranchCommits.col.commitId, commitIds) + + for await (const branchCommits of executeBatchedSelect(selectBranchCommits)) { + for (const branchCommit of branchCommits) { + // Copy `branch_commits` row to target db + await tables.branchCommits(deps.targetDb).insert(branchCommit).onConflict().ignore() + } + } + + // Fetch `stream_commits` rows for versions in batch + const selectStreamCommits = tables.streamCommits(deps.sourceDb).select('*').whereIn(StreamCommits.col.commitId, commitIds) + + for await (const streamCommits of executeBatchedSelect(selectStreamCommits)) { + for (const streamCommit of streamCommits) { + // Copy `stream_commits` row to target db + await tables.streamCommits(deps.targetDb).insert(streamCommit).onConflict().ignore() + } + } + } + } + + return copiedVersionIds + } - return row - } diff --git a/packages/server/modules/workspaces/services/projects.ts b/packages/server/modules/workspaces/services/projects.ts index 886a28c99..f8d81f058 100644 --- a/packages/server/modules/workspaces/services/projects.ts +++ b/packages/server/modules/workspaces/services/projects.ts @@ -1,5 +1,8 @@ import { StreamRecord } from '@/modules/core/helpers/types' import { + CopyProjectModels, + CopyProjects, + CopyProjectVersions, GetDefaultRegion, GetWorkspace, GetWorkspaceRoleForUser, @@ -97,23 +100,23 @@ type GetWorkspaceProjectsReturnValue = { export const getWorkspaceProjectsFactory = ({ getStreams }: { getStreams: GetUserStreamsPage }) => - async ( - args: GetWorkspaceProjectsArgs, - opts: GetWorkspaceProjectsOptions - ): Promise => { - const { streams, cursor } = await getStreams({ - cursor: opts.cursor, - limit: opts.limit || 25, - searchQuery: opts.filter?.search || undefined, - workspaceId: args.workspaceId, - userId: opts.filter.userId - }) + async ( + args: GetWorkspaceProjectsArgs, + opts: GetWorkspaceProjectsOptions + ): Promise => { + const { streams, cursor } = await getStreams({ + cursor: opts.cursor, + limit: opts.limit || 25, + searchQuery: opts.filter?.search || undefined, + workspaceId: args.workspaceId, + userId: opts.filter.userId + }) - return { - items: streams, - cursor + return { + items: streams, + cursor + } } - } type MoveProjectToWorkspaceArgs = { projectId: string @@ -138,66 +141,78 @@ export const moveProjectToWorkspaceFactory = getWorkspaceRoleToDefaultProjectRoleMapping: GetWorkspaceRoleToDefaultProjectRoleMapping updateWorkspaceRole: UpdateWorkspaceRole }) => - async ({ - projectId, - workspaceId - }: MoveProjectToWorkspaceArgs): Promise => { - const project = await getProject({ projectId }) + async ({ + projectId, + workspaceId + }: MoveProjectToWorkspaceArgs): Promise => { + const project = await getProject({ projectId }) - if (!project) throw new ProjectNotFoundError() - if (project.workspaceId?.length) { - // We do not currently support moving projects between workspaces - throw new WorkspaceInvalidProjectError( - 'Specified project already belongs to a workspace. Moving between workspaces is not yet supported.' - ) - } - - // Update roles for current project members - const projectTeam = await getProjectCollaborators({ projectId }) - const workspaceTeam = await getWorkspaceRoles({ workspaceId }) - const defaultProjectRoleMapping = await getWorkspaceRoleToDefaultProjectRoleMapping( - { workspaceId } - ) - - for (const projectMembers of chunk(projectTeam, 5)) { - await Promise.all( - projectMembers.map( - async ({ id: userId, role: serverRole, streamRole: currentProjectRole }) => { - // Update workspace role. Prefer existing workspace role if there is one. - const currentWorkspaceRole = workspaceTeam.find( - (role) => role.userId === userId - ) - const nextWorkspaceRole = currentWorkspaceRole ?? { - userId, - workspaceId, - role: - serverRole === Roles.Server.Guest - ? Roles.Workspace.Guest - : Roles.Workspace.Member, - createdAt: new Date() - } - await updateWorkspaceRole(nextWorkspaceRole) - - // Update project role. Prefer default workspace project role if more permissive. - const defaultProjectRole = - defaultProjectRoleMapping[nextWorkspaceRole.role] ?? Roles.Stream.Reviewer - const nextProjectRole = orderByWeight( - [currentProjectRole, defaultProjectRole], - coreUserRoles - )[0] - await upsertProjectRole({ - userId, - projectId, - role: nextProjectRole.name as StreamRoles - }) - } + if (!project) throw new ProjectNotFoundError() + if (project.workspaceId?.length) { + // We do not currently support moving projects between workspaces + throw new WorkspaceInvalidProjectError( + 'Specified project already belongs to a workspace. Moving between workspaces is not yet supported.' ) + } + + // Update roles for current project members + const projectTeam = await getProjectCollaborators({ projectId }) + const workspaceTeam = await getWorkspaceRoles({ workspaceId }) + const defaultProjectRoleMapping = await getWorkspaceRoleToDefaultProjectRoleMapping( + { workspaceId } ) + + for (const projectMembers of chunk(projectTeam, 5)) { + await Promise.all( + projectMembers.map( + async ({ id: userId, role: serverRole, streamRole: currentProjectRole }) => { + // Update workspace role. Prefer existing workspace role if there is one. + const currentWorkspaceRole = workspaceTeam.find( + (role) => role.userId === userId + ) + const nextWorkspaceRole = currentWorkspaceRole ?? { + userId, + workspaceId, + role: + serverRole === Roles.Server.Guest + ? Roles.Workspace.Guest + : Roles.Workspace.Member, + createdAt: new Date() + } + await updateWorkspaceRole(nextWorkspaceRole) + + // Update project role. Prefer default workspace project role if more permissive. + const defaultProjectRole = + defaultProjectRoleMapping[nextWorkspaceRole.role] ?? Roles.Stream.Reviewer + const nextProjectRole = orderByWeight( + [currentProjectRole, defaultProjectRole], + coreUserRoles + )[0] + await upsertProjectRole({ + userId, + projectId, + role: nextProjectRole.name as StreamRoles + }) + } + ) + ) + } + + // Assign project to workspace + return await updateProject({ projectUpdate: { id: projectId, workspaceId } }) } - // Assign project to workspace - return await updateProject({ projectUpdate: { id: projectId, workspaceId } }) - } +export const moveProjectToRegionFactory = + (deps: { + copyProjects: CopyProjects, + copyProjectModels: CopyProjectModels, + copyProjectVersions: CopyProjectVersions + }) => + async (args: { projectId: string }): Promise => { + const projectIds = await deps.copyProjects({ projectIds: [args.projectId] }) + const modelIdsByProjectId = await deps.copyProjectModels({ projectIds }) + const versionIdsByProjectId = await deps.copyProjectVersions({ projectIds }) + } export const getWorkspaceRoleToDefaultProjectRoleMappingFactory = ({ @@ -205,19 +220,19 @@ export const getWorkspaceRoleToDefaultProjectRoleMappingFactory = }: { getWorkspace: GetWorkspace }): GetWorkspaceRoleToDefaultProjectRoleMapping => - async ({ workspaceId }) => { - const workspace = await getWorkspace({ workspaceId }) + async ({ workspaceId }) => { + const workspace = await getWorkspace({ workspaceId }) - if (!workspace) { - throw new WorkspaceNotFoundError() - } + if (!workspace) { + throw new WorkspaceNotFoundError() + } - return { - [Roles.Workspace.Guest]: null, - [Roles.Workspace.Member]: workspace.defaultProjectRole, - [Roles.Workspace.Admin]: Roles.Stream.Owner + return { + [Roles.Workspace.Guest]: null, + [Roles.Workspace.Member]: workspace.defaultProjectRole, + [Roles.Workspace.Admin]: Roles.Stream.Owner + } } - } export const updateWorkspaceProjectRoleFactory = ({ @@ -229,63 +244,63 @@ export const updateWorkspaceProjectRoleFactory = getWorkspaceRoleForUser: GetWorkspaceRoleForUser updateStreamRoleAndNotify: UpdateStreamRole }): UpdateWorkspaceProjectRole => - async ({ role, updater }) => { - const { workspaceId } = (await getStream({ streamId: role.projectId })) ?? {} - if (!workspaceId) throw new WorkspaceInvalidProjectError() + async ({ role, updater }) => { + const { workspaceId } = (await getStream({ streamId: role.projectId })) ?? {} + if (!workspaceId) throw new WorkspaceInvalidProjectError() - const currentWorkspaceRole = await getWorkspaceRoleForUser({ - workspaceId, - userId: role.userId - }) + const currentWorkspaceRole = await getWorkspaceRoleForUser({ + workspaceId, + userId: role.userId + }) - if (currentWorkspaceRole?.role === Roles.Workspace.Admin) { - // User is workspace admin and cannot have their project roles changed - throw new WorkspaceAdminError() + if (currentWorkspaceRole?.role === Roles.Workspace.Admin) { + // User is workspace admin and cannot have their project roles changed + throw new WorkspaceAdminError() + } + + if ( + currentWorkspaceRole?.role === Roles.Workspace.Guest && + role.role === Roles.Stream.Owner + ) { + // Workspace guests cannot be project owners + throw new WorkspaceInvalidRoleError('Workspace guests cannot be project owners.') + } + + return await updateStreamRoleAndNotify( + role, + updater.userId!, + updater.resourceAccessRules + ) } - if ( - currentWorkspaceRole?.role === Roles.Workspace.Guest && - role.role === Roles.Stream.Owner - ) { - // Workspace guests cannot be project owners - throw new WorkspaceInvalidRoleError('Workspace guests cannot be project owners.') - } - - return await updateStreamRoleAndNotify( - role, - updater.userId!, - updater.resourceAccessRules - ) - } - export const createWorkspaceProjectFactory = (deps: { getDefaultRegion: GetDefaultRegion }) => - async (params: { input: WorkspaceProjectCreateInput; ownerId: string }) => { - const { input, ownerId } = params - const workspaceDefaultRegion = await deps.getDefaultRegion({ - workspaceId: input.workspaceId - }) - const regionKey = workspaceDefaultRegion?.key - const projectDb = await getDb({ regionKey }) - const db = mainDb + async (params: { input: WorkspaceProjectCreateInput; ownerId: string }) => { + const { input, ownerId } = params + const workspaceDefaultRegion = await deps.getDefaultRegion({ + workspaceId: input.workspaceId + }) + const regionKey = workspaceDefaultRegion?.key + const projectDb = await getDb({ regionKey }) + const db = mainDb - // todo, use the command factory here, but for that, we need to migrate to the event bus - // deps not injected to ensure proper DB injection - const createNewProject = createNewProjectFactory({ - storeProject: storeProjectFactory({ db: projectDb }), - getProject: getProjectFactory({ db }), - deleteProject: deleteProjectFactory({ db: projectDb }), - storeModel: storeModelFactory({ db: projectDb }), - // THIS MUST GO TO THE MAIN DB - storeProjectRole: storeProjectRoleFactory({ db }), - projectsEventsEmitter: ProjectsEmitter.emit - }) + // todo, use the command factory here, but for that, we need to migrate to the event bus + // deps not injected to ensure proper DB injection + const createNewProject = createNewProjectFactory({ + storeProject: storeProjectFactory({ db: projectDb }), + getProject: getProjectFactory({ db }), + deleteProject: deleteProjectFactory({ db: projectDb }), + storeModel: storeModelFactory({ db: projectDb }), + // THIS MUST GO TO THE MAIN DB + storeProjectRole: storeProjectRoleFactory({ db }), + projectsEventsEmitter: ProjectsEmitter.emit + }) - const project = await createNewProject({ - ...input, - regionKey, - ownerId - }) + const project = await createNewProject({ + ...input, + regionKey, + ownerId + }) - return project - } + return project + } From e5d04e5a3c090b76f746d903b0960e5867914613 Mon Sep 17 00:00:00 2001 From: Chuck Driesler Date: Mon, 13 Jan 2025 16:19:33 +0000 Subject: [PATCH 02/56] chore(regions): wire up move to resolver --- .../workspacesCore/typedefs/regions.graphql | 8 + .../typedefs/workspaces.graphql | 1 - .../modules/core/graph/generated/graphql.ts | 8 + .../modules/core/repositories/objects.ts | 822 +++++++++--------- .../graph/generated/graphql.ts | 7 + .../server/modules/shared/helpers/dbHelper.ts | 4 +- .../modules/workspaces/domain/operations.ts | 18 +- .../modules/workspaces/errors/regions.ts | 6 + .../workspaces/graph/resolvers/regions.ts | 45 +- .../workspaces/repositories/regions.ts | 294 ++++--- .../modules/workspaces/services/projects.ts | 282 +++--- .../modules/workspaces/services/regions.ts | 88 +- .../workspaces/tests/helpers/creation.ts | 4 +- .../server/test/graphql/generated/graphql.ts | 7 + 14 files changed, 907 insertions(+), 687 deletions(-) diff --git a/packages/server/assets/workspacesCore/typedefs/regions.graphql b/packages/server/assets/workspacesCore/typedefs/regions.graphql index b394c8655..e61b93102 100644 --- a/packages/server/assets/workspacesCore/typedefs/regions.graphql +++ b/packages/server/assets/workspacesCore/typedefs/regions.graphql @@ -12,3 +12,11 @@ extend type WorkspaceMutations { """ setDefaultRegion(workspaceId: String!, regionKey: String!): Workspace! } + +extend type WorkspaceProjectMutations { + """ + Update project region and move all regional data to new db. + TODO: Currently performs all operations synchronously in request, should probably be scheduled. + """ + moveToRegion(projectId: String!, regionKey: String!): Project! +} diff --git a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql index 8c5d69997..226b28a18 100644 --- a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql +++ b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql @@ -170,7 +170,6 @@ type WorkspaceProjectMutations { updateRole(input: ProjectUpdateRoleInput!): Project! @hasStreamRole(role: STREAM_OWNER) @hasWorkspaceRole(role: MEMBER) - moveToRegion(projectId: String!, regionKey: String!): Project! moveToWorkspace(projectId: String!, workspaceId: String!): Project! create(input: WorkspaceProjectCreateInput!): Project! } diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index c6b5b2735..9f5762982 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -4514,6 +4514,7 @@ export type WorkspaceProjectInviteCreateInput = { export type WorkspaceProjectMutations = { __typename?: 'WorkspaceProjectMutations'; create: Project; + moveToRegion: Project; moveToWorkspace: Project; updateRole: Project; }; @@ -4524,6 +4525,12 @@ export type WorkspaceProjectMutationsCreateArgs = { }; +export type WorkspaceProjectMutationsMoveToRegionArgs = { + projectId: Scalars['String']['input']; + regionKey: Scalars['String']['input']; +}; + + export type WorkspaceProjectMutationsMoveToWorkspaceArgs = { projectId: Scalars['String']['input']; workspaceId: Scalars['String']['input']; @@ -6799,6 +6806,7 @@ export type WorkspacePlanResolvers = { create?: Resolver>; + moveToRegion?: Resolver>; moveToWorkspace?: Resolver>; updateRole?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; diff --git a/packages/server/modules/core/repositories/objects.ts b/packages/server/modules/core/repositories/objects.ts index bdf8a82ef..851bf78fa 100644 --- a/packages/server/modules/core/repositories/objects.ts +++ b/packages/server/modules/core/repositories/objects.ts @@ -40,502 +40,502 @@ const tables = { export const getStreamObjectsFactory = (deps: { db: Knex }): GetStreamObjects => - async (streamId: string, objectIds: string[]): Promise => { - if (!objectIds?.length) return [] + async (streamId: string, objectIds: string[]): Promise => { + if (!objectIds?.length) return [] - const q = tables - .objects(deps.db) - .where(Objects.col.streamId, streamId) - .whereIn(Objects.col.id, objectIds) + const q = tables + .objects(deps.db) + .where(Objects.col.streamId, streamId) + .whereIn(Objects.col.id, objectIds) - return await q - } + return await q + } export const getObjectFactory = (deps: { db: Knex }): GetObject => - async (objectId: string, streamId: string): Promise> => { - return await tables - .objects(deps.db) - .where(Objects.col.id, objectId) - .andWhere(Objects.col.streamId, streamId) - .first() - } + async (objectId: string, streamId: string): Promise> => { + return await tables + .objects(deps.db) + .where(Objects.col.id, objectId) + .andWhere(Objects.col.streamId, streamId) + .first() + } export const getFormattedObjectFactory = (deps: { db: Knex }): GetFormattedObject => - async ({ streamId, objectId }) => { - const res = await tables - .objects(deps.db) - .where({ streamId, id: objectId }) - .select('*') - .first() - if (!res) return null + async ({ streamId, objectId }) => { + const res = await tables + .objects(deps.db) + .where({ streamId, id: objectId }) + .select('*') + .first() + if (!res) return null - // TODO: Why tho? A lot if not most of places already just use getObjectFactory, - const finalRes: SetOptional = res - if (finalRes.data) finalRes.data.totalChildrenCount = res.totalChildrenCount // move this back - delete finalRes.streamId // backwards compatibility + // TODO: Why tho? A lot if not most of places already just use getObjectFactory, + const finalRes: SetOptional = res + if (finalRes.data) finalRes.data.totalChildrenCount = res.totalChildrenCount // move this back + delete finalRes.streamId // backwards compatibility - return finalRes - } + return finalRes + } export const getBatchedStreamObjectsFactory = (deps: { db: Knex }): GetBatchedStreamObjects => - (streamId: string, options?: Partial) => { - const baseQuery = tables - .objects(deps.db) - .select('*') - .where(Objects.col.streamId, streamId) - .orderBy(Objects.col.id) + (streamId: string, options?: Partial) => { + const baseQuery = tables + .objects(deps.db) + .select('*') + .where(Objects.col.streamId, streamId) + .orderBy(Objects.col.id) - return executeBatchedSelect(baseQuery, options) - } + return executeBatchedSelect(baseQuery, options) + } export const insertObjectsFactory = (deps: { db: Knex }): StoreObjects => - async (objects: ObjectRecord[], options?: Partial<{ trx: Knex.Transaction }>) => { - const q = tables.objects(deps.db).insert(objects) - if (options?.trx) q.transacting(options.trx) - return await q - } + async (objects: ObjectRecord[], options?: Partial<{ trx: Knex.Transaction }>) => { + const q = tables.objects(deps.db).insert(objects) + if (options?.trx) q.transacting(options.trx) + return await q + } export const storeSingleObjectIfNotFoundFactory = (deps: { db: Knex }): StoreSingleObjectIfNotFound => - async (insertionObject) => { - await tables - .objects(deps.db) - .insert( - // knex is bothered by string being inserted into jsonb, which is actually fine - insertionObject as SpeckleObject - ) - .onConflict() - .ignore() - } + async (insertionObject) => { + await tables + .objects(deps.db) + .insert( + // knex is bothered by string being inserted into jsonb, which is actually fine + insertionObject as SpeckleObject + ) + .onConflict() + .ignore() + } export const storeObjectsIfNotFoundFactory = (deps: { db: Knex }): StoreObjectsIfNotFound => - async (batch) => { - await tables - .objects(deps.db) - .insert( - // knex is bothered by string being inserted into jsonb, which is actually fine - batch as SpeckleObject[] - ) - .onConflict() - .ignore() - } + async (batch) => { + await tables + .objects(deps.db) + .insert( + // knex is bothered by string being inserted into jsonb, which is actually fine + batch as SpeckleObject[] + ) + .onConflict() + .ignore() + } export const storeClosuresIfNotFoundFactory = (deps: { db: Knex }): StoreClosuresIfNotFound => - async (closuresBatch) => { - await tables - .objectChildrenClosure(deps.db) - .insert(closuresBatch) - .onConflict() - .ignore() - } + async (closuresBatch) => { + await tables + .objectChildrenClosure(deps.db) + .insert(closuresBatch) + .onConflict() + .ignore() + } export const getObjectChildrenStreamFactory = (deps: { db: Knex }): GetObjectChildrenStream => - async ({ streamId, objectId }) => { - const q = deps.db.with( - 'object_children_closure', - knex.raw( - `SELECT objects.id as parent, d.key as child, d.value as mindepth, ? as "streamId" + async ({ streamId, objectId }) => { + const q = deps.db.with( + 'object_children_closure', + knex.raw( + `SELECT objects.id as parent, d.key as child, d.value as mindepth, ? as "streamId" FROM objects JOIN jsonb_each_text(objects.data->'__closure') d ON true where objects.id = ?`, - [streamId, objectId] - ) + [streamId, objectId] ) - q.select('id') - q.select(knex.raw('data::text as "dataText"')) - q.from('object_children_closure') + ) + q.select('id') + q.select(knex.raw('data::text as "dataText"')) + q.from('object_children_closure') - q.rightJoin('objects', function () { - this.on('objects.streamId', '=', 'object_children_closure.streamId').andOn( - 'objects.id', - '=', - 'object_children_closure.child' - ) - }) - .where( - knex.raw('object_children_closure."streamId" = ? AND parent = ?', [ - streamId, - objectId - ]) - ) - .orderBy('objects.id') - return q.stream({ highWaterMark: 500 }) - } + q.rightJoin('objects', function () { + this.on('objects.streamId', '=', 'object_children_closure.streamId').andOn( + 'objects.id', + '=', + 'object_children_closure.child' + ) + }) + .where( + knex.raw('object_children_closure."streamId" = ? AND parent = ?', [ + streamId, + objectId + ]) + ) + .orderBy('objects.id') + return q.stream({ highWaterMark: 500 }) + } export const getObjectsStreamFactory = (deps: { db: Knex }): GetObjectsStream => - async ({ streamId, objectIds }) => { - const res = tables - .objects(deps.db) - .whereIn('id', objectIds) - .andWhere('streamId', streamId) - .orderBy('id') - .select( - knex.raw( - '"id", "speckleType", "totalChildrenCount", "totalChildrenCountByDepth", "createdAt", data::text as "dataText"' - ) + async ({ streamId, objectIds }) => { + const res = tables + .objects(deps.db) + .whereIn('id', objectIds) + .andWhere('streamId', streamId) + .orderBy('id') + .select( + knex.raw( + '"id", "speckleType", "totalChildrenCount", "totalChildrenCountByDepth", "createdAt", data::text as "dataText"' ) - return res.stream({ highWaterMark: 500 }) - } + ) + return res.stream({ highWaterMark: 500 }) + } export const hasObjectsFactory = (deps: { db: Knex }): HasObjects => - async ({ streamId, objectIds }) => { - const dbRes = await tables - .objects(deps.db) - .whereIn('id', objectIds) - .andWhere('streamId', streamId) - .select('id') + async ({ streamId, objectIds }) => { + const dbRes = await tables + .objects(deps.db) + .whereIn('id', objectIds) + .andWhere('streamId', streamId) + .select('id') - const res: Record = {} - // eslint-disable-next-line @typescript-eslint/no-for-in-array - for (const i in objectIds) { - res[objectIds[i]] = false - } - // eslint-disable-next-line @typescript-eslint/no-for-in-array - for (const i in dbRes) { - res[dbRes[i].id] = true - } - return res + const res: Record = {} + // eslint-disable-next-line @typescript-eslint/no-for-in-array + for (const i in objectIds) { + res[objectIds[i]] = false } + // eslint-disable-next-line @typescript-eslint/no-for-in-array + for (const i in dbRes) { + res[dbRes[i].id] = true + } + return res + } export const getObjectChildrenFactory = (deps: { db: Knex }): GetObjectChildren => - async ({ streamId, objectId, limit, depth, select, cursor }) => { - limit = toNumber(limit || 0) || 50 - depth = toNumber(depth || 0) || 1000 + async ({ streamId, objectId, limit, depth, select, cursor }) => { + limit = toNumber(limit || 0) || 50 + depth = toNumber(depth || 0) || 1000 - let fullObjectSelect = false + let fullObjectSelect = false - const q = deps.db.with( - 'object_children_closure', - knex.raw( - `SELECT objects.id as parent, d.key as child, d.value as mindepth, ? as "streamId" + const q = deps.db.with( + 'object_children_closure', + knex.raw( + `SELECT objects.id as parent, d.key as child, d.value as mindepth, ? as "streamId" FROM objects JOIN jsonb_each_text(objects.data->'__closure') d ON true where objects.id = ?`, - [streamId, objectId] - ) + [streamId, objectId] ) + ) - if (Array.isArray(select)) { - select.forEach((field, index) => { - q.select( - knex.raw('jsonb_path_query(data, :path) as :name:', { - path: '$.' + field, - name: '' + index - }) - ) - }) - } else { - fullObjectSelect = true - q.select('data') - } - - q.select('id') - q.select('createdAt') - q.select('speckleType') - q.select('totalChildrenCount') - - q.from('object_children_closure') - - q.rightJoin('objects', function () { - this.on('objects.streamId', '=', 'object_children_closure.streamId').andOn( - 'objects.id', - '=', - 'object_children_closure.child' + if (Array.isArray(select)) { + select.forEach((field, index) => { + q.select( + knex.raw('jsonb_path_query(data, :path) as :name:', { + path: '$.' + field, + name: '' + index + }) ) }) - .where( - knex.raw('object_children_closure."streamId" = ? AND parent = ?', [ - streamId, - objectId - ]) - ) - .andWhere(knex.raw('object_children_closure.mindepth < ?', [depth])) - .andWhere(knex.raw('id > ?', [cursor ? cursor : '0'])) - .orderBy('objects.id') - .limit(limit) - - const rows = await q - - if (rows.length === 0) { - return { objects: rows, cursor: null } - } - - if (!fullObjectSelect) - rows.forEach((o, i, arr) => { - const no = { - id: o.id, - createdAt: o.createdAt, - speckleType: o.speckleType, - totalChildrenCount: o.totalChildrenCount, - data: {} - } - let k = 0 - for (const field of select || []) { - set(no.data, field, o[k++]) - } - arr[i] = no - }) - - const lastId = rows[rows.length - 1].id - return { objects: rows, cursor: lastId } + } else { + fullObjectSelect = true + q.select('data') } + q.select('id') + q.select('createdAt') + q.select('speckleType') + q.select('totalChildrenCount') + + q.from('object_children_closure') + + q.rightJoin('objects', function () { + this.on('objects.streamId', '=', 'object_children_closure.streamId').andOn( + 'objects.id', + '=', + 'object_children_closure.child' + ) + }) + .where( + knex.raw('object_children_closure."streamId" = ? AND parent = ?', [ + streamId, + objectId + ]) + ) + .andWhere(knex.raw('object_children_closure.mindepth < ?', [depth])) + .andWhere(knex.raw('id > ?', [cursor ? cursor : '0'])) + .orderBy('objects.id') + .limit(limit) + + const rows = await q + + if (rows.length === 0) { + return { objects: rows, cursor: null } + } + + if (!fullObjectSelect) + rows.forEach((o, i, arr) => { + const no = { + id: o.id, + createdAt: o.createdAt, + speckleType: o.speckleType, + totalChildrenCount: o.totalChildrenCount, + data: {} + } + let k = 0 + for (const field of select || []) { + set(no.data, field, o[k++]) + } + arr[i] = no + }) + + const lastId = rows[rows.length - 1].id + return { objects: rows, cursor: lastId } + } + /** * This query is inefficient on larger sets (n * 10k objects) as we need to return the total count on an arbitrarily (user) defined selection of objects. * A possible future optimisation route would be to cache the total count of a query (as objects are immutable, it will not change) on a first run, and, if found on a subsequent round, do a simpler query and merge the total count result. */ export const getObjectChildrenQueryFactory = (deps: { db: Knex }): GetObjectChildrenQuery => - async (params) => { - const { streamId, objectId, select, query } = params + async (params) => { + const { streamId, objectId, select, query } = params - const limit = toNumber(params.limit || 0) || 50 - const depth = toNumber(params.depth || 0) || 1000 - const orderBy = params.orderBy || { field: 'id', direction: 'asc' } + const limit = toNumber(params.limit || 0) || 50 + const depth = toNumber(params.depth || 0) || 1000 + const orderBy = params.orderBy || { field: 'id', direction: 'asc' } - // Cursors received by this service should be base64 encoded. They are generated on first entry query by this service; They should never be client-side generated. - const cursor: Optional<{ - value: unknown - operator: string - field: string - lastSeenId?: string - }> = params.cursor - ? JSON.parse(Buffer.from(params.cursor, 'base64').toString('binary')) - : undefined + // Cursors received by this service should be base64 encoded. They are generated on first entry query by this service; They should never be client-side generated. + const cursor: Optional<{ + value: unknown + operator: string + field: string + lastSeenId?: string + }> = params.cursor + ? JSON.parse(Buffer.from(params.cursor, 'base64').toString('binary')) + : undefined - // Flag that keeps track of whether we select the whole "data" part of an object or not - let fullObjectSelect = false - if (Array.isArray(select)) { - // if we order by a field that we do not select, select it! - if (orderBy && select.indexOf(orderBy.field) === -1) { - select.push(orderBy.field) - } - // // always add the id! - // if ( select.indexOf( 'id' ) === -1 ) select.unshift( 'id' ) - } else { - fullObjectSelect = true + // Flag that keeps track of whether we select the whole "data" part of an object or not + let fullObjectSelect = false + if (Array.isArray(select)) { + // if we order by a field that we do not select, select it! + if (orderBy && select.indexOf(orderBy.field) === -1) { + select.push(orderBy.field) } + // // always add the id! + // if ( select.indexOf( 'id' ) === -1 ) select.unshift( 'id' ) + } else { + fullObjectSelect = true + } - const additionalIdOrderBy = orderBy.field !== 'id' + const additionalIdOrderBy = orderBy.field !== 'id' - const operatorsWhitelist = ['=', '>', '>=', '<', '<=', '!='] + const operatorsWhitelist = ['=', '>', '>=', '<', '<=', '!='] - const mainQuery = deps.db - .with( - 'object_children_closure', - knex.raw( - `SELECT objects.id as parent, d.key as child, d.value as mindepth, ? as "streamId" + const mainQuery = deps.db + .with( + 'object_children_closure', + knex.raw( + `SELECT objects.id as parent, d.key as child, d.value as mindepth, ? as "streamId" FROM objects JOIN jsonb_each_text(objects.data->'__closure') d ON true where objects.id = ?`, - [streamId, objectId] - ) + [streamId, objectId] ) - .with('objs', (cteInnerQuery) => { - // always select the id - cteInnerQuery.select('id').from('object_children_closure') - cteInnerQuery.select('createdAt') - cteInnerQuery.select('speckleType') - cteInnerQuery.select('totalChildrenCount') + ) + .with('objs', (cteInnerQuery) => { + // always select the id + cteInnerQuery.select('id').from('object_children_closure') + cteInnerQuery.select('createdAt') + cteInnerQuery.select('speckleType') + cteInnerQuery.select('totalChildrenCount') - // if there are any select fields, add them - if (Array.isArray(select)) { - select.forEach((field, index) => { - cteInnerQuery.select( - knex.raw('jsonb_path_query(data, :path) as :name:', { - path: '$.' + field, - name: '' + index - }) - ) - }) - // otherwise, get the whole object, as stored in the jsonb column - } else { - cteInnerQuery.select('data') - } - - // join on objects table - cteInnerQuery - .join('objects', function () { - this.on('objects.streamId', '=', 'object_children_closure.streamId').andOn( - 'objects.id', - '=', - 'object_children_closure.child' - ) - }) - .where('object_children_closure.streamId', streamId) - .andWhere('parent', objectId) - .andWhere('mindepth', '<', depth) - - // Add user provided filters/queries. - if (Array.isArray(query) && query.length > 0) { - cteInnerQuery.andWhere((nestedWhereQuery) => { - query.forEach((statement, index) => { - let castType = 'text' - if (typeof statement.value === 'string') castType = 'text' - if (typeof statement.value === 'boolean') castType = 'boolean' - if (typeof statement.value === 'number') castType = 'numeric' - - if (operatorsWhitelist.indexOf(statement.operator) === -1) - throw new Error('Invalid operator for query') - - // Determine the correct where clause (where, and where, or where) - let whereClause: keyof typeof nestedWhereQuery - if (index === 0) whereClause = 'where' - else if (statement.verb && statement.verb.toLowerCase() === 'or') - whereClause = 'orWhere' - else whereClause = 'andWhere' - - // Note: castType is generated from the statement's value and operators are matched against a whitelist. - // If comparing with strings, the jsonb_path_query(_first) func returns json encoded strings (ie, `bar` is actually `"bar"`), hence we need to add the quotes manually to the raw provided comparison value. - nestedWhereQuery[whereClause]( - knex.raw( - `jsonb_path_query_first( data, ? )::${castType} ${statement.operator} ? `, - [ - '$.' + statement.field, - castType === 'text' ? `"${statement.value}"` : statement.value - ] - ) - ) + // if there are any select fields, add them + if (Array.isArray(select)) { + select.forEach((field, index) => { + cteInnerQuery.select( + knex.raw('jsonb_path_query(data, :path) as :name:', { + path: '$.' + field, + name: '' + index }) - }) - } - - // Order by clause; validate direction! - const direction = - orderBy.direction && orderBy.direction.toLowerCase() === 'desc' - ? 'desc' - : 'asc' - if (orderBy.field === 'id') { - cteInnerQuery.orderBy('id', direction) - } else { - cteInnerQuery.orderByRaw( - knex.raw(`jsonb_path_query_first( data, ? ) ${direction}, id asc`, [ - '$.' + orderBy.field - ]) ) - } - }) - .select('*') - .from('objs') - .joinRaw('RIGHT JOIN ( SELECT count(*) FROM "objs" ) c(total_count) ON TRUE') - - // Set cursor clause, if present. If it's not present, it's an entry query; this method will return a cursor based on its given query. - // We have implemented keyset pagination for more efficient searches on larger sets. This approach depends on an order by value provided by the user and a (hidden) primary key. - // logger.debug( cursor ) - if (cursor) { - let castType = 'text' - if (typeof cursor.value === 'string') castType = 'text' - if (typeof cursor.value === 'boolean') castType = 'boolean' - if (typeof cursor.value === 'number') castType = 'numeric' - - // When strings are used inside an order clause, as mentioned above, we need to add quotes around the comparison value, as the jsonb_path_query funcs return json encoded strings (`{"test":"foo"}` => test is returned as `"foo"`) - if (castType === 'text') cursor.value = `"${cursor.value}"` - - if (operatorsWhitelist.indexOf(cursor.operator) === -1) - throw new Error('Invalid operator for cursor') - - // Unwrapping the tuple comparison of ( userOrderByField, id ) > ( lastValueOfUserOrderBy, lastSeenId ) - if (fullObjectSelect) { - if (cursor.field === 'id') { - mainQuery.where(knex.raw(`id ${cursor.operator} ? `, [cursor.value])) - } else { - mainQuery.where( - knex.raw( - `jsonb_path_query_first( data, ? )::${castType} ${cursor.operator}= ? `, - ['$.' + cursor.field, cursor.value] - ) - ) - } + }) + // otherwise, get the whole object, as stored in the jsonb column } else { - mainQuery.where( - knex.raw(`??::${castType} ${cursor.operator}= ? `, [ - (select || []).indexOf(cursor.field).toString(), - cursor.value + cteInnerQuery.select('data') + } + + // join on objects table + cteInnerQuery + .join('objects', function () { + this.on('objects.streamId', '=', 'object_children_closure.streamId').andOn( + 'objects.id', + '=', + 'object_children_closure.child' + ) + }) + .where('object_children_closure.streamId', streamId) + .andWhere('parent', objectId) + .andWhere('mindepth', '<', depth) + + // Add user provided filters/queries. + if (Array.isArray(query) && query.length > 0) { + cteInnerQuery.andWhere((nestedWhereQuery) => { + query.forEach((statement, index) => { + let castType = 'text' + if (typeof statement.value === 'string') castType = 'text' + if (typeof statement.value === 'boolean') castType = 'boolean' + if (typeof statement.value === 'number') castType = 'numeric' + + if (operatorsWhitelist.indexOf(statement.operator) === -1) + throw new Error('Invalid operator for query') + + // Determine the correct where clause (where, and where, or where) + let whereClause: keyof typeof nestedWhereQuery + if (index === 0) whereClause = 'where' + else if (statement.verb && statement.verb.toLowerCase() === 'or') + whereClause = 'orWhere' + else whereClause = 'andWhere' + + // Note: castType is generated from the statement's value and operators are matched against a whitelist. + // If comparing with strings, the jsonb_path_query(_first) func returns json encoded strings (ie, `bar` is actually `"bar"`), hence we need to add the quotes manually to the raw provided comparison value. + nestedWhereQuery[whereClause]( + knex.raw( + `jsonb_path_query_first( data, ? )::${castType} ${statement.operator} ? `, + [ + '$.' + statement.field, + castType === 'text' ? `"${statement.value}"` : statement.value + ] + ) + ) + }) + }) + } + + // Order by clause; validate direction! + const direction = + orderBy.direction && orderBy.direction.toLowerCase() === 'desc' + ? 'desc' + : 'asc' + if (orderBy.field === 'id') { + cteInnerQuery.orderBy('id', direction) + } else { + cteInnerQuery.orderByRaw( + knex.raw(`jsonb_path_query_first( data, ? ) ${direction}, id asc`, [ + '$.' + orderBy.field ]) ) } + }) + .select('*') + .from('objs') + .joinRaw('RIGHT JOIN ( SELECT count(*) FROM "objs" ) c(total_count) ON TRUE') - if (cursor.lastSeenId) { - mainQuery.andWhere((qb) => { - // Dunno what the TS issue here is, the JS code itself seemed to work fine as JS - // eslint-disable-next-line @typescript-eslint/no-explicit-any - qb.where('id' as any, '>', cursor!.lastSeenId as any) - if (fullObjectSelect) - qb.orWhere( - knex.raw( - `jsonb_path_query_first( data, ? )::${castType} ${cursor!.operator} ? `, - ['$.' + cursor!.field, cursor!.value] - ) - ) - else - qb.orWhere( - knex.raw(`??::${castType} ${cursor!.operator} ? `, [ - (select || []).indexOf(cursor!.field).toString(), - cursor!.value - ]) - ) - }) + // Set cursor clause, if present. If it's not present, it's an entry query; this method will return a cursor based on its given query. + // We have implemented keyset pagination for more efficient searches on larger sets. This approach depends on an order by value provided by the user and a (hidden) primary key. + // logger.debug( cursor ) + if (cursor) { + let castType = 'text' + if (typeof cursor.value === 'string') castType = 'text' + if (typeof cursor.value === 'boolean') castType = 'boolean' + if (typeof cursor.value === 'number') castType = 'numeric' + + // When strings are used inside an order clause, as mentioned above, we need to add quotes around the comparison value, as the jsonb_path_query funcs return json encoded strings (`{"test":"foo"}` => test is returned as `"foo"`) + if (castType === 'text') cursor.value = `"${cursor.value}"` + + if (operatorsWhitelist.indexOf(cursor.operator) === -1) + throw new Error('Invalid operator for cursor') + + // Unwrapping the tuple comparison of ( userOrderByField, id ) > ( lastValueOfUserOrderBy, lastSeenId ) + if (fullObjectSelect) { + if (cursor.field === 'id') { + mainQuery.where(knex.raw(`id ${cursor.operator} ? `, [cursor.value])) + } else { + mainQuery.where( + knex.raw( + `jsonb_path_query_first( data, ? )::${castType} ${cursor.operator}= ? `, + ['$.' + cursor.field, cursor.value] + ) + ) } + } else { + mainQuery.where( + knex.raw(`??::${castType} ${cursor.operator}= ? `, [ + (select || []).indexOf(cursor.field).toString(), + cursor.value + ]) + ) } - mainQuery.limit(limit) - // logger.debug( mainQuery.toString() ) - // Finally, execute the query - const rows = await mainQuery - const totalCount = rows && rows.length > 0 ? parseInt(rows[0].total_count) : 0 - - // Return early - if (totalCount === 0) return { totalCount, objects: [], cursor: null } - - // Reconstruct the object based on the provided select paths. - if (!fullObjectSelect) { - rows.forEach((o, i, arr) => { - const no = { - id: o.id, - createdAt: o.createdAt, - speckleType: o.speckleType, - totalChildrenCount: o.totalChildrenCount, - data: {} - } - let k = 0 - for (const field of select || []) { - set(no.data, field, o[k++]) - } - arr[i] = no + if (cursor.lastSeenId) { + mainQuery.andWhere((qb) => { + // Dunno what the TS issue here is, the JS code itself seemed to work fine as JS + // eslint-disable-next-line @typescript-eslint/no-explicit-any + qb.where('id' as any, '>', cursor!.lastSeenId as any) + if (fullObjectSelect) + qb.orWhere( + knex.raw( + `jsonb_path_query_first( data, ? )::${castType} ${cursor!.operator} ? `, + ['$.' + cursor!.field, cursor!.value] + ) + ) + else + qb.orWhere( + knex.raw(`??::${castType} ${cursor!.operator} ? `, [ + (select || []).indexOf(cursor!.field).toString(), + cursor!.value + ]) + ) }) } - - // Assemble the cursor for an eventual next call - const cursorObj: typeof cursor = { - field: cursor?.field || orderBy.field, - operator: - cursor?.operator || - (orderBy.direction && orderBy.direction.toLowerCase() === 'desc' ? '<' : '>'), - value: get(rows[rows.length - 1], `data.${orderBy.field}`) - } - - // If we're not ordering by id (default case, where no order by argument is provided), we need to add the last seen id of this query in order to enable keyset pagination. - if (additionalIdOrderBy) { - cursorObj.lastSeenId = rows[rows.length - 1].id - } - - // Cursor objects should be client-side opaque, hence we encode them to base64. - const cursorEncoded = Buffer.from(JSON.stringify(cursorObj), 'binary').toString( - 'base64' - ) - return { - totalCount, - objects: rows, - cursor: rows.length === limit ? cursorEncoded : null - } } + + mainQuery.limit(limit) + // logger.debug( mainQuery.toString() ) + // Finally, execute the query + const rows = await mainQuery + const totalCount = rows && rows.length > 0 ? parseInt(rows[0].total_count) : 0 + + // Return early + if (totalCount === 0) return { totalCount, objects: [], cursor: null } + + // Reconstruct the object based on the provided select paths. + if (!fullObjectSelect) { + rows.forEach((o, i, arr) => { + const no = { + id: o.id, + createdAt: o.createdAt, + speckleType: o.speckleType, + totalChildrenCount: o.totalChildrenCount, + data: {} + } + let k = 0 + for (const field of select || []) { + set(no.data, field, o[k++]) + } + arr[i] = no + }) + } + + // Assemble the cursor for an eventual next call + const cursorObj: typeof cursor = { + field: cursor?.field || orderBy.field, + operator: + cursor?.operator || + (orderBy.direction && orderBy.direction.toLowerCase() === 'desc' ? '<' : '>'), + value: get(rows[rows.length - 1], `data.${orderBy.field}`) + } + + // If we're not ordering by id (default case, where no order by argument is provided), we need to add the last seen id of this query in order to enable keyset pagination. + if (additionalIdOrderBy) { + cursorObj.lastSeenId = rows[rows.length - 1].id + } + + // Cursor objects should be client-side opaque, hence we encode them to base64. + const cursorEncoded = Buffer.from(JSON.stringify(cursorObj), 'binary').toString( + 'base64' + ) + return { + totalCount, + objects: rows, + cursor: rows.length === limit ? cursorEncoded : null + } + } diff --git a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts index b2c96a2a6..860a5ee9d 100644 --- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts +++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts @@ -4495,6 +4495,7 @@ export type WorkspaceProjectInviteCreateInput = { export type WorkspaceProjectMutations = { __typename?: 'WorkspaceProjectMutations'; create: Project; + moveToRegion: Project; moveToWorkspace: Project; updateRole: Project; }; @@ -4505,6 +4506,12 @@ export type WorkspaceProjectMutationsCreateArgs = { }; +export type WorkspaceProjectMutationsMoveToRegionArgs = { + projectId: Scalars['String']['input']; + regionKey: Scalars['String']['input']; +}; + + export type WorkspaceProjectMutationsMoveToWorkspaceArgs = { projectId: Scalars['String']['input']; workspaceId: Scalars['String']['input']; diff --git a/packages/server/modules/shared/helpers/dbHelper.ts b/packages/server/modules/shared/helpers/dbHelper.ts index d8fac4a0b..b8aa59344 100644 --- a/packages/server/modules/shared/helpers/dbHelper.ts +++ b/packages/server/modules/shared/helpers/dbHelper.ts @@ -22,7 +22,7 @@ export async function* executeBatchedSelect< >( selectQuery: Knex.QueryBuilder, options?: Partial -): AsyncGenerator, void, unknown> { +): AsyncGenerator, void, unknown> { const { batchSize = 100, trx } = options || {} if (trx) selectQuery.transacting(trx) @@ -33,7 +33,7 @@ export async function* executeBatchedSelect< let currentOffset = 0 while (hasMorePages) { const q = selectQuery.clone().offset(currentOffset) - const results = (await q) as Awaited<(typeof selectQuery)> + const results = (await q) as Awaited if (!results.length) { hasMorePages = false diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index eb54b5a10..7b646bfa3 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -287,7 +287,7 @@ export type GetAvailableRegions = (params: { workspaceId: string }) => Promise -export type AssignRegion = (params: { +export type AssignWorkspaceRegion = (params: { workspaceId: string regionKey: string }) => Promise @@ -313,6 +313,18 @@ export type UpsertWorkspaceCreationState = (params: { * Project regions */ +/** + * Updates project region and moves all regional data to target regional db + */ +export type UpdateProjectRegion = (params: { + projectId: string + regionKey: string +}) => Promise + export type CopyProjects = (params: { projectIds: string[] }) => Promise -export type CopyProjectModels = (params: { projectIds: string[] }) => Promise> -export type CopyProjectVersions = (params: { projectIds: string[] }) => Promise> +export type CopyProjectModels = (params: { + projectIds: string[] +}) => Promise> +export type CopyProjectVersions = (params: { + projectIds: string[] +}) => Promise> diff --git a/packages/server/modules/workspaces/errors/regions.ts b/packages/server/modules/workspaces/errors/regions.ts index 60ce6774e..5a73b52bc 100644 --- a/packages/server/modules/workspaces/errors/regions.ts +++ b/packages/server/modules/workspaces/errors/regions.ts @@ -5,3 +5,9 @@ export class WorkspaceRegionAssignmentError extends BaseError { static code = 'WORKSPACE_REGION_ASSIGNMENT_ERROR' static statusCode = 400 } + +export class ProjectRegionAssignmentError extends BaseError { + static defaultMessage = 'Failed to assign region to project' + static code = 'PROJECT_REGION_ASSIGNMENT_ERROR' + static statusCode = 400 +} diff --git a/packages/server/modules/workspaces/graph/resolvers/regions.ts b/packages/server/modules/workspaces/graph/resolvers/regions.ts index a80df9e94..80ae195c4 100644 --- a/packages/server/modules/workspaces/graph/resolvers/regions.ts +++ b/packages/server/modules/workspaces/graph/resolvers/regions.ts @@ -2,10 +2,13 @@ import { db } from '@/db/knex' import { Resolvers } from '@/modules/core/graph/generated/graphql' import { getWorkspacePlanFactory } from '@/modules/gatekeeper/repositories/billing' import { canWorkspaceUseRegionsFactory } from '@/modules/gatekeeper/services/featureAuthorization' -import { getDb } from '@/modules/multiregion/utils/dbSelector' +import { getDb, getProjectDbClient } from '@/modules/multiregion/utils/dbSelector' import { getRegionsFactory } from '@/modules/multiregion/repositories' import { authorizeResolver } from '@/modules/shared' import { + copyProjectModelsFactory, + copyProjectsFactory, + copyProjectVersionsFactory, getDefaultRegionFactory, upsertRegionAssignmentFactory } from '@/modules/workspaces/repositories/regions' @@ -14,10 +17,14 @@ import { upsertWorkspaceFactory } from '@/modules/workspaces/repositories/workspaces' import { - assignRegionFactory, - getAvailableRegionsFactory + assignWorkspaceRegionFactory, + getAvailableRegionsFactory, + updateProjectRegionFactory } from '@/modules/workspaces/services/regions' import { Roles } from '@speckle/shared' +import { getProjectFactory } from '@/modules/core/repositories/streams' +import { getStreamBranchCountFactory } from '@/modules/core/repositories/branches' +import { getStreamCommitCountFactory } from '@/modules/core/repositories/commits' export default { Workspace: { @@ -37,7 +44,7 @@ export default { const regionDb = await getDb({ regionKey: args.regionKey }) - const assignRegion = assignRegionFactory({ + const assignRegion = assignWorkspaceRegionFactory({ getAvailableRegions: getAvailableRegionsFactory({ getRegions: getRegionsFactory({ db }), canWorkspaceUseRegions: canWorkspaceUseRegionsFactory({ @@ -53,5 +60,35 @@ export default { return await ctx.loaders.workspaces!.getWorkspace.load(args.workspaceId) } + }, + WorkspaceProjectMutations: { + moveToRegion: async (_parent, args, context) => { + await authorizeResolver( + context.userId, + args.projectId, + Roles.Stream.Owner, + context.resourceAccessRules + ) + + const sourceDb = await getProjectDbClient({ projectId: args.projectId }) + const targetDb = await getDb({ regionKey: args.regionKey }) + + const updateProjectRegion = updateProjectRegionFactory({ + getProject: getProjectFactory({ db: sourceDb }), + countProjectModels: getStreamBranchCountFactory({ db: sourceDb }), + countProjectVersions: getStreamCommitCountFactory({ db: sourceDb }), + getAvailableRegions: getAvailableRegionsFactory({ + getRegions: getRegionsFactory({ db }), + canWorkspaceUseRegions: canWorkspaceUseRegionsFactory({ + getWorkspacePlan: getWorkspacePlanFactory({ db }) + }) + }), + copyProjects: copyProjectsFactory({ sourceDb, targetDb }), + copyProjectModels: copyProjectModelsFactory({ sourceDb, targetDb }), + copyProjectVersions: copyProjectVersionsFactory({ sourceDb, targetDb }) + }) + + return await updateProjectRegion(args) + } } } as Resolvers diff --git a/packages/server/modules/workspaces/repositories/regions.ts b/packages/server/modules/workspaces/repositories/regions.ts index c6688386d..962330ba4 100644 --- a/packages/server/modules/workspaces/repositories/regions.ts +++ b/packages/server/modules/workspaces/repositories/regions.ts @@ -1,8 +1,21 @@ -import { BranchCommits, Branches, buildTableHelper, Commits, StreamCommits, StreamFavorites, Streams, StreamsMeta } from '@/modules/core/dbSchema' +import { + BranchCommits, + Branches, + buildTableHelper, + Commits, + StreamCommits, + StreamFavorites, + Streams, + StreamsMeta +} from '@/modules/core/dbSchema' import { Branch } from '@/modules/core/domain/branches/types' import { Commit } from '@/modules/core/domain/commits/types' import { Stream } from '@/modules/core/domain/streams/types' -import { BranchCommitRecord, StreamCommitRecord, StreamFavoriteRecord } from '@/modules/core/helpers/types' +import { + BranchCommitRecord, + StreamCommitRecord, + StreamFavoriteRecord +} from '@/modules/core/helpers/types' import { RegionRecord } from '@/modules/multiregion/helpers/types' import { Regions } from '@/modules/multiregion/repositories' import { executeBatchedSelect } from '@/modules/shared/helpers/dbHelper' @@ -35,144 +48,187 @@ const tables = { export const upsertRegionAssignmentFactory = (deps: { db: Knex }): UpsertRegionAssignment => - async (params) => { - const { workspaceId, regionKey } = params - const [row] = await tables - .workspaceRegions(deps.db) - .insert({ workspaceId, regionKey }, '*') - .onConflict(['workspaceId', 'regionKey']) - .merge() + async (params) => { + const { workspaceId, regionKey } = params + const [row] = await tables + .workspaceRegions(deps.db) + .insert({ workspaceId, regionKey }, '*') + .onConflict(['workspaceId', 'regionKey']) + .merge() - return row - } + return row + } export const getDefaultRegionFactory = (deps: { db: Knex }): GetDefaultRegion => - async (params) => { - const { workspaceId } = params - const row = await tables - .regions(deps.db) - .select(Regions.cols) - .join(WorkspaceRegions.name, WorkspaceRegions.col.regionKey, Regions.col.key) - .where({ [WorkspaceRegions.col.workspaceId]: workspaceId }) - .first() + async (params) => { + const { workspaceId } = params + const row = await tables + .regions(deps.db) + .select(Regions.cols) + .join(WorkspaceRegions.name, WorkspaceRegions.col.regionKey, Regions.col.key) + .where({ [WorkspaceRegions.col.workspaceId]: workspaceId }) + .first() - return row - } + return row + } +export const copyProjectsFactory = + (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjects => + async ({ projectIds }) => { + const selectProjects = tables + .projects(deps.sourceDb) + .select('*') + .whereIn(Streams.col.id, projectIds) + const copiedProjectIds: string[] = [] -export const copyProjects = - (deps: { sourceDb: Knex, targetDb: Knex }): CopyProjects => - async ({ projectIds }) => { - const selectProjects = tables.projects(deps.sourceDb).select('*').whereIn(Streams.col.id, projectIds) - const copiedProjectIds: string[] = [] + // Copy project record + for await (const projects of executeBatchedSelect(selectProjects)) { + for (const project of projects) { + // Store copied project id + copiedProjectIds.push(project.id) - // Copy project record - for await (const projects of executeBatchedSelect(selectProjects)) { - for (const project of projects) { - // Store copied project id - copiedProjectIds.push(project.id) + // Copy `streams` row to target db + await tables.projects(deps.targetDb).insert(project).onConflict().ignore() + } - // Copy `streams` row to target db - await tables.projects(deps.targetDb) - .insert(project) + const projectIds = projects.map((project) => project.id) + + // Fetch `stream_favorites` rows for projects in batch + const selectStreamFavorites = tables + .streamFavorites(deps.sourceDb) + .select('*') + .whereIn(StreamFavorites.col.streamId, projectIds) + + for await (const streamFavorites of executeBatchedSelect(selectStreamFavorites)) { + for (const streamFavorite of streamFavorites) { + // Copy `stream_favorites` row to target db + await tables + .streamFavorites(deps.targetDb) + .insert(streamFavorite) .onConflict() .ignore() } + } - const projectIds = projects.map((project) => project.id) + // Fetch `streams_meta` rows for projects in batch + const selectStreamsMetadata = tables + .streamsMeta(deps.sourceDb) + .select('*') + .whereIn(StreamsMeta.col.streamId, projectIds) - // Fetch `stream_favorites` rows for projects in batch - const selectStreamFavorites = tables.streamFavorites(deps.sourceDb).select('*').whereIn(StreamFavorites.col.streamId, projectIds) + for await (const streamsMetadataBatch of executeBatchedSelect( + selectStreamsMetadata + )) { + for (const streamMetadata of streamsMetadataBatch) { + // Copy `streams_meta` row to target db + await tables + .streamsMeta(deps.targetDb) + .insert(streamMetadata) + .onConflict() + .ignore() + } + } + } - for await (const streamFavorites of executeBatchedSelect(selectStreamFavorites)) { - for (const streamFavorite of streamFavorites) { - // Copy `stream_favorites` row to target db - await tables.streamFavorites(deps.targetDb).insert(streamFavorite).onConflict().ignore() + return copiedProjectIds + } + +export const copyProjectModelsFactory = + (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectModels => + async ({ projectIds }) => { + const copiedModelIds: Record = projectIds.reduce( + (result, id) => ({ ...result, [id]: [] }), + {} + ) + + for (const projectId of projectIds) { + const selectModels = tables + .models(deps.sourceDb) + .select('*') + .where({ streamId: projectId }) + + for await (const models of executeBatchedSelect(selectModels)) { + for (const model of models) { + // Store copied model ids + copiedModelIds[projectId].push(model.id) + + // Copy `branches` row to target db + await tables.models(deps.targetDb).insert(model).onConflict().ignore() + } + } + } + + return copiedModelIds + } + +export const copyProjectVersionsFactory = + (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectVersions => + async ({ projectIds }) => { + const copiedVersionIds: Record = projectIds.reduce( + (result, id) => ({ ...result, [id]: [] }), + {} + ) + + for (const projectId of projectIds) { + const selectVersions = tables + .streamCommits(deps.sourceDb) + .select('*') + .join( + Commits.name, + Commits.col.id, + StreamCommits.col.commitId + ) + .where({ streamId: projectId }) + + for await (const versions of executeBatchedSelect(selectVersions)) { + for (const version of versions) { + const { commitId, ...commit } = version + + // Store copied version id + copiedVersionIds[projectId].push(commitId) + + // Copy `commits` row to target db + await tables.versions(deps.targetDb).insert(commit).onConflict().ignore() + } + + const commitIds = versions.map((version) => version.commitId) + + // Fetch `branch_commits` rows for versions in batch + const selectBranchCommits = tables + .branchCommits(deps.sourceDb) + .select('*') + .whereIn(BranchCommits.col.commitId, commitIds) + + for await (const branchCommits of executeBatchedSelect(selectBranchCommits)) { + for (const branchCommit of branchCommits) { + // Copy `branch_commits` row to target db + await tables + .branchCommits(deps.targetDb) + .insert(branchCommit) + .onConflict() + .ignore() } } - // Fetch `streams_meta` rows for projects in batch - const selectStreamsMetadata = tables.streamsMeta(deps.sourceDb).select('*').whereIn(StreamsMeta.col.streamId, projectIds) + // Fetch `stream_commits` rows for versions in batch + const selectStreamCommits = tables + .streamCommits(deps.sourceDb) + .select('*') + .whereIn(StreamCommits.col.commitId, commitIds) - for await (const streamsMetadataBatch of executeBatchedSelect(selectStreamsMetadata)) { - for (const streamMetadata of streamsMetadataBatch) { - // Copy `streams_meta` row to target db - await tables.streamsMeta(deps.targetDb).insert(streamMetadata).onConflict().ignore() + for await (const streamCommits of executeBatchedSelect(selectStreamCommits)) { + for (const streamCommit of streamCommits) { + // Copy `stream_commits` row to target db + await tables + .streamCommits(deps.targetDb) + .insert(streamCommit) + .onConflict() + .ignore() } } } - - return copiedProjectIds - } - -export const copyProjectModels = - (deps: { sourceDb: Knex, targetDb: Knex }): CopyProjectModels => - async ({ projectIds }) => { - const copiedModelIds: Record = projectIds.reduce((result, id) => ({ ...result, [id]: [] }), {}) - - for (const projectId of projectIds) { - const selectModels = tables.models(deps.sourceDb).select('*').where({ streamId: projectId }) - - for await (const models of executeBatchedSelect(selectModels)) { - for (const model of models) { - // Store copied model ids - copiedModelIds[projectId].push(model.id) - - // Copy `branches` row to target db - await tables.models(deps.targetDb).insert(model).onConflict().ignore() - } - } - } - - return copiedModelIds - } - -export const copyProjectVersions = - (deps: { sourceDb: Knex, targetDb: Knex }): CopyProjectVersions => - async ({ projectIds }) => { - const copiedVersionIds: Record = projectIds.reduce((result, id) => ({ ...result, [id]: [] }), {}) - - for (const projectId of projectIds) { - const selectVersions = tables.streamCommits(deps.sourceDb).select('*') - .join(Commits.name, Commits.col.id, StreamCommits.col.commitId) - .where({ streamId: projectId }) - - for await (const versions of executeBatchedSelect(selectVersions)) { - for (const version of versions) { - const { commitId, ...commit } = version - - // Store copied version id - copiedVersionIds[projectId].push(commitId) - - // Copy `commits` row to target db - await tables.versions(deps.targetDb).insert(commit).onConflict().ignore() - } - - const commitIds = versions.map((version) => version.commitId) - - // Fetch `branch_commits` rows for versions in batch - const selectBranchCommits = tables.branchCommits(deps.sourceDb).select('*').whereIn(BranchCommits.col.commitId, commitIds) - - for await (const branchCommits of executeBatchedSelect(selectBranchCommits)) { - for (const branchCommit of branchCommits) { - // Copy `branch_commits` row to target db - await tables.branchCommits(deps.targetDb).insert(branchCommit).onConflict().ignore() - } - } - - // Fetch `stream_commits` rows for versions in batch - const selectStreamCommits = tables.streamCommits(deps.sourceDb).select('*').whereIn(StreamCommits.col.commitId, commitIds) - - for await (const streamCommits of executeBatchedSelect(selectStreamCommits)) { - for (const streamCommit of streamCommits) { - // Copy `stream_commits` row to target db - await tables.streamCommits(deps.targetDb).insert(streamCommit).onConflict().ignore() - } - } - } - } - - return copiedVersionIds } + return copiedVersionIds + } diff --git a/packages/server/modules/workspaces/services/projects.ts b/packages/server/modules/workspaces/services/projects.ts index f8d81f058..6ce8483ae 100644 --- a/packages/server/modules/workspaces/services/projects.ts +++ b/packages/server/modules/workspaces/services/projects.ts @@ -100,23 +100,23 @@ type GetWorkspaceProjectsReturnValue = { export const getWorkspaceProjectsFactory = ({ getStreams }: { getStreams: GetUserStreamsPage }) => - async ( - args: GetWorkspaceProjectsArgs, - opts: GetWorkspaceProjectsOptions - ): Promise => { - const { streams, cursor } = await getStreams({ - cursor: opts.cursor, - limit: opts.limit || 25, - searchQuery: opts.filter?.search || undefined, - workspaceId: args.workspaceId, - userId: opts.filter.userId - }) + async ( + args: GetWorkspaceProjectsArgs, + opts: GetWorkspaceProjectsOptions + ): Promise => { + const { streams, cursor } = await getStreams({ + cursor: opts.cursor, + limit: opts.limit || 25, + searchQuery: opts.filter?.search || undefined, + workspaceId: args.workspaceId, + userId: opts.filter.userId + }) - return { - items: streams, - cursor - } + return { + items: streams, + cursor } + } type MoveProjectToWorkspaceArgs = { projectId: string @@ -141,78 +141,78 @@ export const moveProjectToWorkspaceFactory = getWorkspaceRoleToDefaultProjectRoleMapping: GetWorkspaceRoleToDefaultProjectRoleMapping updateWorkspaceRole: UpdateWorkspaceRole }) => - async ({ - projectId, - workspaceId - }: MoveProjectToWorkspaceArgs): Promise => { - const project = await getProject({ projectId }) + async ({ + projectId, + workspaceId + }: MoveProjectToWorkspaceArgs): Promise => { + const project = await getProject({ projectId }) - if (!project) throw new ProjectNotFoundError() - if (project.workspaceId?.length) { - // We do not currently support moving projects between workspaces - throw new WorkspaceInvalidProjectError( - 'Specified project already belongs to a workspace. Moving between workspaces is not yet supported.' - ) - } - - // Update roles for current project members - const projectTeam = await getProjectCollaborators({ projectId }) - const workspaceTeam = await getWorkspaceRoles({ workspaceId }) - const defaultProjectRoleMapping = await getWorkspaceRoleToDefaultProjectRoleMapping( - { workspaceId } + if (!project) throw new ProjectNotFoundError() + if (project.workspaceId?.length) { + // We do not currently support moving projects between workspaces + throw new WorkspaceInvalidProjectError( + 'Specified project already belongs to a workspace. Moving between workspaces is not yet supported.' ) - - for (const projectMembers of chunk(projectTeam, 5)) { - await Promise.all( - projectMembers.map( - async ({ id: userId, role: serverRole, streamRole: currentProjectRole }) => { - // Update workspace role. Prefer existing workspace role if there is one. - const currentWorkspaceRole = workspaceTeam.find( - (role) => role.userId === userId - ) - const nextWorkspaceRole = currentWorkspaceRole ?? { - userId, - workspaceId, - role: - serverRole === Roles.Server.Guest - ? Roles.Workspace.Guest - : Roles.Workspace.Member, - createdAt: new Date() - } - await updateWorkspaceRole(nextWorkspaceRole) - - // Update project role. Prefer default workspace project role if more permissive. - const defaultProjectRole = - defaultProjectRoleMapping[nextWorkspaceRole.role] ?? Roles.Stream.Reviewer - const nextProjectRole = orderByWeight( - [currentProjectRole, defaultProjectRole], - coreUserRoles - )[0] - await upsertProjectRole({ - userId, - projectId, - role: nextProjectRole.name as StreamRoles - }) - } - ) - ) - } - - // Assign project to workspace - return await updateProject({ projectUpdate: { id: projectId, workspaceId } }) } + // Update roles for current project members + const projectTeam = await getProjectCollaborators({ projectId }) + const workspaceTeam = await getWorkspaceRoles({ workspaceId }) + const defaultProjectRoleMapping = await getWorkspaceRoleToDefaultProjectRoleMapping( + { workspaceId } + ) + + for (const projectMembers of chunk(projectTeam, 5)) { + await Promise.all( + projectMembers.map( + async ({ id: userId, role: serverRole, streamRole: currentProjectRole }) => { + // Update workspace role. Prefer existing workspace role if there is one. + const currentWorkspaceRole = workspaceTeam.find( + (role) => role.userId === userId + ) + const nextWorkspaceRole = currentWorkspaceRole ?? { + userId, + workspaceId, + role: + serverRole === Roles.Server.Guest + ? Roles.Workspace.Guest + : Roles.Workspace.Member, + createdAt: new Date() + } + await updateWorkspaceRole(nextWorkspaceRole) + + // Update project role. Prefer default workspace project role if more permissive. + const defaultProjectRole = + defaultProjectRoleMapping[nextWorkspaceRole.role] ?? Roles.Stream.Reviewer + const nextProjectRole = orderByWeight( + [currentProjectRole, defaultProjectRole], + coreUserRoles + )[0] + await upsertProjectRole({ + userId, + projectId, + role: nextProjectRole.name as StreamRoles + }) + } + ) + ) + } + + // Assign project to workspace + return await updateProject({ projectUpdate: { id: projectId, workspaceId } }) + } + export const moveProjectToRegionFactory = (deps: { - copyProjects: CopyProjects, - copyProjectModels: CopyProjectModels, + copyProjects: CopyProjects + copyProjectModels: CopyProjectModels copyProjectVersions: CopyProjectVersions }) => - async (args: { projectId: string }): Promise => { - const projectIds = await deps.copyProjects({ projectIds: [args.projectId] }) - const modelIdsByProjectId = await deps.copyProjectModels({ projectIds }) - const versionIdsByProjectId = await deps.copyProjectVersions({ projectIds }) - } + async (args: { projectId: string }): Promise => { + const projectIds = await deps.copyProjects({ projectIds: [args.projectId] }) + const modelIdsByProjectId = await deps.copyProjectModels({ projectIds }) + const versionIdsByProjectId = await deps.copyProjectVersions({ projectIds }) + } export const getWorkspaceRoleToDefaultProjectRoleMappingFactory = ({ @@ -220,20 +220,20 @@ export const getWorkspaceRoleToDefaultProjectRoleMappingFactory = }: { getWorkspace: GetWorkspace }): GetWorkspaceRoleToDefaultProjectRoleMapping => - async ({ workspaceId }) => { - const workspace = await getWorkspace({ workspaceId }) + async ({ workspaceId }) => { + const workspace = await getWorkspace({ workspaceId }) - if (!workspace) { - throw new WorkspaceNotFoundError() - } - - return { - [Roles.Workspace.Guest]: null, - [Roles.Workspace.Member]: workspace.defaultProjectRole, - [Roles.Workspace.Admin]: Roles.Stream.Owner - } + if (!workspace) { + throw new WorkspaceNotFoundError() } + return { + [Roles.Workspace.Guest]: null, + [Roles.Workspace.Member]: workspace.defaultProjectRole, + [Roles.Workspace.Admin]: Roles.Stream.Owner + } + } + export const updateWorkspaceProjectRoleFactory = ({ getStream, @@ -244,63 +244,63 @@ export const updateWorkspaceProjectRoleFactory = getWorkspaceRoleForUser: GetWorkspaceRoleForUser updateStreamRoleAndNotify: UpdateStreamRole }): UpdateWorkspaceProjectRole => - async ({ role, updater }) => { - const { workspaceId } = (await getStream({ streamId: role.projectId })) ?? {} - if (!workspaceId) throw new WorkspaceInvalidProjectError() + async ({ role, updater }) => { + const { workspaceId } = (await getStream({ streamId: role.projectId })) ?? {} + if (!workspaceId) throw new WorkspaceInvalidProjectError() - const currentWorkspaceRole = await getWorkspaceRoleForUser({ - workspaceId, - userId: role.userId - }) + const currentWorkspaceRole = await getWorkspaceRoleForUser({ + workspaceId, + userId: role.userId + }) - if (currentWorkspaceRole?.role === Roles.Workspace.Admin) { - // User is workspace admin and cannot have their project roles changed - throw new WorkspaceAdminError() - } - - if ( - currentWorkspaceRole?.role === Roles.Workspace.Guest && - role.role === Roles.Stream.Owner - ) { - // Workspace guests cannot be project owners - throw new WorkspaceInvalidRoleError('Workspace guests cannot be project owners.') - } - - return await updateStreamRoleAndNotify( - role, - updater.userId!, - updater.resourceAccessRules - ) + if (currentWorkspaceRole?.role === Roles.Workspace.Admin) { + // User is workspace admin and cannot have their project roles changed + throw new WorkspaceAdminError() } + if ( + currentWorkspaceRole?.role === Roles.Workspace.Guest && + role.role === Roles.Stream.Owner + ) { + // Workspace guests cannot be project owners + throw new WorkspaceInvalidRoleError('Workspace guests cannot be project owners.') + } + + return await updateStreamRoleAndNotify( + role, + updater.userId!, + updater.resourceAccessRules + ) + } + export const createWorkspaceProjectFactory = (deps: { getDefaultRegion: GetDefaultRegion }) => - async (params: { input: WorkspaceProjectCreateInput; ownerId: string }) => { - const { input, ownerId } = params - const workspaceDefaultRegion = await deps.getDefaultRegion({ - workspaceId: input.workspaceId - }) - const regionKey = workspaceDefaultRegion?.key - const projectDb = await getDb({ regionKey }) - const db = mainDb + async (params: { input: WorkspaceProjectCreateInput; ownerId: string }) => { + const { input, ownerId } = params + const workspaceDefaultRegion = await deps.getDefaultRegion({ + workspaceId: input.workspaceId + }) + const regionKey = workspaceDefaultRegion?.key + const projectDb = await getDb({ regionKey }) + const db = mainDb - // todo, use the command factory here, but for that, we need to migrate to the event bus - // deps not injected to ensure proper DB injection - const createNewProject = createNewProjectFactory({ - storeProject: storeProjectFactory({ db: projectDb }), - getProject: getProjectFactory({ db }), - deleteProject: deleteProjectFactory({ db: projectDb }), - storeModel: storeModelFactory({ db: projectDb }), - // THIS MUST GO TO THE MAIN DB - storeProjectRole: storeProjectRoleFactory({ db }), - projectsEventsEmitter: ProjectsEmitter.emit - }) + // todo, use the command factory here, but for that, we need to migrate to the event bus + // deps not injected to ensure proper DB injection + const createNewProject = createNewProjectFactory({ + storeProject: storeProjectFactory({ db: projectDb }), + getProject: getProjectFactory({ db }), + deleteProject: deleteProjectFactory({ db: projectDb }), + storeModel: storeModelFactory({ db: projectDb }), + // THIS MUST GO TO THE MAIN DB + storeProjectRole: storeProjectRoleFactory({ db }), + projectsEventsEmitter: ProjectsEmitter.emit + }) - const project = await createNewProject({ - ...input, - regionKey, - ownerId - }) + const project = await createNewProject({ + ...input, + regionKey, + ownerId + }) - return project - } + return project + } diff --git a/packages/server/modules/workspaces/services/regions.ts b/packages/server/modules/workspaces/services/regions.ts index 6d5c9cb22..e673fc522 100644 --- a/packages/server/modules/workspaces/services/regions.ts +++ b/packages/server/modules/workspaces/services/regions.ts @@ -1,14 +1,24 @@ +import { GetStreamBranchCount } from '@/modules/core/domain/branches/operations' +import { GetStreamCommitCount } from '@/modules/core/domain/commits/operations' +import { GetProject } from '@/modules/core/domain/projects/operations' import { WorkspaceFeatureAccessFunction } from '@/modules/gatekeeper/domain/operations' import { GetRegions } from '@/modules/multiregion/domain/operations' import { - AssignRegion, + AssignWorkspaceRegion, + CopyProjectModels, + CopyProjects, + CopyProjectVersions, GetAvailableRegions, GetDefaultRegion, GetWorkspace, + UpdateProjectRegion, UpsertRegionAssignment, UpsertWorkspace } from '@/modules/workspaces/domain/operations' -import { WorkspaceRegionAssignmentError } from '@/modules/workspaces/errors/regions' +import { + ProjectRegionAssignmentError, + WorkspaceRegionAssignmentError +} from '@/modules/workspaces/errors/regions' export const getAvailableRegionsFactory = (deps: { @@ -25,14 +35,14 @@ export const getAvailableRegionsFactory = return await deps.getRegions() } -export const assignRegionFactory = +export const assignWorkspaceRegionFactory = (deps: { getAvailableRegions: GetAvailableRegions upsertRegionAssignment: UpsertRegionAssignment getDefaultRegion: GetDefaultRegion getWorkspace: GetWorkspace insertRegionWorkspace: UpsertWorkspace - }): AssignRegion => + }): AssignWorkspaceRegion => async (params) => { const { workspaceId, regionKey } = params @@ -69,3 +79,73 @@ export const assignRegionFactory = // Copy workspace into region db await deps.insertRegionWorkspace({ workspace }) } + +export const updateProjectRegionFactory = + (deps: { + getProject: GetProject + countProjectModels: GetStreamBranchCount + countProjectVersions: GetStreamCommitCount + getAvailableRegions: GetAvailableRegions + copyProjects: CopyProjects + copyProjectModels: CopyProjectModels + copyProjectVersions: CopyProjectVersions + }): UpdateProjectRegion => + async (params) => { + const { projectId, regionKey } = params + + const project = await deps.getProject({ projectId }) + if (!project) { + throw new ProjectRegionAssignmentError('Project not found', { + info: { params } + }) + } + if (!project.workspaceId) { + throw new ProjectRegionAssignmentError('Project not a part of a workspace', { + info: { params } + }) + } + + const availableRegions = await deps.getAvailableRegions({ + workspaceId: project.workspaceId + }) + if (!availableRegions.find((region) => region.key === regionKey)) { + throw new ProjectRegionAssignmentError( + 'Specified region not available for workspace', + { + info: { + params, + workspaceId: project.workspaceId + } + } + ) + } + + // Move commits + const projectIds = await deps.copyProjects({ projectIds: [projectId] }) + const modelIds = await deps.copyProjectModels({ projectIds }) + const versionIds = await deps.copyProjectVersions({ projectIds }) + + // TODO: Move objects + // TODO: Move automations + // TODO: Move comments + // TODO: Move file blobs + // TODO: Move webhooks + + // TODO: Validate state after move captures latest state of project + const sourceProjectModelCount = await deps.countProjectModels(projectId) + const sourceProjectVersionCount = await deps.countProjectVersions(projectId) + + const isReconciled = + modelIds[projectId].length === sourceProjectModelCount && + versionIds[projectId].length === sourceProjectVersionCount + + if (!isReconciled) { + // TODO: Move failed or source project added data while changing regions. Retry move. + throw new ProjectRegionAssignmentError( + 'Missing data from source project in target region copy after move.' + ) + } + + // TODO: Update project region in db + return { ...project, regionKey } + } diff --git a/packages/server/modules/workspaces/tests/helpers/creation.ts b/packages/server/modules/workspaces/tests/helpers/creation.ts index bb35888bd..500fe1d35 100644 --- a/packages/server/modules/workspaces/tests/helpers/creation.ts +++ b/packages/server/modules/workspaces/tests/helpers/creation.ts @@ -64,7 +64,7 @@ import { import { SetOptional } from 'type-fest' import { isMultiRegionTestMode } from '@/test/speckle-helpers/regions' import { - assignRegionFactory, + assignWorkspaceRegionFactory, getAvailableRegionsFactory } from '@/modules/workspaces/services/regions' import { getRegionsFactory } from '@/modules/multiregion/repositories' @@ -185,7 +185,7 @@ export const createTestWorkspace = async ( if (useRegion) { const regionDb = await getDb({ regionKey }) - const assignRegion = assignRegionFactory({ + const assignRegion = assignWorkspaceRegionFactory({ getAvailableRegions: getAvailableRegionsFactory({ getRegions: getRegionsFactory({ db }), canWorkspaceUseRegions: canWorkspaceUseRegionsFactory({ diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index cabbd586e..19789627f 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -4496,6 +4496,7 @@ export type WorkspaceProjectInviteCreateInput = { export type WorkspaceProjectMutations = { __typename?: 'WorkspaceProjectMutations'; create: Project; + moveToRegion: Project; moveToWorkspace: Project; updateRole: Project; }; @@ -4506,6 +4507,12 @@ export type WorkspaceProjectMutationsCreateArgs = { }; +export type WorkspaceProjectMutationsMoveToRegionArgs = { + projectId: Scalars['String']['input']; + regionKey: Scalars['String']['input']; +}; + + export type WorkspaceProjectMutationsMoveToWorkspaceArgs = { projectId: Scalars['String']['input']; workspaceId: Scalars['String']['input']; From f64a8bbabaa753842b3a387a9f467eccd211bba5 Mon Sep 17 00:00:00 2001 From: Chuck Driesler Date: Tue, 14 Jan 2025 15:13:32 +0000 Subject: [PATCH 03/56] chore(regions): successful basic test of project region change --- .../lib/common/generated/gql/graphql.ts | 15 +++- .../modules/core/graph/generated/graphql.ts | 4 + .../server/modules/core/services/projects.ts | 2 +- .../graph/generated/graphql.ts | 4 + .../modules/workspaces/services/projects.ts | 15 ---- .../tests/integration/projects.graph.spec.ts | 79 ++++++++++++++++++- .../server/test/graphql/generated/graphql.ts | 13 +++ packages/server/test/graphql/multiRegion.ts | 12 +++ 8 files changed, 126 insertions(+), 18 deletions(-) diff --git a/packages/frontend-2/lib/common/generated/gql/graphql.ts b/packages/frontend-2/lib/common/generated/gql/graphql.ts index 3468bbbbb..c27fb8c71 100644 --- a/packages/frontend-2/lib/common/generated/gql/graphql.ts +++ b/packages/frontend-2/lib/common/generated/gql/graphql.ts @@ -2595,7 +2595,7 @@ export type Query = { /** Look up server users */ users: UserSearchResultCollection; /** Look up server users with a collection of emails */ - usersByEmail: UserSearchResultCollection; + usersByEmail: Array>; /** Validates the slug, to make sure it contains only valid characters and its not taken. */ validateWorkspaceSlug: Scalars['Boolean']['output']; workspace: Workspace; @@ -3872,6 +3872,7 @@ export type UserProjectsFilter = { onlyWithRoles?: InputMaybe>; /** Filter out projects by name */ search?: InputMaybe; + workspaceId?: InputMaybe; }; export type UserProjectsUpdatedMessage = { @@ -4491,6 +4492,11 @@ export type WorkspaceProjectInviteCreateInput = { export type WorkspaceProjectMutations = { __typename?: 'WorkspaceProjectMutations'; create: Project; + /** + * Update project region and move all regional data to new db. + * TODO: Currently performs all operations synchronously in request, should probably be scheduled. + */ + moveToRegion: Project; moveToWorkspace: Project; updateRole: Project; }; @@ -4501,6 +4507,12 @@ export type WorkspaceProjectMutationsCreateArgs = { }; +export type WorkspaceProjectMutationsMoveToRegionArgs = { + projectId: Scalars['String']['input']; + regionKey: Scalars['String']['input']; +}; + + export type WorkspaceProjectMutationsMoveToWorkspaceArgs = { projectId: Scalars['String']['input']; workspaceId: Scalars['String']['input']; @@ -8132,6 +8144,7 @@ export type WorkspacePlanFieldArgs = { } export type WorkspaceProjectMutationsFieldArgs = { create: WorkspaceProjectMutationsCreateArgs, + moveToRegion: WorkspaceProjectMutationsMoveToRegionArgs, moveToWorkspace: WorkspaceProjectMutationsMoveToWorkspaceArgs, updateRole: WorkspaceProjectMutationsUpdateRoleArgs, } diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index 9f5762982..ac5f14b7c 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -4514,6 +4514,10 @@ export type WorkspaceProjectInviteCreateInput = { export type WorkspaceProjectMutations = { __typename?: 'WorkspaceProjectMutations'; create: Project; + /** + * Update project region and move all regional data to new db. + * TODO: Currently performs all operations synchronously in request, should probably be scheduled. + */ moveToRegion: Project; moveToWorkspace: Project; updateRole: Project; diff --git a/packages/server/modules/core/services/projects.ts b/packages/server/modules/core/services/projects.ts index f5f12abea..3e87bd391 100644 --- a/packages/server/modules/core/services/projects.ts +++ b/packages/server/modules/core/services/projects.ts @@ -62,7 +62,7 @@ export const createNewProjectFactory = async () => { await getProject({ projectId }) }, - { maxAttempts: 10 } + { maxAttempts: 100 } ) } catch (err) { if (err instanceof StreamNotFoundError) { diff --git a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts index 860a5ee9d..406faf17f 100644 --- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts +++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts @@ -4495,6 +4495,10 @@ export type WorkspaceProjectInviteCreateInput = { export type WorkspaceProjectMutations = { __typename?: 'WorkspaceProjectMutations'; create: Project; + /** + * Update project region and move all regional data to new db. + * TODO: Currently performs all operations synchronously in request, should probably be scheduled. + */ moveToRegion: Project; moveToWorkspace: Project; updateRole: Project; diff --git a/packages/server/modules/workspaces/services/projects.ts b/packages/server/modules/workspaces/services/projects.ts index 6ce8483ae..886a28c99 100644 --- a/packages/server/modules/workspaces/services/projects.ts +++ b/packages/server/modules/workspaces/services/projects.ts @@ -1,8 +1,5 @@ import { StreamRecord } from '@/modules/core/helpers/types' import { - CopyProjectModels, - CopyProjects, - CopyProjectVersions, GetDefaultRegion, GetWorkspace, GetWorkspaceRoleForUser, @@ -202,18 +199,6 @@ export const moveProjectToWorkspaceFactory = return await updateProject({ projectUpdate: { id: projectId, workspaceId } }) } -export const moveProjectToRegionFactory = - (deps: { - copyProjects: CopyProjects - copyProjectModels: CopyProjectModels - copyProjectVersions: CopyProjectVersions - }) => - async (args: { projectId: string }): Promise => { - const projectIds = await deps.copyProjects({ projectIds: [args.projectId] }) - const modelIdsByProjectId = await deps.copyProjectModels({ projectIds }) - const versionIdsByProjectId = await deps.copyProjectVersions({ projectIds }) - } - export const getWorkspaceRoleToDefaultProjectRoleMappingFactory = ({ getWorkspace diff --git a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts index d9c66f859..ad844e2de 100644 --- a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts @@ -1,5 +1,7 @@ import { db } from '@/db/knex' import { AllScopes } from '@/modules/core/helpers/mainConstants' +import { createRandomEmail } from '@/modules/core/helpers/testHelpers' +import { StreamRecord } from '@/modules/core/helpers/types' import { grantStreamPermissionsFactory } from '@/modules/core/repositories/streams' import { BasicTestWorkspace, @@ -8,6 +10,7 @@ import { import { BasicTestUser, createAuthTokenForUser, + createTestUser, createTestUsers } from '@/test/authHelper' import { @@ -15,7 +18,8 @@ import { CreateWorkspaceProjectDocument, GetWorkspaceProjectsDocument, GetWorkspaceTeamDocument, - MoveProjectToWorkspaceDocument + MoveProjectToWorkspaceDocument, + UpdateProjectRegionDocument } from '@/test/graphql/generated/graphql' import { createTestContext, @@ -23,10 +27,16 @@ import { TestApolloServer } from '@/test/graphqlHelper' import { beforeEachContext } from '@/test/hooks' +import { + getMainTestRegionClient, + isMultiRegionTestMode, + waitForRegionUser +} from '@/test/speckle-helpers/regions' import { BasicTestStream, createTestStream } from '@/test/speckle-helpers/streamHelper' import { Roles } from '@speckle/shared' import { expect } from 'chai' import cryptoRandomString from 'crypto-random-string' +import { Knex } from 'knex' const grantStreamPermissions = grantStreamPermissionsFactory({ db }) @@ -272,3 +282,70 @@ describe('Workspace project GQL CRUD', () => { }) }) }) + +isMultiRegionTestMode() + ? describe('Workspace project region changes', () => { + const testRegionKey = 'region1' + + const adminUser: BasicTestUser = { + id: '', + name: 'John Speckle', + email: createRandomEmail() + } + + const testWorkspace: BasicTestWorkspace = { + id: '', + ownerId: '', + slug: '', + name: 'Unlimited Workspace' + } + + const testProject: BasicTestStream = { + id: '', + ownerId: '', + name: 'Regional Project', + isPublic: true + } + + let apollo: TestApolloServer + let regionDb: Knex + + before(async () => { + await createTestUser(adminUser) + await waitForRegionUser(adminUser) + await createTestWorkspace(testWorkspace, adminUser, { + regionKey: testRegionKey, + addPlan: { + name: 'unlimited', + status: 'valid' + } + }) + + testProject.workspaceId = testWorkspace.id + + apollo = await testApolloServer({ authUserId: adminUser.id }) + regionDb = getMainTestRegionClient() + }) + + beforeEach(async () => { + await createTestStream(testProject, adminUser) + }) + + it('moves project record to target regional db', async () => { + const res = await apollo.execute(UpdateProjectRegionDocument, { + projectId: testProject.id, + regionKey: testRegionKey + }) + + expect(res).to.not.haveGraphQLErrors() + + const project = await regionDb + .table('streams') + .select('*') + .where({ id: testProject.id }) + .first() + + expect(project).to.not.be.undefined + }) + }) + : void 0 diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index 19789627f..17565adea 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -4496,6 +4496,10 @@ export type WorkspaceProjectInviteCreateInput = { export type WorkspaceProjectMutations = { __typename?: 'WorkspaceProjectMutations'; create: Project; + /** + * Update project region and move all regional data to new db. + * TODO: Currently performs all operations synchronously in request, should probably be scheduled. + */ moveToRegion: Project; moveToWorkspace: Project; updateRole: Project; @@ -5087,6 +5091,14 @@ export type UpdateRegionMutationVariables = Exact<{ export type UpdateRegionMutation = { __typename?: 'Mutation', serverInfoMutations: { __typename?: 'ServerInfoMutations', multiRegion: { __typename?: 'ServerRegionMutations', update: { __typename?: 'ServerRegionItem', id: string, key: string, name: string, description?: string | null } } } }; +export type UpdateProjectRegionMutationVariables = Exact<{ + projectId: Scalars['String']['input']; + regionKey: Scalars['String']['input']; +}>; + + +export type UpdateProjectRegionMutation = { __typename?: 'Mutation', workspaceMutations: { __typename?: 'WorkspaceMutations', projects: { __typename?: 'WorkspaceProjectMutations', moveToRegion: { __typename?: 'Project', id: string } } } }; + export type BasicProjectAccessRequestFieldsFragment = { __typename?: 'ProjectAccessRequest', id: string, requesterId: string, projectId: string, createdAt: string, requester: { __typename?: 'LimitedUser', id: string, name: string } }; export type CreateProjectAccessRequestMutationVariables = Exact<{ @@ -5608,6 +5620,7 @@ export const GetAvailableRegionKeysDocument = {"kind":"Document","definitions":[ export const CreateNewRegionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateNewRegion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"CreateServerRegionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverInfoMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"multiRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MainRegionMetadata"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MainRegionMetadata"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerRegionItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]} as unknown as DocumentNode; export const GetRegionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetRegions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"multiRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"regions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MainRegionMetadata"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MainRegionMetadata"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerRegionItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]} as unknown as DocumentNode; export const UpdateRegionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateRegion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"UpdateServerRegionInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverInfoMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"multiRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"update"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"MainRegionMetadata"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MainRegionMetadata"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerRegionItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]} as unknown as DocumentNode; +export const UpdateProjectRegionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateProjectRegion"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"regionKey"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"moveToRegion"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}},{"kind":"Argument","name":{"kind":"Name","value":"regionKey"},"value":{"kind":"Variable","name":{"kind":"Name","value":"regionKey"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateProjectAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateProjectAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"accessRequestMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"create"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectAccessRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"requester"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"requesterId"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode; export const GetActiveUserProjectAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetActiveUserProjectAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectAccessRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectAccessRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"requester"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"requesterId"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode; export const GetActiveUserFullProjectAccessRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetActiveUserFullProjectAccessRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectAccessRequest"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BasicProjectAccessRequestFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectAccessRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"requester"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"requesterId"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]} as unknown as DocumentNode; diff --git a/packages/server/test/graphql/multiRegion.ts b/packages/server/test/graphql/multiRegion.ts index ea44f957f..71a73e57b 100644 --- a/packages/server/test/graphql/multiRegion.ts +++ b/packages/server/test/graphql/multiRegion.ts @@ -60,3 +60,15 @@ export const updateRegionMutation = gql` ${mainRegionMetadataFragment} ` + +export const updateProjectRegionMutation = gql` + mutation UpdateProjectRegion($projectId: String!, $regionKey: String!) { + workspaceMutations { + projects { + moveToRegion(projectId: $projectId, regionKey: $regionKey) { + id + } + } + } + } +` From f1a82e63112e39507878b26c16b6a4f6db0147dd Mon Sep 17 00:00:00 2001 From: Chuck Driesler Date: Thu, 16 Jan 2025 19:46:02 +0000 Subject: [PATCH 04/56] fix(regions): sabrina carpenter please please please --- docker-compose-deps.yml | 28 +++++++++++++++++++ .../tests/integration/projects.graph.spec.ts | 11 ++++---- packages/server/multiregion.test.example.json | 14 ++++++++++ 3 files changed, 48 insertions(+), 5 deletions(-) diff --git a/docker-compose-deps.yml b/docker-compose-deps.yml index d5068bf4e..e6e59ea02 100644 --- a/docker-compose-deps.yml +++ b/docker-compose-deps.yml @@ -34,6 +34,22 @@ services: ports: - '127.0.0.1:5401:5432' + postgres-region2: + build: + context: . + dockerfile: utils/postgres/Dockerfile + restart: always + environment: + POSTGRES_DB: speckle + POSTGRES_USER: speckle + POSTGRES_PASSWORD: speckle + volumes: + - postgres-region2-data:/var/lib/postgresql/data/ + - ./setup/db/10-docker_postgres_init.sql:/docker-entrypoint-initdb.d/10-docker_postgres_init.sql + - ./setup/db/11-docker_postgres_keycloack_init.sql:/docker-entrypoint-initdb.d/11-docker_postgres_keycloack_init.sql + ports: + - '127.0.0.1:5402:5432' + redis: image: 'redis:7-alpine' restart: always @@ -62,6 +78,16 @@ services: - '127.0.0.1:9020:9000' - '127.0.0.1:9021:9001' + minio-region2: + image: 'minio/minio' + command: server /data --console-address ":9001" + restart: always + volumes: + - minio-region2-data:/data + ports: + - '127.0.0.1:9040:9000' + - '127.0.0.1:9041:9001' + # Local OIDC provider for testing keycloak: image: quay.io/keycloak/keycloak:25.0 @@ -133,8 +159,10 @@ services: volumes: postgres-data: postgres-region1-data: + postgres-region2-data: redis-data: pgadmin-data: redis_insight-data: minio-data: minio-region1-data: + minio-region2-data: diff --git a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts index ad844e2de..60c749375 100644 --- a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts @@ -3,6 +3,7 @@ import { AllScopes } from '@/modules/core/helpers/mainConstants' import { createRandomEmail } from '@/modules/core/helpers/testHelpers' import { StreamRecord } from '@/modules/core/helpers/types' import { grantStreamPermissionsFactory } from '@/modules/core/repositories/streams' +import { getDb } from '@/modules/multiregion/utils/dbSelector' import { BasicTestWorkspace, createTestWorkspace @@ -28,7 +29,6 @@ import { } from '@/test/graphqlHelper' import { beforeEachContext } from '@/test/hooks' import { - getMainTestRegionClient, isMultiRegionTestMode, waitForRegionUser } from '@/test/speckle-helpers/regions' @@ -285,7 +285,8 @@ describe('Workspace project GQL CRUD', () => { isMultiRegionTestMode() ? describe('Workspace project region changes', () => { - const testRegionKey = 'region1' + const regionKey1 = 'region1' + const regionKey2 = 'region2' const adminUser: BasicTestUser = { id: '', @@ -314,7 +315,7 @@ isMultiRegionTestMode() await createTestUser(adminUser) await waitForRegionUser(adminUser) await createTestWorkspace(testWorkspace, adminUser, { - regionKey: testRegionKey, + regionKey: regionKey1, addPlan: { name: 'unlimited', status: 'valid' @@ -324,7 +325,7 @@ isMultiRegionTestMode() testProject.workspaceId = testWorkspace.id apollo = await testApolloServer({ authUserId: adminUser.id }) - regionDb = getMainTestRegionClient() + regionDb = await getDb({ regionKey: regionKey2 }) }) beforeEach(async () => { @@ -334,7 +335,7 @@ isMultiRegionTestMode() it('moves project record to target regional db', async () => { const res = await apollo.execute(UpdateProjectRegionDocument, { projectId: testProject.id, - regionKey: testRegionKey + regionKey: regionKey2 }) expect(res).to.not.haveGraphQLErrors() diff --git a/packages/server/multiregion.test.example.json b/packages/server/multiregion.test.example.json index 0eff18956..6fc82a924 100644 --- a/packages/server/multiregion.test.example.json +++ b/packages/server/multiregion.test.example.json @@ -27,6 +27,20 @@ "endpoint": "http://127.0.0.1:9020", "s3Region": "us-east-1" } + }, + "region2": { + "postgres": { + "connectionUri": "postgresql://speckle:speckle@127.0.0.1:5402/speckle2_test", + "privateConnectionUri": "postgresql://speckle:speckle@postgres-region2:5432/speckle2_test" + }, + "blobStorage": { + "accessKey": "minioadmin", + "secretKey": "minioadmin", + "bucket": "test-speckle-server", + "createBucketIfNotExists": true, + "endpoint": "http://127.0.0.1:9040", + "s3Region": "us-east-1" + } } } } From 88bafb233ad33b562366409df94bb9b9df99ede3 Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Wed, 22 Jan 2025 17:12:41 +0000 Subject: [PATCH 05/56] fix(regions): repair multiregion test setup --- .../modules/multiregion/utils/dbSelector.ts | 7 ++++- .../modules/workspaces/domain/operations.ts | 1 + .../workspaces/graph/resolvers/regions.ts | 2 ++ .../workspaces/repositories/regions.ts | 26 ++++++++++++++++++- .../modules/workspaces/services/regions.ts | 5 ++++ 5 files changed, 39 insertions(+), 2 deletions(-) diff --git a/packages/server/modules/multiregion/utils/dbSelector.ts b/packages/server/modules/multiregion/utils/dbSelector.ts index ae6b210e8..a01c5af9d 100644 --- a/packages/server/modules/multiregion/utils/dbSelector.ts +++ b/packages/server/modules/multiregion/utils/dbSelector.ts @@ -215,7 +215,12 @@ const setUpUserReplication = async ({ info: { pubName, regionName } } ) - if (!err.message.includes('already exists')) throw err + if ( + !['already exists', 'violates unique constraint'].some((message) => + err.message.includes(message) + ) + ) + throw err } const fromUrl = new URL( diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index 223bb6789..2dfb5ab37 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -352,6 +352,7 @@ export type UpdateProjectRegion = (params: { regionKey: string }) => Promise +export type CopyWorkspace = (params: { workspaceId: string }) => Promise export type CopyProjects = (params: { projectIds: string[] }) => Promise export type CopyProjectModels = (params: { projectIds: string[] diff --git a/packages/server/modules/workspaces/graph/resolvers/regions.ts b/packages/server/modules/workspaces/graph/resolvers/regions.ts index 80ae195c4..453738ced 100644 --- a/packages/server/modules/workspaces/graph/resolvers/regions.ts +++ b/packages/server/modules/workspaces/graph/resolvers/regions.ts @@ -9,6 +9,7 @@ import { copyProjectModelsFactory, copyProjectsFactory, copyProjectVersionsFactory, + copyWorkspaceFactory, getDefaultRegionFactory, upsertRegionAssignmentFactory } from '@/modules/workspaces/repositories/regions' @@ -83,6 +84,7 @@ export default { getWorkspacePlan: getWorkspacePlanFactory({ db }) }) }), + copyWorkspace: copyWorkspaceFactory({ sourceDb, targetDb }), copyProjects: copyProjectsFactory({ sourceDb, targetDb }), copyProjectModels: copyProjectModelsFactory({ sourceDb, targetDb }), copyProjectVersions: copyProjectVersionsFactory({ sourceDb, targetDb }) diff --git a/packages/server/modules/workspaces/repositories/regions.ts b/packages/server/modules/workspaces/repositories/regions.ts index 962330ba4..57b375bba 100644 --- a/packages/server/modules/workspaces/repositories/regions.ts +++ b/packages/server/modules/workspaces/repositories/regions.ts @@ -23,10 +23,16 @@ import { CopyProjectModels, CopyProjects, CopyProjectVersions, + CopyWorkspace, GetDefaultRegion, UpsertRegionAssignment } from '@/modules/workspaces/domain/operations' -import { WorkspaceRegionAssignment } from '@/modules/workspacesCore/domain/types' +import { WorkspaceNotFoundError } from '@/modules/workspaces/errors/workspace' +import { + Workspace, + WorkspaceRegionAssignment +} from '@/modules/workspacesCore/domain/types' +import { Workspaces } from '@/modules/workspacesCore/helpers/db' import { Knex } from 'knex' export const WorkspaceRegions = buildTableHelper('workspace_regions', [ @@ -35,6 +41,7 @@ export const WorkspaceRegions = buildTableHelper('workspace_regions', [ ]) const tables = { + workspaces: (db: Knex) => db(Workspaces.name), workspaceRegions: (db: Knex) => db(WorkspaceRegions.name), regions: (db: Knex) => db(Regions.name), projects: (db: Knex) => db(Streams.name), @@ -73,6 +80,23 @@ export const getDefaultRegionFactory = return row } +export const copyWorkspaceFactory = + (deps: { sourceDb: Knex; targetDb: Knex }): CopyWorkspace => + async ({ workspaceId }) => { + const workspace = await tables + .workspaces(deps.sourceDb) + .select('*') + .where({ id: workspaceId }) + + if (!workspace) { + throw new WorkspaceNotFoundError() + } + + await tables.workspaces(deps.targetDb).insert(workspace) + + return workspaceId + } + export const copyProjectsFactory = (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjects => async ({ projectIds }) => { diff --git a/packages/server/modules/workspaces/services/regions.ts b/packages/server/modules/workspaces/services/regions.ts index e673fc522..0fc132393 100644 --- a/packages/server/modules/workspaces/services/regions.ts +++ b/packages/server/modules/workspaces/services/regions.ts @@ -8,6 +8,7 @@ import { CopyProjectModels, CopyProjects, CopyProjectVersions, + CopyWorkspace, GetAvailableRegions, GetDefaultRegion, GetWorkspace, @@ -86,6 +87,7 @@ export const updateProjectRegionFactory = countProjectModels: GetStreamBranchCount countProjectVersions: GetStreamCommitCount getAvailableRegions: GetAvailableRegions + copyWorkspace: CopyWorkspace copyProjects: CopyProjects copyProjectModels: CopyProjectModels copyProjectVersions: CopyProjectVersions @@ -120,6 +122,9 @@ export const updateProjectRegionFactory = ) } + // Move workspace + await deps.copyWorkspace({ workspaceId: project.workspaceId }) + // Move commits const projectIds = await deps.copyProjects({ projectIds: [projectId] }) const modelIds = await deps.copyProjectModels({ projectIds }) From 1fb3b43ffa7369775cf202586bee385ac466f2e4 Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Wed, 22 Jan 2025 17:31:45 +0000 Subject: [PATCH 06/56] chore(regions): appease ts --- packages/server/modules/multiregion/utils/dbSelector.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/server/modules/multiregion/utils/dbSelector.ts b/packages/server/modules/multiregion/utils/dbSelector.ts index a01c5af9d..3746e91b6 100644 --- a/packages/server/modules/multiregion/utils/dbSelector.ts +++ b/packages/server/modules/multiregion/utils/dbSelector.ts @@ -206,7 +206,7 @@ const setUpUserReplication = async ({ try { await from.public.raw(`CREATE PUBLICATION ${pubName} FOR TABLE users;`) } catch (err) { - if (!(err instanceof Error)) + if (!(err instanceof Error)) { throw new DatabaseError( 'Could not create publication {pubName} when setting up user replication for region {regionName}', from.public, @@ -215,9 +215,13 @@ const setUpUserReplication = async ({ info: { pubName, regionName } } ) + } + + const errorMessage = err.message + if ( !['already exists', 'violates unique constraint'].some((message) => - err.message.includes(message) + errorMessage.includes(message) ) ) throw err From 54b61bfec1bc020d1f3632c1eb6244b1665a23db Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Wed, 22 Jan 2025 22:23:28 +0000 Subject: [PATCH 07/56] chore(multiregion): update test multiregion config --- .circleci/multiregion.test-ci.json | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.circleci/multiregion.test-ci.json b/.circleci/multiregion.test-ci.json index 3d5a9ec1c..78619c2af 100644 --- a/.circleci/multiregion.test-ci.json +++ b/.circleci/multiregion.test-ci.json @@ -25,6 +25,19 @@ "endpoint": "http://127.0.0.1:9020", "s3Region": "us-east-1" } + }, + "region2": { + "postgres": { + "connectionUri": "postgresql://speckle:speckle@127.0.0.1:5434/speckle2_test" + }, + "blobStorage": { + "accessKey": "minioadmin", + "secretKey": "minioadmin", + "bucket": "speckle-server", + "createBucketIfNotExists": true, + "endpoint": "http://127.0.0.1:9040", + "s3Region": "us-east-1" + } } } } From de8d78a594cf87d3f3cc15256e6e4e3d1c2cf02a Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Wed, 22 Jan 2025 23:05:17 +0000 Subject: [PATCH 08/56] chore(multiregion): fix test docker config and test --- .circleci/config.yml | 8 ++ .../workspaces/repositories/regions.ts | 4 +- .../tests/integration/projects.graph.spec.ts | 90 ++++++++++++++++--- 3 files changed, 90 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 47030bcee..8ffa834c2 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -598,10 +598,18 @@ jobs: POSTGRES_PASSWORD: speckle POSTGRES_USER: speckle command: -c 'max_connections=1000' -c 'port=5433' -c 'wal_level=logical' + - image: 'speckle/speckle-postgres' + environment: + POSTGRES_DB: speckle2_test + POSTGRES_PASSWORD: speckle + POSTGRES_USER: speckle + command: -c 'max_connections=1000' -c 'port=5434' -c 'wal_level=logical' - image: 'minio/minio' command: server /data --console-address ":9001" --address "0.0.0.0:9000" - image: 'minio/minio' command: server /data --console-address ":9021" --address "0.0.0.0:9020" + - image: 'minio/minio' + command: server /data --console-address ":9041" --address "0.0.0.0:9040" environment: # Same as test-server: NODE_ENV: test diff --git a/packages/server/modules/workspaces/repositories/regions.ts b/packages/server/modules/workspaces/repositories/regions.ts index 57b375bba..1fdca9848 100644 --- a/packages/server/modules/workspaces/repositories/regions.ts +++ b/packages/server/modules/workspaces/repositories/regions.ts @@ -207,10 +207,10 @@ export const copyProjectVersionsFactory = for await (const versions of executeBatchedSelect(selectVersions)) { for (const version of versions) { - const { commitId, ...commit } = version + const { commitId, streamId, ...commit } = version // Store copied version id - copiedVersionIds[projectId].push(commitId) + copiedVersionIds[streamId].push(commitId) // Copy `commits` row to target db await tables.versions(deps.targetDb).insert(commit).onConflict().ignore() diff --git a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts index 60c749375..6d219beb9 100644 --- a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts @@ -1,7 +1,7 @@ import { db } from '@/db/knex' import { AllScopes } from '@/modules/core/helpers/mainConstants' import { createRandomEmail } from '@/modules/core/helpers/testHelpers' -import { StreamRecord } from '@/modules/core/helpers/types' +import { BranchRecord, CommitRecord, StreamRecord } from '@/modules/core/helpers/types' import { grantStreamPermissionsFactory } from '@/modules/core/repositories/streams' import { getDb } from '@/modules/multiregion/utils/dbSelector' import { @@ -28,6 +28,12 @@ import { TestApolloServer } from '@/test/graphqlHelper' import { beforeEachContext } from '@/test/hooks' +import { BasicTestBranch, createTestBranch } from '@/test/speckle-helpers/branchHelper' +import { + BasicTestCommit, + createTestCommit, + createTestObject +} from '@/test/speckle-helpers/commitHelper' import { isMultiRegionTestMode, waitForRegionUser @@ -37,6 +43,7 @@ import { Roles } from '@speckle/shared' import { expect } from 'chai' import cryptoRandomString from 'crypto-random-string' import { Knex } from 'knex' +import { SetOptional } from 'type-fest' const grantStreamPermissions = grantStreamPermissionsFactory({ db }) @@ -294,10 +301,9 @@ isMultiRegionTestMode() email: createRandomEmail() } - const testWorkspace: BasicTestWorkspace = { + const testWorkspace: SetOptional = { id: '', ownerId: '', - slug: '', name: 'Unlimited Workspace' } @@ -308,12 +314,34 @@ isMultiRegionTestMode() isPublic: true } + const testModel: BasicTestBranch = { + id: '', + name: cryptoRandomString({ length: 8 }), + streamId: '', + authorId: '' + } + + const testVersion: BasicTestCommit = { + id: '', + objectId: '', + streamId: '', + authorId: '' + } + let apollo: TestApolloServer - let regionDb: Knex + let targetRegionDb: Knex before(async () => { await createTestUser(adminUser) await waitForRegionUser(adminUser) + + apollo = await testApolloServer({ authUserId: adminUser.id }) + targetRegionDb = await getDb({ regionKey: regionKey2 }) + }) + + beforeEach(async () => { + delete testWorkspace.slug + await createTestWorkspace(testWorkspace, adminUser, { regionKey: regionKey1, addPlan: { @@ -324,12 +352,20 @@ isMultiRegionTestMode() testProject.workspaceId = testWorkspace.id - apollo = await testApolloServer({ authUserId: adminUser.id }) - regionDb = await getDb({ regionKey: regionKey2 }) - }) - - beforeEach(async () => { await createTestStream(testProject, adminUser) + await createTestBranch({ + stream: testProject, + branch: testModel, + owner: adminUser + }) + + testVersion.branchName = testModel.name + testVersion.objectId = await createTestObject({ projectId: testProject.id }) + + await createTestCommit(testVersion, { + owner: adminUser, + stream: testProject + }) }) it('moves project record to target regional db', async () => { @@ -340,7 +376,7 @@ isMultiRegionTestMode() expect(res).to.not.haveGraphQLErrors() - const project = await regionDb + const project = await targetRegionDb .table('streams') .select('*') .where({ id: testProject.id }) @@ -348,5 +384,39 @@ isMultiRegionTestMode() expect(project).to.not.be.undefined }) + + it('moves project models to target regional db', async () => { + const res = await apollo.execute(UpdateProjectRegionDocument, { + projectId: testProject.id, + regionKey: regionKey2 + }) + + expect(res).to.not.haveGraphQLErrors() + + const branch = await targetRegionDb + .table('branches') + .select('*') + .where({ id: testModel.id }) + .first() + + expect(branch).to.not.be.undefined + }) + + it('moves project model versions to target regional db', async () => { + const res = await apollo.execute(UpdateProjectRegionDocument, { + projectId: testProject.id, + regionKey: regionKey2 + }) + + expect(res).to.not.haveGraphQLErrors() + + const version = await targetRegionDb + .table('commits') + .select('*') + .where({ id: testVersion.id }) + .first() + + expect(version).to.not.be.undefined + }) }) : void 0 From ca850399b70ed08fbf32f15252770bb7d4f0cef1 Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Thu, 23 Jan 2025 11:52:35 +0000 Subject: [PATCH 09/56] chore(multiregion): use transaction --- .circleci/config.yml | 2 +- .../server/modules/workspaces/graph/resolvers/regions.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8ffa834c2..1ea6bcbd1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -615,7 +615,7 @@ jobs: NODE_ENV: test DATABASE_URL: 'postgres://speckle:speckle@127.0.0.1:5432/speckle2_test' PGDATABASE: speckle2_test - POSTGRES_MAX_CONNECTIONS_SERVER: 20 + POSTGRES_MAX_CONNECTIONS_SERVER: 50 PGUSER: speckle SESSION_SECRET: 'keyboard cat' STRATEGY_LOCAL: 'true' diff --git a/packages/server/modules/workspaces/graph/resolvers/regions.ts b/packages/server/modules/workspaces/graph/resolvers/regions.ts index 453738ced..b58c27270 100644 --- a/packages/server/modules/workspaces/graph/resolvers/regions.ts +++ b/packages/server/modules/workspaces/graph/resolvers/regions.ts @@ -26,6 +26,7 @@ import { Roles } from '@speckle/shared' import { getProjectFactory } from '@/modules/core/repositories/streams' import { getStreamBranchCountFactory } from '@/modules/core/repositories/branches' import { getStreamCommitCountFactory } from '@/modules/core/repositories/commits' +import { withTransaction } from '@/modules/shared/helpers/dbHelper' export default { Workspace: { @@ -72,7 +73,7 @@ export default { ) const sourceDb = await getProjectDbClient({ projectId: args.projectId }) - const targetDb = await getDb({ regionKey: args.regionKey }) + const targetDb = await (await getDb({ regionKey: args.regionKey })).transaction() const updateProjectRegion = updateProjectRegionFactory({ getProject: getProjectFactory({ db: sourceDb }), @@ -90,7 +91,7 @@ export default { copyProjectVersions: copyProjectVersionsFactory({ sourceDb, targetDb }) }) - return await updateProjectRegion(args) + return await withTransaction(updateProjectRegion(args), targetDb) } } } as Resolvers From 201fe2e3ec7617f1fdb64d3cb1fc01a2f90d9290 Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Thu, 23 Jan 2025 16:57:43 +0000 Subject: [PATCH 10/56] chore(multiregion): maybe this will work --- .../modules/auth/tests/apps.graphql.spec.js | 6 ------ .../server/modules/auth/tests/auth.spec.js | 6 ------ .../server/modules/stats/tests/stats.spec.ts | 9 +-------- .../modules/webhooks/tests/webhooks.spec.js | 20 ++----------------- 4 files changed, 3 insertions(+), 38 deletions(-) diff --git a/packages/server/modules/auth/tests/apps.graphql.spec.js b/packages/server/modules/auth/tests/apps.graphql.spec.js index 6f651fde0..4e90f06f8 100644 --- a/packages/server/modules/auth/tests/apps.graphql.spec.js +++ b/packages/server/modules/auth/tests/apps.graphql.spec.js @@ -63,7 +63,6 @@ const { getServerInfoFactory } = require('@/modules/core/repositories/server') const { getEventBus } = require('@/modules/shared/services/eventBus') let sendRequest -let server const createAppToken = createAppTokenFactory({ storeApiToken: storeApiTokenFactory({ db }), @@ -128,7 +127,6 @@ describe('GraphQL @apps-api', () => { before(async () => { const ctx = await beforeEachContext() - server = ctx.server ;({ sendRequest } = await initializeTestServer(ctx)) testUser = { name: 'Dimitrie Stefanescu', @@ -157,10 +155,6 @@ describe('GraphQL @apps-api', () => { ])}` }) - after(async () => { - await server.close() - }) - let testAppId let testApp diff --git a/packages/server/modules/auth/tests/auth.spec.js b/packages/server/modules/auth/tests/auth.spec.js index 45f75cd95..67888198b 100644 --- a/packages/server/modules/auth/tests/auth.spec.js +++ b/packages/server/modules/auth/tests/auth.spec.js @@ -138,7 +138,6 @@ const expect = chai.expect let app let sendRequest -let server describe('Auth @auth', () => { describe('Local authN & authZ (token endpoints)', () => { @@ -160,7 +159,6 @@ describe('Auth @auth', () => { before(async () => { const ctx = await beforeEachContext() - server = ctx.server app = ctx.app ;({ sendRequest } = await initializeTestServer(ctx)) @@ -173,10 +171,6 @@ describe('Auth @auth', () => { ) }) - after(async () => { - await server.close() - }) - it('Should register a new user (speckle frontend)', async () => { await request(app) .post('/auth/local/register?challenge=test') diff --git a/packages/server/modules/stats/tests/stats.spec.ts b/packages/server/modules/stats/tests/stats.spec.ts index f0fd0c17b..757f8df63 100644 --- a/packages/server/modules/stats/tests/stats.spec.ts +++ b/packages/server/modules/stats/tests/stats.spec.ts @@ -8,7 +8,6 @@ import { getTotalUserCountFactory } from '@/modules/stats/repositories/index' import { Scopes } from '@speckle/shared' -import { Server } from 'node:http' import { db } from '@/db/knex' import { createCommitByBranchIdFactory, @@ -196,8 +195,7 @@ describe('Server stats services @stats-services', function () { }) describe('Server stats api @stats-api', function () { - let server: Server, - sendRequest: Awaited>['sendRequest'] + let sendRequest: Awaited>['sendRequest'] const adminUser = { name: 'Dimitrie', @@ -235,7 +233,6 @@ describe('Server stats api @stats-api', function () { before(async function () { this.timeout(15000) const ctx = await beforeEachContext() - server = ctx.server ;({ sendRequest } = await initializeTestServer(ctx)) adminUser.id = await createUser(adminUser) @@ -265,10 +262,6 @@ describe('Server stats api @stats-api', function () { await seedDb(params) }) - after(async function () { - await server.close() - }) - it('Should not get stats if user is not admin', async () => { const res = await sendRequest(adminUser.badToken, { query: fullQuery }) expect(res.body.errors).to.exist diff --git a/packages/server/modules/webhooks/tests/webhooks.spec.js b/packages/server/modules/webhooks/tests/webhooks.spec.js index 0c8001a16..0befc5da0 100644 --- a/packages/server/modules/webhooks/tests/webhooks.spec.js +++ b/packages/server/modules/webhooks/tests/webhooks.spec.js @@ -2,11 +2,7 @@ const expect = require('chai').expect const assert = require('assert') -const { - beforeEachContext, - initializeTestServer, - truncateTables -} = require('@/test/hooks') +const { beforeEachContext, initializeTestServer } = require('@/test/hooks') const { noErrors } = require('@/test/helpers') const { Scopes, Roles } = require('@speckle/shared') const { @@ -26,7 +22,6 @@ const { deleteWebhookFactory, dispatchStreamEventFactory } = require('@/modules/webhooks/services/webhooks') -const { Users, Streams } = require('@/modules/core/dbSchema') const { getStreamFactory, createStreamFactory, @@ -166,7 +161,7 @@ const createPersonalAccessToken = createPersonalAccessTokenFactory({ describe('Webhooks @webhooks', () => { const getWebhook = getWebhookByIdFactory({ db }) - let server, sendRequest + let sendRequest const userOne = { name: 'User', @@ -191,7 +186,6 @@ describe('Webhooks @webhooks', () => { before(async () => { const ctx = await beforeEachContext() - server = ctx.server ;({ sendRequest } = await initializeTestServer(ctx)) userOne.id = await createUser(userOne) @@ -201,16 +195,6 @@ describe('Webhooks @webhooks', () => { webhookOne.streamId = streamOne.id }) - after(async () => { - await truncateTables([ - Users.name, - Streams.name, - 'webhooks_config', - 'webhooks_events' - ]) - await server.close() - }) - describe('Create, Read, Update, Delete Webhooks', () => { it('Should create a webhook', async () => { webhookOne.id = await createWebhookFactory({ From 08b53ccba305bde10789a753bc11d4170f9a98fe Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Fri, 24 Jan 2025 12:26:52 +0000 Subject: [PATCH 11/56] fix(multiregion): drop subs synchronously --- packages/server/test/hooks.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/server/test/hooks.ts b/packages/server/test/hooks.ts index 8bc17fac2..c86fd4cf8 100644 --- a/packages/server/test/hooks.ts +++ b/packages/server/test/hooks.ts @@ -210,8 +210,10 @@ export const resetPubSubFactory = (deps: { db: Knex }) => async () => { } // Drop all subs - // (concurrently, cause it seems possible and we have those delays there) - await Promise.all(subscriptions.rows.map(dropSubs)) + for (const subscription of subscriptions.rows) { + await dropSubs(subscription) + await wait(1000) + } // Drop all pubs for (const pub of publications.rows) { From 8edde647dfad341d34908e3c858568a9d1a3a392 Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Fri, 24 Jan 2025 13:23:00 +0000 Subject: [PATCH 12/56] chore(multiregion): desperate test logs --- packages/server/test/hooks.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/server/test/hooks.ts b/packages/server/test/hooks.ts index c86fd4cf8..ed299ef47 100644 --- a/packages/server/test/hooks.ts +++ b/packages/server/test/hooks.ts @@ -209,8 +209,12 @@ export const resetPubSubFactory = (deps: { db: Knex }) => async () => { ) } + console.log(subscriptions.rows.length) + console.log(subscriptions.rows) + // Drop all subs for (const subscription of subscriptions.rows) { + console.log(JSON.stringify(subscription, null, 2)) await dropSubs(subscription) await wait(1000) } From afc06d78d749265033b7b4274c282c90c63a146d Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Fri, 24 Jan 2025 14:01:01 +0000 Subject: [PATCH 13/56] chore(multiregion): somehow that worked? --- packages/server/test/hooks.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/server/test/hooks.ts b/packages/server/test/hooks.ts index ed299ef47..fe6ac34c9 100644 --- a/packages/server/test/hooks.ts +++ b/packages/server/test/hooks.ts @@ -209,14 +209,10 @@ export const resetPubSubFactory = (deps: { db: Knex }) => async () => { ) } - console.log(subscriptions.rows.length) - console.log(subscriptions.rows) - // Drop all subs for (const subscription of subscriptions.rows) { - console.log(JSON.stringify(subscription, null, 2)) await dropSubs(subscription) - await wait(1000) + await wait(500) } // Drop all pubs From 6d6f800fbf82ac25a3d690aa349a75abdf2eddb2 Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Fri, 24 Jan 2025 14:34:12 +0000 Subject: [PATCH 14/56] chore(multiregion): add load-bearing log statement --- packages/server/test/hooks.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/server/test/hooks.ts b/packages/server/test/hooks.ts index fe6ac34c9..036814827 100644 --- a/packages/server/test/hooks.ts +++ b/packages/server/test/hooks.ts @@ -211,6 +211,8 @@ export const resetPubSubFactory = (deps: { db: Knex }) => async () => { // Drop all subs for (const subscription of subscriptions.rows) { + // If we do not log something here, CircleCI may kill the job while we wait all `dropSubs` calls to finish. + console.log(`Dropping subscription ${subscription.subname}`) await dropSubs(subscription) await wait(500) } From a27b97ab13a4e850be2e97b0cf724843847ad8dc Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Fri, 24 Jan 2025 14:49:41 +0000 Subject: [PATCH 15/56] chore(multiregion): move services --- .../workspaces/graph/resolvers/regions.ts | 14 +- .../workspaces/repositories/projectRegions.ts | 216 +++++++++++++++++ .../workspaces/repositories/regions.ts | 218 +----------------- .../workspaces/services/projectRegions.ts | 86 +++++++ .../modules/workspaces/services/regions.ts | 87 +------ 5 files changed, 314 insertions(+), 307 deletions(-) create mode 100644 packages/server/modules/workspaces/repositories/projectRegions.ts create mode 100644 packages/server/modules/workspaces/services/projectRegions.ts diff --git a/packages/server/modules/workspaces/graph/resolvers/regions.ts b/packages/server/modules/workspaces/graph/resolvers/regions.ts index b58c27270..57a45aa21 100644 --- a/packages/server/modules/workspaces/graph/resolvers/regions.ts +++ b/packages/server/modules/workspaces/graph/resolvers/regions.ts @@ -6,22 +6,24 @@ import { getDb, getProjectDbClient } from '@/modules/multiregion/utils/dbSelecto import { getRegionsFactory } from '@/modules/multiregion/repositories' import { authorizeResolver } from '@/modules/shared' import { - copyProjectModelsFactory, - copyProjectsFactory, - copyProjectVersionsFactory, - copyWorkspaceFactory, getDefaultRegionFactory, upsertRegionAssignmentFactory } from '@/modules/workspaces/repositories/regions' +import { + copyProjectModelsFactory, + copyProjectsFactory, + copyProjectVersionsFactory, + copyWorkspaceFactory +} from '@/modules/workspaces/repositories/projectRegions' import { getWorkspaceFactory, upsertWorkspaceFactory } from '@/modules/workspaces/repositories/workspaces' import { assignWorkspaceRegionFactory, - getAvailableRegionsFactory, - updateProjectRegionFactory + getAvailableRegionsFactory } from '@/modules/workspaces/services/regions' +import { updateProjectRegionFactory } from '@/modules/workspaces/services/projectRegions' import { Roles } from '@speckle/shared' import { getProjectFactory } from '@/modules/core/repositories/streams' import { getStreamBranchCountFactory } from '@/modules/core/repositories/branches' diff --git a/packages/server/modules/workspaces/repositories/projectRegions.ts b/packages/server/modules/workspaces/repositories/projectRegions.ts new file mode 100644 index 000000000..94da98d14 --- /dev/null +++ b/packages/server/modules/workspaces/repositories/projectRegions.ts @@ -0,0 +1,216 @@ +import { + BranchCommits, + Branches, + Commits, + StreamCommits, + StreamFavorites, + Streams, + StreamsMeta +} from '@/modules/core/dbSchema' +import { Branch } from '@/modules/core/domain/branches/types' +import { Commit } from '@/modules/core/domain/commits/types' +import { Stream } from '@/modules/core/domain/streams/types' +import { + BranchCommitRecord, + StreamCommitRecord, + StreamFavoriteRecord +} from '@/modules/core/helpers/types' +import { executeBatchedSelect } from '@/modules/shared/helpers/dbHelper' +import { + CopyProjectModels, + CopyProjects, + CopyProjectVersions, + CopyWorkspace +} from '@/modules/workspaces/domain/operations' +import { WorkspaceNotFoundError } from '@/modules/workspaces/errors/workspace' +import { Knex } from 'knex' +import { Workspace } from '@/modules/workspacesCore/domain/types' +import { Workspaces } from '@/modules/workspacesCore/helpers/db' + +const tables = { + workspaces: (db: Knex) => db(Workspaces.name), + projects: (db: Knex) => db(Streams.name), + models: (db: Knex) => db(Branches.name), + versions: (db: Knex) => db(Commits.name), + branchCommits: (db: Knex) => db(BranchCommits.name), + streamCommits: (db: Knex) => db(StreamCommits.name), + streamFavorites: (db: Knex) => db(StreamFavorites.name), + streamsMeta: (db: Knex) => db(StreamsMeta.name) +} + +export const copyWorkspaceFactory = + (deps: { sourceDb: Knex; targetDb: Knex }): CopyWorkspace => + async ({ workspaceId }) => { + const workspace = await tables + .workspaces(deps.sourceDb) + .select('*') + .where({ id: workspaceId }) + + if (!workspace) { + throw new WorkspaceNotFoundError() + } + + await tables.workspaces(deps.targetDb).insert(workspace) + + return workspaceId + } + +export const copyProjectsFactory = + (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjects => + async ({ projectIds }) => { + const selectProjects = tables + .projects(deps.sourceDb) + .select('*') + .whereIn(Streams.col.id, projectIds) + const copiedProjectIds: string[] = [] + + // Copy project record + for await (const projects of executeBatchedSelect(selectProjects)) { + for (const project of projects) { + // Store copied project id + copiedProjectIds.push(project.id) + + // Copy `streams` row to target db + await tables.projects(deps.targetDb).insert(project).onConflict().ignore() + } + + const projectIds = projects.map((project) => project.id) + + // Fetch `stream_favorites` rows for projects in batch + const selectStreamFavorites = tables + .streamFavorites(deps.sourceDb) + .select('*') + .whereIn(StreamFavorites.col.streamId, projectIds) + + for await (const streamFavorites of executeBatchedSelect(selectStreamFavorites)) { + for (const streamFavorite of streamFavorites) { + // Copy `stream_favorites` row to target db + await tables + .streamFavorites(deps.targetDb) + .insert(streamFavorite) + .onConflict() + .ignore() + } + } + + // Fetch `streams_meta` rows for projects in batch + const selectStreamsMetadata = tables + .streamsMeta(deps.sourceDb) + .select('*') + .whereIn(StreamsMeta.col.streamId, projectIds) + + for await (const streamsMetadataBatch of executeBatchedSelect( + selectStreamsMetadata + )) { + for (const streamMetadata of streamsMetadataBatch) { + // Copy `streams_meta` row to target db + await tables + .streamsMeta(deps.targetDb) + .insert(streamMetadata) + .onConflict() + .ignore() + } + } + } + + return copiedProjectIds + } + +export const copyProjectModelsFactory = + (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectModels => + async ({ projectIds }) => { + const copiedModelIds: Record = projectIds.reduce( + (result, id) => ({ ...result, [id]: [] }), + {} + ) + + for (const projectId of projectIds) { + const selectModels = tables + .models(deps.sourceDb) + .select('*') + .where({ streamId: projectId }) + + for await (const models of executeBatchedSelect(selectModels)) { + for (const model of models) { + // Store copied model ids + copiedModelIds[projectId].push(model.id) + + // Copy `branches` row to target db + await tables.models(deps.targetDb).insert(model).onConflict().ignore() + } + } + } + + return copiedModelIds + } + +export const copyProjectVersionsFactory = + (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectVersions => + async ({ projectIds }) => { + const copiedVersionIds: Record = projectIds.reduce( + (result, id) => ({ ...result, [id]: [] }), + {} + ) + + for (const projectId of projectIds) { + const selectVersions = tables + .streamCommits(deps.sourceDb) + .select('*') + .join( + Commits.name, + Commits.col.id, + StreamCommits.col.commitId + ) + .where({ streamId: projectId }) + + for await (const versions of executeBatchedSelect(selectVersions)) { + for (const version of versions) { + const { commitId, streamId, ...commit } = version + + // Store copied version id + copiedVersionIds[streamId].push(commitId) + + // Copy `commits` row to target db + await tables.versions(deps.targetDb).insert(commit).onConflict().ignore() + } + + const commitIds = versions.map((version) => version.commitId) + + // Fetch `branch_commits` rows for versions in batch + const selectBranchCommits = tables + .branchCommits(deps.sourceDb) + .select('*') + .whereIn(BranchCommits.col.commitId, commitIds) + + for await (const branchCommits of executeBatchedSelect(selectBranchCommits)) { + for (const branchCommit of branchCommits) { + // Copy `branch_commits` row to target db + await tables + .branchCommits(deps.targetDb) + .insert(branchCommit) + .onConflict() + .ignore() + } + } + + // Fetch `stream_commits` rows for versions in batch + const selectStreamCommits = tables + .streamCommits(deps.sourceDb) + .select('*') + .whereIn(StreamCommits.col.commitId, commitIds) + + for await (const streamCommits of executeBatchedSelect(selectStreamCommits)) { + for (const streamCommit of streamCommits) { + // Copy `stream_commits` row to target db + await tables + .streamCommits(deps.targetDb) + .insert(streamCommit) + .onConflict() + .ignore() + } + } + } + } + + return copiedVersionIds + } diff --git a/packages/server/modules/workspaces/repositories/regions.ts b/packages/server/modules/workspaces/repositories/regions.ts index 1fdca9848..dc9563829 100644 --- a/packages/server/modules/workspaces/repositories/regions.ts +++ b/packages/server/modules/workspaces/repositories/regions.ts @@ -1,38 +1,11 @@ -import { - BranchCommits, - Branches, - buildTableHelper, - Commits, - StreamCommits, - StreamFavorites, - Streams, - StreamsMeta -} from '@/modules/core/dbSchema' -import { Branch } from '@/modules/core/domain/branches/types' -import { Commit } from '@/modules/core/domain/commits/types' -import { Stream } from '@/modules/core/domain/streams/types' -import { - BranchCommitRecord, - StreamCommitRecord, - StreamFavoriteRecord -} from '@/modules/core/helpers/types' +import { buildTableHelper } from '@/modules/core/dbSchema' import { RegionRecord } from '@/modules/multiregion/helpers/types' import { Regions } from '@/modules/multiregion/repositories' -import { executeBatchedSelect } from '@/modules/shared/helpers/dbHelper' import { - CopyProjectModels, - CopyProjects, - CopyProjectVersions, - CopyWorkspace, GetDefaultRegion, UpsertRegionAssignment } from '@/modules/workspaces/domain/operations' -import { WorkspaceNotFoundError } from '@/modules/workspaces/errors/workspace' -import { - Workspace, - WorkspaceRegionAssignment -} from '@/modules/workspacesCore/domain/types' -import { Workspaces } from '@/modules/workspacesCore/helpers/db' +import { WorkspaceRegionAssignment } from '@/modules/workspacesCore/domain/types' import { Knex } from 'knex' export const WorkspaceRegions = buildTableHelper('workspace_regions', [ @@ -41,16 +14,8 @@ export const WorkspaceRegions = buildTableHelper('workspace_regions', [ ]) const tables = { - workspaces: (db: Knex) => db(Workspaces.name), - workspaceRegions: (db: Knex) => db(WorkspaceRegions.name), regions: (db: Knex) => db(Regions.name), - projects: (db: Knex) => db(Streams.name), - models: (db: Knex) => db(Branches.name), - versions: (db: Knex) => db(Commits.name), - branchCommits: (db: Knex) => db(BranchCommits.name), - streamCommits: (db: Knex) => db(StreamCommits.name), - streamFavorites: (db: Knex) => db(StreamFavorites.name), - streamsMeta: (db: Knex) => db(StreamsMeta.name) + workspaceRegions: (db: Knex) => db(WorkspaceRegions.name) } export const upsertRegionAssignmentFactory = @@ -79,180 +44,3 @@ export const getDefaultRegionFactory = return row } - -export const copyWorkspaceFactory = - (deps: { sourceDb: Knex; targetDb: Knex }): CopyWorkspace => - async ({ workspaceId }) => { - const workspace = await tables - .workspaces(deps.sourceDb) - .select('*') - .where({ id: workspaceId }) - - if (!workspace) { - throw new WorkspaceNotFoundError() - } - - await tables.workspaces(deps.targetDb).insert(workspace) - - return workspaceId - } - -export const copyProjectsFactory = - (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjects => - async ({ projectIds }) => { - const selectProjects = tables - .projects(deps.sourceDb) - .select('*') - .whereIn(Streams.col.id, projectIds) - const copiedProjectIds: string[] = [] - - // Copy project record - for await (const projects of executeBatchedSelect(selectProjects)) { - for (const project of projects) { - // Store copied project id - copiedProjectIds.push(project.id) - - // Copy `streams` row to target db - await tables.projects(deps.targetDb).insert(project).onConflict().ignore() - } - - const projectIds = projects.map((project) => project.id) - - // Fetch `stream_favorites` rows for projects in batch - const selectStreamFavorites = tables - .streamFavorites(deps.sourceDb) - .select('*') - .whereIn(StreamFavorites.col.streamId, projectIds) - - for await (const streamFavorites of executeBatchedSelect(selectStreamFavorites)) { - for (const streamFavorite of streamFavorites) { - // Copy `stream_favorites` row to target db - await tables - .streamFavorites(deps.targetDb) - .insert(streamFavorite) - .onConflict() - .ignore() - } - } - - // Fetch `streams_meta` rows for projects in batch - const selectStreamsMetadata = tables - .streamsMeta(deps.sourceDb) - .select('*') - .whereIn(StreamsMeta.col.streamId, projectIds) - - for await (const streamsMetadataBatch of executeBatchedSelect( - selectStreamsMetadata - )) { - for (const streamMetadata of streamsMetadataBatch) { - // Copy `streams_meta` row to target db - await tables - .streamsMeta(deps.targetDb) - .insert(streamMetadata) - .onConflict() - .ignore() - } - } - } - - return copiedProjectIds - } - -export const copyProjectModelsFactory = - (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectModels => - async ({ projectIds }) => { - const copiedModelIds: Record = projectIds.reduce( - (result, id) => ({ ...result, [id]: [] }), - {} - ) - - for (const projectId of projectIds) { - const selectModels = tables - .models(deps.sourceDb) - .select('*') - .where({ streamId: projectId }) - - for await (const models of executeBatchedSelect(selectModels)) { - for (const model of models) { - // Store copied model ids - copiedModelIds[projectId].push(model.id) - - // Copy `branches` row to target db - await tables.models(deps.targetDb).insert(model).onConflict().ignore() - } - } - } - - return copiedModelIds - } - -export const copyProjectVersionsFactory = - (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectVersions => - async ({ projectIds }) => { - const copiedVersionIds: Record = projectIds.reduce( - (result, id) => ({ ...result, [id]: [] }), - {} - ) - - for (const projectId of projectIds) { - const selectVersions = tables - .streamCommits(deps.sourceDb) - .select('*') - .join( - Commits.name, - Commits.col.id, - StreamCommits.col.commitId - ) - .where({ streamId: projectId }) - - for await (const versions of executeBatchedSelect(selectVersions)) { - for (const version of versions) { - const { commitId, streamId, ...commit } = version - - // Store copied version id - copiedVersionIds[streamId].push(commitId) - - // Copy `commits` row to target db - await tables.versions(deps.targetDb).insert(commit).onConflict().ignore() - } - - const commitIds = versions.map((version) => version.commitId) - - // Fetch `branch_commits` rows for versions in batch - const selectBranchCommits = tables - .branchCommits(deps.sourceDb) - .select('*') - .whereIn(BranchCommits.col.commitId, commitIds) - - for await (const branchCommits of executeBatchedSelect(selectBranchCommits)) { - for (const branchCommit of branchCommits) { - // Copy `branch_commits` row to target db - await tables - .branchCommits(deps.targetDb) - .insert(branchCommit) - .onConflict() - .ignore() - } - } - - // Fetch `stream_commits` rows for versions in batch - const selectStreamCommits = tables - .streamCommits(deps.sourceDb) - .select('*') - .whereIn(StreamCommits.col.commitId, commitIds) - - for await (const streamCommits of executeBatchedSelect(selectStreamCommits)) { - for (const streamCommit of streamCommits) { - // Copy `stream_commits` row to target db - await tables - .streamCommits(deps.targetDb) - .insert(streamCommit) - .onConflict() - .ignore() - } - } - } - } - - return copiedVersionIds - } diff --git a/packages/server/modules/workspaces/services/projectRegions.ts b/packages/server/modules/workspaces/services/projectRegions.ts new file mode 100644 index 000000000..561aee792 --- /dev/null +++ b/packages/server/modules/workspaces/services/projectRegions.ts @@ -0,0 +1,86 @@ +import { GetStreamBranchCount } from '@/modules/core/domain/branches/operations' +import { GetStreamCommitCount } from '@/modules/core/domain/commits/operations' +import { GetProject } from '@/modules/core/domain/projects/operations' +import { + CopyProjectModels, + CopyProjects, + CopyProjectVersions, + CopyWorkspace, + GetAvailableRegions, + UpdateProjectRegion +} from '@/modules/workspaces/domain/operations' +import { ProjectRegionAssignmentError } from '@/modules/workspaces/errors/regions' + +export const updateProjectRegionFactory = + (deps: { + getProject: GetProject + countProjectModels: GetStreamBranchCount + countProjectVersions: GetStreamCommitCount + getAvailableRegions: GetAvailableRegions + copyWorkspace: CopyWorkspace + copyProjects: CopyProjects + copyProjectModels: CopyProjectModels + copyProjectVersions: CopyProjectVersions + }): UpdateProjectRegion => + async (params) => { + const { projectId, regionKey } = params + + const project = await deps.getProject({ projectId }) + if (!project) { + throw new ProjectRegionAssignmentError('Project not found', { + info: { params } + }) + } + if (!project.workspaceId) { + throw new ProjectRegionAssignmentError('Project not a part of a workspace', { + info: { params } + }) + } + + const availableRegions = await deps.getAvailableRegions({ + workspaceId: project.workspaceId + }) + if (!availableRegions.find((region) => region.key === regionKey)) { + throw new ProjectRegionAssignmentError( + 'Specified region not available for workspace', + { + info: { + params, + workspaceId: project.workspaceId + } + } + ) + } + + // Move workspace + await deps.copyWorkspace({ workspaceId: project.workspaceId }) + + // Move commits + const projectIds = await deps.copyProjects({ projectIds: [projectId] }) + const modelIds = await deps.copyProjectModels({ projectIds }) + const versionIds = await deps.copyProjectVersions({ projectIds }) + + // TODO: Move objects + // TODO: Move automations + // TODO: Move comments + // TODO: Move file blobs + // TODO: Move webhooks + + // TODO: Validate state after move captures latest state of project + const sourceProjectModelCount = await deps.countProjectModels(projectId) + const sourceProjectVersionCount = await deps.countProjectVersions(projectId) + + const isReconciled = + modelIds[projectId].length === sourceProjectModelCount && + versionIds[projectId].length === sourceProjectVersionCount + + if (!isReconciled) { + // TODO: Move failed or source project added data while changing regions. Retry move. + throw new ProjectRegionAssignmentError( + 'Missing data from source project in target region copy after move.' + ) + } + + // TODO: Update project region in db + return { ...project, regionKey } + } diff --git a/packages/server/modules/workspaces/services/regions.ts b/packages/server/modules/workspaces/services/regions.ts index 0fc132393..88623c067 100644 --- a/packages/server/modules/workspaces/services/regions.ts +++ b/packages/server/modules/workspaces/services/regions.ts @@ -1,25 +1,14 @@ -import { GetStreamBranchCount } from '@/modules/core/domain/branches/operations' -import { GetStreamCommitCount } from '@/modules/core/domain/commits/operations' -import { GetProject } from '@/modules/core/domain/projects/operations' import { WorkspaceFeatureAccessFunction } from '@/modules/gatekeeper/domain/operations' import { GetRegions } from '@/modules/multiregion/domain/operations' import { AssignWorkspaceRegion, - CopyProjectModels, - CopyProjects, - CopyProjectVersions, - CopyWorkspace, GetAvailableRegions, GetDefaultRegion, GetWorkspace, - UpdateProjectRegion, UpsertRegionAssignment, UpsertWorkspace } from '@/modules/workspaces/domain/operations' -import { - ProjectRegionAssignmentError, - WorkspaceRegionAssignmentError -} from '@/modules/workspaces/errors/regions' +import { WorkspaceRegionAssignmentError } from '@/modules/workspaces/errors/regions' export const getAvailableRegionsFactory = (deps: { @@ -80,77 +69,3 @@ export const assignWorkspaceRegionFactory = // Copy workspace into region db await deps.insertRegionWorkspace({ workspace }) } - -export const updateProjectRegionFactory = - (deps: { - getProject: GetProject - countProjectModels: GetStreamBranchCount - countProjectVersions: GetStreamCommitCount - getAvailableRegions: GetAvailableRegions - copyWorkspace: CopyWorkspace - copyProjects: CopyProjects - copyProjectModels: CopyProjectModels - copyProjectVersions: CopyProjectVersions - }): UpdateProjectRegion => - async (params) => { - const { projectId, regionKey } = params - - const project = await deps.getProject({ projectId }) - if (!project) { - throw new ProjectRegionAssignmentError('Project not found', { - info: { params } - }) - } - if (!project.workspaceId) { - throw new ProjectRegionAssignmentError('Project not a part of a workspace', { - info: { params } - }) - } - - const availableRegions = await deps.getAvailableRegions({ - workspaceId: project.workspaceId - }) - if (!availableRegions.find((region) => region.key === regionKey)) { - throw new ProjectRegionAssignmentError( - 'Specified region not available for workspace', - { - info: { - params, - workspaceId: project.workspaceId - } - } - ) - } - - // Move workspace - await deps.copyWorkspace({ workspaceId: project.workspaceId }) - - // Move commits - const projectIds = await deps.copyProjects({ projectIds: [projectId] }) - const modelIds = await deps.copyProjectModels({ projectIds }) - const versionIds = await deps.copyProjectVersions({ projectIds }) - - // TODO: Move objects - // TODO: Move automations - // TODO: Move comments - // TODO: Move file blobs - // TODO: Move webhooks - - // TODO: Validate state after move captures latest state of project - const sourceProjectModelCount = await deps.countProjectModels(projectId) - const sourceProjectVersionCount = await deps.countProjectVersions(projectId) - - const isReconciled = - modelIds[projectId].length === sourceProjectModelCount && - versionIds[projectId].length === sourceProjectVersionCount - - if (!isReconciled) { - // TODO: Move failed or source project added data while changing regions. Retry move. - throw new ProjectRegionAssignmentError( - 'Missing data from source project in target region copy after move.' - ) - } - - // TODO: Update project region in db - return { ...project, regionKey } - } From 6a0fadcc82acbfd7567e3173415f0b25fcf13e8b Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Fri, 24 Jan 2025 15:03:52 +0000 Subject: [PATCH 16/56] fix(multiregion): test drop waits --- packages/server/test/hooks.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/server/test/hooks.ts b/packages/server/test/hooks.ts index 036814827..99a3cf402 100644 --- a/packages/server/test/hooks.ts +++ b/packages/server/test/hooks.ts @@ -22,8 +22,7 @@ import { MaybeAsync, MaybeNullOrUndefined, Nullable, - Optional, - wait + Optional } from '@speckle/shared' import * as mocha from 'mocha' import { @@ -199,22 +198,17 @@ export const resetPubSubFactory = (deps: { db: Knex }) => async () => { await deps.db.raw( `SELECT * FROM aiven_extras.pg_alter_subscription_disable('${info.subname}');` ) - await wait(500) await deps.db.raw( `SELECT * FROM aiven_extras.pg_drop_subscription('${info.subname}');` ) - await wait(1000) await deps.db.raw( `SELECT * FROM aiven_extras.dblink_slot_create_or_drop('${info.subconninfo}', '${info.subslotname}', 'drop');` ) } // Drop all subs - for (const subscription of subscriptions.rows) { - // If we do not log something here, CircleCI may kill the job while we wait all `dropSubs` calls to finish. - console.log(`Dropping subscription ${subscription.subname}`) - await dropSubs(subscription) - await wait(500) + for (const sub of subscriptions.rows) { + await dropSubs(sub) } // Drop all pubs From 0811916ef38c4d5e098590f7f5d76148f0819440 Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Mon, 3 Feb 2025 14:03:34 +0000 Subject: [PATCH 17/56] chore(regions): fix import --- packages/server/modules/workspaces/graph/resolvers/regions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/modules/workspaces/graph/resolvers/regions.ts b/packages/server/modules/workspaces/graph/resolvers/regions.ts index 57a45aa21..73770ec14 100644 --- a/packages/server/modules/workspaces/graph/resolvers/regions.ts +++ b/packages/server/modules/workspaces/graph/resolvers/regions.ts @@ -25,7 +25,7 @@ import { } from '@/modules/workspaces/services/regions' import { updateProjectRegionFactory } from '@/modules/workspaces/services/projectRegions' import { Roles } from '@speckle/shared' -import { getProjectFactory } from '@/modules/core/repositories/streams' +import { getProjectFactory } from '@/modules/core/repositories/projects' import { getStreamBranchCountFactory } from '@/modules/core/repositories/branches' import { getStreamCommitCountFactory } from '@/modules/core/repositories/commits' import { withTransaction } from '@/modules/shared/helpers/dbHelper' From b48721e85ab55d828bf70cc7e73e27d5c4118451 Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Mon, 3 Feb 2025 14:29:32 +0000 Subject: [PATCH 18/56] chore(regions): make test a bit more thorough for good measure --- .../tests/integration/projects.graph.spec.ts | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts index 6d219beb9..9b33dc0ad 100644 --- a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts @@ -1,7 +1,13 @@ import { db } from '@/db/knex' import { AllScopes } from '@/modules/core/helpers/mainConstants' import { createRandomEmail } from '@/modules/core/helpers/testHelpers' -import { BranchRecord, CommitRecord, StreamRecord } from '@/modules/core/helpers/types' +import { + BranchCommitRecord, + BranchRecord, + CommitRecord, + StreamCommitRecord, + StreamRecord +} from '@/modules/core/helpers/types' import { grantStreamPermissionsFactory } from '@/modules/core/repositories/streams' import { getDb } from '@/modules/multiregion/utils/dbSelector' import { @@ -415,8 +421,21 @@ isMultiRegionTestMode() .select('*') .where({ id: testVersion.id }) .first() - expect(version).to.not.be.undefined + + const streamCommitsRecord = await targetRegionDb + .table('stream_commits') + .select('*') + .where({ commitId: testVersion.id }) + .first() + expect(streamCommitsRecord).to.not.be.undefined + + const branchCommitsRecord = await targetRegionDb + .table('branch_commits') + .select('*') + .where({ commitId: testVersion.id }) + .first() + expect(branchCommitsRecord).to.not.be.undefined }) }) : void 0 From 2b8f6af4cd991409e5989a5319072202dcea51d6 Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Mon, 3 Feb 2025 15:31:45 +0000 Subject: [PATCH 19/56] fix(regions): move project objects --- .../modules/core/domain/objects/operations.ts | 2 + .../modules/core/repositories/objects.ts | 12 ++ .../modules/workspaces/domain/operations.ts | 3 + .../workspaces/graph/resolvers/regions.ts | 6 +- .../workspaces/repositories/projectRegions.ts | 125 ++++++++++++++++-- .../workspaces/services/projectRegions.ts | 21 ++- 6 files changed, 149 insertions(+), 20 deletions(-) diff --git a/packages/server/modules/core/domain/objects/operations.ts b/packages/server/modules/core/domain/objects/operations.ts index 7e9de7115..cb9d10747 100644 --- a/packages/server/modules/core/domain/objects/operations.ts +++ b/packages/server/modules/core/domain/objects/operations.ts @@ -15,6 +15,8 @@ export type GetStreamObjects = ( objectIds: string[] ) => Promise +export type GetStreamObjectCount = (params: { streamId: string }) => Promise + export type GetObject = ( objectId: string, streamId: string diff --git a/packages/server/modules/core/repositories/objects.ts b/packages/server/modules/core/repositories/objects.ts index 120a91e72..3f8ffab44 100644 --- a/packages/server/modules/core/repositories/objects.ts +++ b/packages/server/modules/core/repositories/objects.ts @@ -14,6 +14,7 @@ import { GetObjectChildrenQuery, GetObjectChildrenStream, GetObjectsStream, + GetStreamObjectCount, GetStreamObjects, HasObjects, StoreClosuresIfNotFound, @@ -92,6 +93,17 @@ export const getBatchedStreamObjectsFactory = return executeBatchedSelect(baseQuery, options) } +export const getStreamObjectCountFactory = + (deps: { db: Knex }): GetStreamObjectCount => + async ({ streamId }) => { + const [res] = await tables + .objects(deps.db) + .where(Objects.col.streamId, streamId) + .count() + + return parseInt(res.count as string) + } + export const insertObjectsFactory = (deps: { db: Knex }): StoreObjects => async (objects: ObjectRecord[], options?: Partial<{ trx: Knex.Transaction }>) => { diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index 83ee87e85..d88acb978 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -363,3 +363,6 @@ export type CopyProjectModels = (params: { export type CopyProjectVersions = (params: { projectIds: string[] }) => Promise> +export type CopyProjectObjects = (params: { + projectIds: string[] +}) => Promise> diff --git a/packages/server/modules/workspaces/graph/resolvers/regions.ts b/packages/server/modules/workspaces/graph/resolvers/regions.ts index 73770ec14..92b685f6a 100644 --- a/packages/server/modules/workspaces/graph/resolvers/regions.ts +++ b/packages/server/modules/workspaces/graph/resolvers/regions.ts @@ -11,6 +11,7 @@ import { } from '@/modules/workspaces/repositories/regions' import { copyProjectModelsFactory, + copyProjectObjectsFactory, copyProjectsFactory, copyProjectVersionsFactory, copyWorkspaceFactory @@ -29,6 +30,7 @@ import { getProjectFactory } from '@/modules/core/repositories/projects' import { getStreamBranchCountFactory } from '@/modules/core/repositories/branches' import { getStreamCommitCountFactory } from '@/modules/core/repositories/commits' import { withTransaction } from '@/modules/shared/helpers/dbHelper' +import { getStreamObjectCountFactory } from '@/modules/core/repositories/objects' export default { Workspace: { @@ -81,6 +83,7 @@ export default { getProject: getProjectFactory({ db: sourceDb }), countProjectModels: getStreamBranchCountFactory({ db: sourceDb }), countProjectVersions: getStreamCommitCountFactory({ db: sourceDb }), + countProjectObjects: getStreamObjectCountFactory({ db: sourceDb }), getAvailableRegions: getAvailableRegionsFactory({ getRegions: getRegionsFactory({ db }), canWorkspaceUseRegions: canWorkspaceUseRegionsFactory({ @@ -90,7 +93,8 @@ export default { copyWorkspace: copyWorkspaceFactory({ sourceDb, targetDb }), copyProjects: copyProjectsFactory({ sourceDb, targetDb }), copyProjectModels: copyProjectModelsFactory({ sourceDb, targetDb }), - copyProjectVersions: copyProjectVersionsFactory({ sourceDb, targetDb }) + copyProjectVersions: copyProjectVersionsFactory({ sourceDb, targetDb }), + copyProjectObjects: copyProjectObjectsFactory({ sourceDb, targetDb }) }) return await withTransaction(updateProjectRegion(args), targetDb) diff --git a/packages/server/modules/workspaces/repositories/projectRegions.ts b/packages/server/modules/workspaces/repositories/projectRegions.ts index 94da98d14..b18c5aac7 100644 --- a/packages/server/modules/workspaces/repositories/projectRegions.ts +++ b/packages/server/modules/workspaces/repositories/projectRegions.ts @@ -2,6 +2,7 @@ import { BranchCommits, Branches, Commits, + Objects, StreamCommits, StreamFavorites, Streams, @@ -12,12 +13,15 @@ import { Commit } from '@/modules/core/domain/commits/types' import { Stream } from '@/modules/core/domain/streams/types' import { BranchCommitRecord, + ObjectChildrenClosureRecord, + ObjectRecord, StreamCommitRecord, StreamFavoriteRecord } from '@/modules/core/helpers/types' import { executeBatchedSelect } from '@/modules/shared/helpers/dbHelper' import { CopyProjectModels, + CopyProjectObjects, CopyProjects, CopyProjectVersions, CopyWorkspace @@ -26,6 +30,7 @@ import { WorkspaceNotFoundError } from '@/modules/workspaces/errors/workspace' import { Knex } from 'knex' import { Workspace } from '@/modules/workspacesCore/domain/types' import { Workspaces } from '@/modules/workspacesCore/helpers/db' +import { ObjectPreview } from '@/modules/previews/domain/types' const tables = { workspaces: (db: Knex) => db(Workspaces.name), @@ -35,9 +40,17 @@ const tables = { branchCommits: (db: Knex) => db(BranchCommits.name), streamCommits: (db: Knex) => db(StreamCommits.name), streamFavorites: (db: Knex) => db(StreamFavorites.name), - streamsMeta: (db: Knex) => db(StreamsMeta.name) + streamsMeta: (db: Knex) => db(StreamsMeta.name), + objects: (db: Knex) => db(Objects.name), + objectClosures: (db: Knex) => + db('object_children_closure'), + objectPreviews: (db: Knex) => db('object_preview') } +/** + * Copies rows from the following tables: + * - workspaces + */ export const copyWorkspaceFactory = (deps: { sourceDb: Knex; targetDb: Knex }): CopyWorkspace => async ({ workspaceId }) => { @@ -55,6 +68,12 @@ export const copyWorkspaceFactory = return workspaceId } +/** + * Copies rows from the following tables: + * - streams + * - streams_meta + * - stream_favorites + */ export const copyProjectsFactory = (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjects => async ({ projectIds }) => { @@ -116,15 +135,18 @@ export const copyProjectsFactory = return copiedProjectIds } +/** + * Copies rows from the following tables: + * - branches + */ export const copyProjectModelsFactory = (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectModels => async ({ projectIds }) => { - const copiedModelIds: Record = projectIds.reduce( - (result, id) => ({ ...result, [id]: [] }), - {} - ) + const copiedModelIds: Record = {} for (const projectId of projectIds) { + copiedModelIds[projectId] = [] + const selectModels = tables .models(deps.sourceDb) .select('*') @@ -144,15 +166,21 @@ export const copyProjectModelsFactory = return copiedModelIds } +/** + * Copies rows from the following tables: + * - commits + * - branch_commits + * - stream_commits + */ export const copyProjectVersionsFactory = (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectVersions => async ({ projectIds }) => { - const copiedVersionIds: Record = projectIds.reduce( - (result, id) => ({ ...result, [id]: [] }), - {} - ) + const copiedVersionIds: Record = {} for (const projectId of projectIds) { + copiedVersionIds[projectId] = [] + + // Copy `commits` table rows in batches const selectVersions = tables .streamCommits(deps.sourceDb) .select('*') @@ -170,13 +198,13 @@ export const copyProjectVersionsFactory = // Store copied version id copiedVersionIds[streamId].push(commitId) - // Copy `commits` row to target db + // Write `commits` row to target db await tables.versions(deps.targetDb).insert(commit).onConflict().ignore() } const commitIds = versions.map((version) => version.commitId) - // Fetch `branch_commits` rows for versions in batch + // Copy `branch_commits` table rows for current batch of versions const selectBranchCommits = tables .branchCommits(deps.sourceDb) .select('*') @@ -184,7 +212,7 @@ export const copyProjectVersionsFactory = for await (const branchCommits of executeBatchedSelect(selectBranchCommits)) { for (const branchCommit of branchCommits) { - // Copy `branch_commits` row to target db + // Write `branch_commits` row to target db await tables .branchCommits(deps.targetDb) .insert(branchCommit) @@ -193,7 +221,7 @@ export const copyProjectVersionsFactory = } } - // Fetch `stream_commits` rows for versions in batch + // Copy `stream_commits` table rows for current batch of versions const selectStreamCommits = tables .streamCommits(deps.sourceDb) .select('*') @@ -201,7 +229,7 @@ export const copyProjectVersionsFactory = for await (const streamCommits of executeBatchedSelect(selectStreamCommits)) { for (const streamCommit of streamCommits) { - // Copy `stream_commits` row to target db + // Write `stream_commits` row to target db await tables .streamCommits(deps.targetDb) .insert(streamCommit) @@ -214,3 +242,72 @@ export const copyProjectVersionsFactory = return copiedVersionIds } + +/** + * Copies rows from the following tables: + * - objects + * - object_children_closure + * - object_preview + */ +export const copyProjectObjectsFactory = + (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectObjects => + async ({ projectIds }) => { + const copiedObjectIds: Record = {} + + for (const projectId of projectIds) { + copiedObjectIds[projectId] = [] + + // Copy `objects` table rows in batches + const selectObjects = tables + .objects(deps.sourceDb) + .select('*') + .where(Objects.col.streamId, projectId) + .orderBy(Objects.col.id) + + for await (const objects of executeBatchedSelect(selectObjects)) { + for (const object of objects) { + // Store copied object ids by source project + copiedObjectIds[projectId].push(object.id) + + // Write `objects` table row to target db + await tables.objects(deps.targetDb).insert(object).onConflict().ignore() + } + } + + // Copy `object_children_closure` rows in batches + const selectObjectClosures = tables + .objectClosures(deps.sourceDb) + .select('*') + .where('streamId', projectId) + + for await (const closures of executeBatchedSelect(selectObjectClosures)) { + for (const closure of closures) { + // Write `object_children_closure` row to target db + await tables + .objectClosures(deps.targetDb) + .insert(closure) + .onConflict() + .ignore() + } + } + + // Copy `object_preview` rows in batches + const selectObjectPreviews = tables + .objectPreviews(deps.sourceDb) + .select('*') + .where('streamId', projectId) + + for await (const previews of executeBatchedSelect(selectObjectPreviews)) { + for (const preview of previews) { + // Write `object_preview` row to target db + await tables + .objectPreviews(deps.targetDb) + .insert(preview) + .onConflict() + .ignore() + } + } + } + + return copiedObjectIds + } diff --git a/packages/server/modules/workspaces/services/projectRegions.ts b/packages/server/modules/workspaces/services/projectRegions.ts index 561aee792..f09328e93 100644 --- a/packages/server/modules/workspaces/services/projectRegions.ts +++ b/packages/server/modules/workspaces/services/projectRegions.ts @@ -1,8 +1,10 @@ import { GetStreamBranchCount } from '@/modules/core/domain/branches/operations' import { GetStreamCommitCount } from '@/modules/core/domain/commits/operations' +import { GetStreamObjectCount } from '@/modules/core/domain/objects/operations' import { GetProject } from '@/modules/core/domain/projects/operations' import { CopyProjectModels, + CopyProjectObjects, CopyProjects, CopyProjectVersions, CopyWorkspace, @@ -16,11 +18,13 @@ export const updateProjectRegionFactory = getProject: GetProject countProjectModels: GetStreamBranchCount countProjectVersions: GetStreamCommitCount + countProjectObjects: GetStreamObjectCount getAvailableRegions: GetAvailableRegions copyWorkspace: CopyWorkspace copyProjects: CopyProjects copyProjectModels: CopyProjectModels copyProjectVersions: CopyProjectVersions + copyProjectObjects: CopyProjectObjects }): UpdateProjectRegion => async (params) => { const { projectId, regionKey } = params @@ -60,7 +64,9 @@ export const updateProjectRegionFactory = const modelIds = await deps.copyProjectModels({ projectIds }) const versionIds = await deps.copyProjectVersions({ projectIds }) - // TODO: Move objects + // Move objects + const objectIds = await deps.copyProjectObjects({ projectIds }) + // TODO: Move automations // TODO: Move comments // TODO: Move file blobs @@ -69,12 +75,17 @@ export const updateProjectRegionFactory = // TODO: Validate state after move captures latest state of project const sourceProjectModelCount = await deps.countProjectModels(projectId) const sourceProjectVersionCount = await deps.countProjectVersions(projectId) + const sourceProjectObjectCount = await deps.countProjectObjects({ + streamId: projectId + }) - const isReconciled = - modelIds[projectId].length === sourceProjectModelCount && - versionIds[projectId].length === sourceProjectVersionCount + const tests = [ + modelIds[projectId].length === sourceProjectModelCount, + versionIds[projectId].length === sourceProjectVersionCount, + objectIds[projectId].length === sourceProjectObjectCount + ] - if (!isReconciled) { + if (!tests.every((test) => !!test)) { // TODO: Move failed or source project added data while changing regions. Retry move. throw new ProjectRegionAssignmentError( 'Missing data from source project in target region copy after move.' From 62466f5cb968ac1f3652699c0461612f5252a44f Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Mon, 3 Feb 2025 15:52:10 +0000 Subject: [PATCH 20/56] chore(regions): add tests for object move --- .../core/tests/integration/objects.spec.ts | 79 +++++++++++++++++++ .../tests/integration/projects.graph.spec.ts | 43 ++++++++-- 2 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 packages/server/modules/core/tests/integration/objects.spec.ts diff --git a/packages/server/modules/core/tests/integration/objects.spec.ts b/packages/server/modules/core/tests/integration/objects.spec.ts new file mode 100644 index 000000000..a2436f38a --- /dev/null +++ b/packages/server/modules/core/tests/integration/objects.spec.ts @@ -0,0 +1,79 @@ +import { createRandomEmail } from '@/modules/core/helpers/testHelpers' +import { getStreamObjectCountFactory } from '@/modules/core/repositories/objects' +import { BasicTestUser, createTestUser } from '@/test/authHelper' +import { BasicTestBranch, createTestBranch } from '@/test/speckle-helpers/branchHelper' +import { + BasicTestCommit, + createTestCommit, + createTestObject +} from '@/test/speckle-helpers/commitHelper' +import { BasicTestStream, createTestStream } from '@/test/speckle-helpers/streamHelper' +import cryptoRandomString from 'crypto-random-string' +import { db } from '@/db/knex' +import { expect } from 'chai' + +const getStreamObjectCount = getStreamObjectCountFactory({ db }) + +describe('Object repository functions', () => { + const adminUser: BasicTestUser = { + id: '', + name: 'John Speckle', + email: createRandomEmail() + } + + const testProject: BasicTestStream = { + id: '', + ownerId: '', + name: 'Test Project', + isPublic: true + } + + const testModel: BasicTestBranch = { + id: '', + name: cryptoRandomString({ length: 8 }), + streamId: '', + authorId: '' + } + + const testVersion: BasicTestCommit = { + id: '', + objectId: '', + streamId: '', + authorId: '' + } + + before(async () => { + await createTestUser(adminUser) + }) + + beforeEach(async () => { + await createTestStream(testProject, adminUser) + await createTestBranch({ + stream: testProject, + branch: testModel, + owner: adminUser + }) + + testVersion.branchName = testModel.name + testVersion.objectId = await createTestObject({ projectId: testProject.id }) + + await createTestCommit(testVersion, { + owner: adminUser, + stream: testProject + }) + }) + + describe('getStreamObjectCountFactory creates a function, that', () => { + it('correctly counts the number of objects in a project', async () => { + const count = await getStreamObjectCount({ streamId: testProject.id }) + expect(count).to.equal(1) + }) + + it('returns 0 if the project does not exist', async () => { + const count = await getStreamObjectCount({ + streamId: cryptoRandomString({ length: 9 }) + }) + expect(count).to.equal(0) + }) + }) +}) diff --git a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts index 9b33dc0ad..7813b8fc2 100644 --- a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts @@ -5,6 +5,7 @@ import { BranchCommitRecord, BranchRecord, CommitRecord, + ObjectRecord, StreamCommitRecord, StreamRecord } from '@/modules/core/helpers/types' @@ -51,6 +52,15 @@ import cryptoRandomString from 'crypto-random-string' import { Knex } from 'knex' import { SetOptional } from 'type-fest' +const tables = { + projects: (db: Knex) => db.table('streams'), + models: (db: Knex) => db.table('branches'), + versions: (db: Knex) => db.table('commits'), + streamCommits: (db: Knex) => db.table('stream_commits'), + branchCommits: (db: Knex) => db.table('branch_commits'), + objects: (db: Knex) => db.table('objects') +} + const grantStreamPermissions = grantStreamPermissionsFactory({ db }) describe('Workspace project GQL CRUD', () => { @@ -382,8 +392,8 @@ isMultiRegionTestMode() expect(res).to.not.haveGraphQLErrors() - const project = await targetRegionDb - .table('streams') + const project = await tables + .projects(targetRegionDb) .select('*') .where({ id: testProject.id }) .first() @@ -416,26 +426,43 @@ isMultiRegionTestMode() expect(res).to.not.haveGraphQLErrors() - const version = await targetRegionDb - .table('commits') + const version = await tables + .versions(targetRegionDb) .select('*') .where({ id: testVersion.id }) .first() expect(version).to.not.be.undefined - const streamCommitsRecord = await targetRegionDb - .table('stream_commits') + const streamCommitsRecord = await tables + .streamCommits(targetRegionDb) .select('*') .where({ commitId: testVersion.id }) .first() expect(streamCommitsRecord).to.not.be.undefined - const branchCommitsRecord = await targetRegionDb - .table('branch_commits') + const branchCommitsRecord = await tables + .branchCommits(targetRegionDb) .select('*') .where({ commitId: testVersion.id }) .first() expect(branchCommitsRecord).to.not.be.undefined }) + + it('moves project version objects to target regional db', async () => { + const res = await apollo.execute(UpdateProjectRegionDocument, { + projectId: testProject.id, + regionKey: regionKey2 + }) + + expect(res).to.not.haveGraphQLErrors() + + const object = await tables + .objects(targetRegionDb) + .select('*') + .where({ id: testVersion.objectId }) + .first() + + expect(object).to.not.be.undefined + }) }) : void 0 From 51ba709e66669cf4023ba44b7e5b30211a7b02d0 Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Mon, 3 Feb 2025 23:55:11 +0000 Subject: [PATCH 21/56] feat(regions): move project automations --- .../modules/automate/domain/operations.ts | 8 +- .../modules/automate/helpers/graphTypes.ts | 4 +- .../server/modules/automate/helpers/types.ts | 4 +- .../automate/repositories/automations.ts | 19 +- .../modules/automate/services/encryption.ts | 4 +- .../modules/core/graph/dataloaders/index.ts | 4 +- .../modules/workspaces/domain/operations.ts | 3 + .../workspaces/graph/resolvers/regions.ts | 8 +- .../workspaces/repositories/projectRegions.ts | 194 +++++++++++++++++- .../workspaces/services/projectRegions.ts | 14 +- 10 files changed, 240 insertions(+), 22 deletions(-) diff --git a/packages/server/modules/automate/domain/operations.ts b/packages/server/modules/automate/domain/operations.ts index b9fe28a2b..4e795d195 100644 --- a/packages/server/modules/automate/domain/operations.ts +++ b/packages/server/modules/automate/domain/operations.ts @@ -1,6 +1,6 @@ import { InsertableAutomationFunctionRun } from '@/modules/automate/domain/types' import { - AutomateRevisionFunctionRecord, + AutomationRevisionFunctionRecord, AutomationFunctionRunRecord, AutomationRecord, AutomationRevisionRecord, @@ -175,7 +175,7 @@ export type GetRevisionsTriggerDefinitions = (params: { export type GetRevisionsFunctions = (params: { automationRevisionIds: string[] -}) => Promise<{ [automationRevisionId: string]: AutomateRevisionFunctionRecord[] }> +}) => Promise<{ [automationRevisionId: string]: AutomationRevisionFunctionRecord[] }> export type CreateStoredAuthCode = ( params: Omit @@ -204,3 +204,7 @@ export type TriggerAutomationRevisionRun = < manifest: M source?: RunTriggerSource }) => Promise<{ automationRunId: string }> + +export type GetProjectAutomationCount = (params: { + projectId: string +}) => Promise diff --git a/packages/server/modules/automate/helpers/graphTypes.ts b/packages/server/modules/automate/helpers/graphTypes.ts index 40be15930..366a99f5c 100644 --- a/packages/server/modules/automate/helpers/graphTypes.ts +++ b/packages/server/modules/automate/helpers/graphTypes.ts @@ -1,5 +1,5 @@ import { - AutomateRevisionFunctionRecord, + AutomationRevisionFunctionRecord, AutomationFunctionRunRecord, AutomationRecord, AutomationRevisionRecord, @@ -56,7 +56,7 @@ export type AutomationRunTriggerGraphQLReturn = AutomationRunTriggerRecord & { } export type AutomationRevisionFunctionGraphQLReturn = Merge< - AutomateRevisionFunctionRecord, + AutomationRevisionFunctionRecord, { functionInputs: Nullable> release: AutomateFunctionReleaseGraphQLReturn diff --git a/packages/server/modules/automate/helpers/types.ts b/packages/server/modules/automate/helpers/types.ts index 3c8b82d12..7881385d6 100644 --- a/packages/server/modules/automate/helpers/types.ts +++ b/packages/server/modules/automate/helpers/types.ts @@ -66,7 +66,7 @@ export type AutomationRunRecord = { executionEngineRunId: string | null } -export type AutomateRevisionFunctionRecord = { +export type AutomationRevisionFunctionRecord = { functionReleaseId: string functionId: string functionInputs: string | null @@ -118,7 +118,7 @@ export type AutomationTokenRecord = { } export type AutomationRevisionWithTriggersFunctions = AutomationRevisionRecord & { - functions: AutomateRevisionFunctionRecord[] + functions: AutomationRevisionFunctionRecord[] triggers: AutomationTriggerDefinitionRecord[] } diff --git a/packages/server/modules/automate/repositories/automations.ts b/packages/server/modules/automate/repositories/automations.ts index c424b3a8e..c07a21b40 100644 --- a/packages/server/modules/automate/repositories/automations.ts +++ b/packages/server/modules/automate/repositories/automations.ts @@ -16,6 +16,7 @@ import { GetLatestAutomationRevision, GetLatestAutomationRevisions, GetLatestVersionAutomationRuns, + GetProjectAutomationCount, GetRevisionsFunctions, GetRevisionsTriggerDefinitions, StoreAutomation, @@ -37,7 +38,7 @@ import { AutomationRunRecord, AutomationTokenRecord, AutomationTriggerRecordBase, - AutomateRevisionFunctionRecord, + AutomationRevisionFunctionRecord, AutomationRunWithTriggersFunctionRuns, AutomationRunTriggerRecord, AutomationFunctionRunRecord, @@ -86,7 +87,7 @@ const tables = { automationRevisions: (db: Knex) => db(AutomationRevisions.name), automationRevisionFunctions: (db: Knex) => - db(AutomationRevisionFunctions.name), + db(AutomationRevisionFunctions.name), automationTriggers: (db: Knex) => db(AutomationTriggers.name), automationRuns: (db: Knex) => db(AutomationRuns.name), @@ -321,7 +322,7 @@ export const storeAutomationTokenFactory = } export type InsertableAutomationRevisionFunction = Omit< - AutomateRevisionFunctionRecord, + AutomationRevisionFunctionRecord, 'automationRevisionId' > @@ -369,7 +370,7 @@ export const storeAutomationRevisionFactory = .automationRevisionFunctions(deps.db) .insert( revision.functions.map( - (f): AutomateRevisionFunctionRecord => ({ + (f): AutomationRevisionFunctionRecord => ({ ...f, automationRevisionId: id }) @@ -742,10 +743,12 @@ const getProjectAutomationsBaseQueryFactory = } export const getProjectAutomationsTotalCountFactory = - (deps: { db: Knex }) => async (params: GetProjectAutomationsParams) => { - const q = getProjectAutomationsBaseQueryFactory(deps)(params).count< - [{ count: string }] - >(Automations.col.id) + (deps: { db: Knex }): GetProjectAutomationCount => + async ({ projectId }) => { + const q = getProjectAutomationsBaseQueryFactory(deps)({ + projectId, + args: {} + }).count<[{ count: string }]>(Automations.col.id) const [ret] = await q diff --git a/packages/server/modules/automate/services/encryption.ts b/packages/server/modules/automate/services/encryption.ts index 3b8228c8b..8cfc98d66 100644 --- a/packages/server/modules/automate/services/encryption.ts +++ b/packages/server/modules/automate/services/encryption.ts @@ -7,7 +7,7 @@ import { Nullable, Optional } from '@speckle/shared' import { MisconfiguredEnvironmentError } from '@/modules/shared/errors' import { AutomationFunctionInputEncryptionError } from '@/modules/automate/errors/management' import { KeyPair, buildDecryptor } from '@/modules/shared/utils/libsodium' -import { AutomateRevisionFunctionRecord } from '@/modules/automate/helpers/types' +import { AutomationRevisionFunctionRecord } from '@/modules/automate/helpers/types' import { AutomationRevisionFunctionGraphQLReturn } from '@/modules/automate/helpers/graphTypes' import { FunctionReleaseSchemaType } from '@/modules/automate/helpers/executionEngine' import { LibsodiumEncryptionError } from '@/modules/shared/errors/encryption' @@ -118,7 +118,7 @@ export type GetFunctionInputsForFrontendDeps = { } & GetFunctionInputDecryptorDeps export type AutomationRevisionFunctionForInputRedaction = Merge< - AutomateRevisionFunctionRecord, + AutomationRevisionFunctionRecord, { release: FunctionReleaseSchemaType } > diff --git a/packages/server/modules/core/graph/dataloaders/index.ts b/packages/server/modules/core/graph/dataloaders/index.ts index 958019035..f6ed23fd1 100644 --- a/packages/server/modules/core/graph/dataloaders/index.ts +++ b/packages/server/modules/core/graph/dataloaders/index.ts @@ -56,7 +56,7 @@ import { Users } from '@/modules/core/dbSchema' import { getStreamPendingModelsFactory } from '@/modules/fileuploads/repositories/fileUploads' import { FileUploadRecord } from '@/modules/fileuploads/helpers/types' import { - AutomateRevisionFunctionRecord, + AutomationRevisionFunctionRecord, AutomationRecord, AutomationRevisionRecord, AutomationRunTriggerRecord, @@ -595,7 +595,7 @@ const dataLoadersDefinition = defineRequestDataloaders( }) return ids.map((i) => results[i] || []) }), - getRevisionFunctions: createLoader( + getRevisionFunctions: createLoader( async (ids) => { const results = await getRevisionsFunctions({ automationRevisionIds: ids.slice() diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index d88acb978..51dbdf23b 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -366,3 +366,6 @@ export type CopyProjectVersions = (params: { export type CopyProjectObjects = (params: { projectIds: string[] }) => Promise> +export type CopyProjectAutomations = (params: { + projectIds: string[] +}) => Promise> diff --git a/packages/server/modules/workspaces/graph/resolvers/regions.ts b/packages/server/modules/workspaces/graph/resolvers/regions.ts index 92b685f6a..f43fe0ab3 100644 --- a/packages/server/modules/workspaces/graph/resolvers/regions.ts +++ b/packages/server/modules/workspaces/graph/resolvers/regions.ts @@ -10,6 +10,7 @@ import { upsertRegionAssignmentFactory } from '@/modules/workspaces/repositories/regions' import { + copyProjectAutomationsFactory, copyProjectModelsFactory, copyProjectObjectsFactory, copyProjectsFactory, @@ -31,6 +32,7 @@ import { getStreamBranchCountFactory } from '@/modules/core/repositories/branche import { getStreamCommitCountFactory } from '@/modules/core/repositories/commits' import { withTransaction } from '@/modules/shared/helpers/dbHelper' import { getStreamObjectCountFactory } from '@/modules/core/repositories/objects' +import { getProjectAutomationsTotalCountFactory } from '@/modules/automate/repositories/automations' export default { Workspace: { @@ -84,6 +86,9 @@ export default { countProjectModels: getStreamBranchCountFactory({ db: sourceDb }), countProjectVersions: getStreamCommitCountFactory({ db: sourceDb }), countProjectObjects: getStreamObjectCountFactory({ db: sourceDb }), + countProjectAutomations: getProjectAutomationsTotalCountFactory({ + db: sourceDb + }), getAvailableRegions: getAvailableRegionsFactory({ getRegions: getRegionsFactory({ db }), canWorkspaceUseRegions: canWorkspaceUseRegionsFactory({ @@ -94,7 +99,8 @@ export default { copyProjects: copyProjectsFactory({ sourceDb, targetDb }), copyProjectModels: copyProjectModelsFactory({ sourceDb, targetDb }), copyProjectVersions: copyProjectVersionsFactory({ sourceDb, targetDb }), - copyProjectObjects: copyProjectObjectsFactory({ sourceDb, targetDb }) + copyProjectObjects: copyProjectObjectsFactory({ sourceDb, targetDb }), + copyProjectAutomations: copyProjectAutomationsFactory({ sourceDb, targetDb }) }) return await withTransaction(updateProjectRegion(args), targetDb) diff --git a/packages/server/modules/workspaces/repositories/projectRegions.ts b/packages/server/modules/workspaces/repositories/projectRegions.ts index b18c5aac7..4c7926b3e 100644 --- a/packages/server/modules/workspaces/repositories/projectRegions.ts +++ b/packages/server/modules/workspaces/repositories/projectRegions.ts @@ -1,4 +1,12 @@ import { + AutomationFunctionRuns, + AutomationRevisionFunctions, + AutomationRevisions, + AutomationRuns, + AutomationRunTriggers, + Automations, + AutomationTokens, + AutomationTriggers, BranchCommits, Branches, Commits, @@ -20,6 +28,7 @@ import { } from '@/modules/core/helpers/types' import { executeBatchedSelect } from '@/modules/shared/helpers/dbHelper' import { + CopyProjectAutomations, CopyProjectModels, CopyProjectObjects, CopyProjects, @@ -31,6 +40,16 @@ import { Knex } from 'knex' import { Workspace } from '@/modules/workspacesCore/domain/types' import { Workspaces } from '@/modules/workspacesCore/helpers/db' import { ObjectPreview } from '@/modules/previews/domain/types' +import { + AutomationFunctionRunRecord, + AutomationRecord, + AutomationRevisionFunctionRecord, + AutomationRevisionRecord, + AutomationRunRecord, + AutomationRunTriggerRecord, + AutomationTokenRecord, + AutomationTriggerDefinitionRecord +} from '@/modules/automate/helpers/types' const tables = { workspaces: (db: Knex) => db(Workspaces.name), @@ -44,7 +63,20 @@ const tables = { objects: (db: Knex) => db(Objects.name), objectClosures: (db: Knex) => db('object_children_closure'), - objectPreviews: (db: Knex) => db('object_preview') + objectPreviews: (db: Knex) => db('object_preview'), + automations: (db: Knex) => db(Automations.name), + automationTokens: (db: Knex) => db(AutomationTokens.name), + automationRevisions: (db: Knex) => + db(AutomationRevisions.name), + automationTriggers: (db: Knex) => + db(AutomationTriggers.name), + automationRevisionFunctions: (db: Knex) => + db(AutomationRevisionFunctions.name), + automationRuns: (db: Knex) => db(AutomationRuns.name), + automationRunTriggers: (db: Knex) => + db(AutomationRunTriggers.name), + automationFunctionRuns: (db: Knex) => + db(AutomationFunctionRuns.name) } /** @@ -311,3 +343,163 @@ export const copyProjectObjectsFactory = return copiedObjectIds } + +/** + * Copies rows from the following tables: + * - automations + * - automation_tokens + * - automation_revisions + * - automation_triggers + * - automation_revision_functions + * - automation_runs + * - automation_run_triggers + * - automation_function_runs + */ +export const copyProjectAutomationsFactory = + (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectAutomations => + async ({ projectIds }) => { + const copiedAutomationIds: Record = {} + + for (const projectId of projectIds) { + copiedAutomationIds[projectId] = [] + + // Copy `automations` table rows in batches + const selectAutomations = tables + .automations(deps.sourceDb) + .select('*') + .where(Automations.col.projectId, projectId) + + for await (const automations of executeBatchedSelect(selectAutomations)) { + for (const automation of automations) { + // Store copied automation id + copiedAutomationIds[projectId].push(automation.id) + + // Write `automations` table row to target db + await tables + .automations(deps.targetDb) + .insert(automation) + .onConflict() + .ignore() + + // Copy `automation_tokens` rows for automation + const selectAutomationTokens = tables + .automationTokens(deps.sourceDb) + .select('*') + .where(AutomationTokens.col.automationId, automation.id) + + for await (const tokens of executeBatchedSelect(selectAutomationTokens)) { + for (const token of tokens) { + // Write `automation_tokens` row to target db + await tables + .automationTokens(deps.targetDb) + .insert(token) + .onConflict() + .ignore() + } + } + + // Copy `automation_revisions` rows for automation + const selectAutomationRevisions = tables + .automationRevisions(deps.sourceDb) + .select('*') + .where(AutomationRevisions.col.automationId, automation.id) + + for await (const automationRevisions of executeBatchedSelect( + selectAutomationRevisions + )) { + for (const automationRevision of automationRevisions) { + // Write `automation_revisions` row to target db + await tables + .automationRevisions(deps.targetDb) + .insert(automationRevision) + .onConflict() + .ignore() + + // Copy `automation_triggers` rows for automation revision + const automationTriggers = await tables + .automationTriggers(deps.sourceDb) + .select('*') + .where( + AutomationTriggers.col.automationRevisionId, + automationRevision.id + ) + + for (const automationTrigger of automationTriggers) { + await tables + .automationTriggers(deps.targetDb) + .insert(automationTrigger) + .onConflict() + .ignore() + } + + // Copy `automation_revision_functions` rows for automation revision + const automationRevisionFunctions = await tables + .automationRevisionFunctions(deps.sourceDb) + .select('*') + .where( + AutomationRevisionFunctions.col.automationRevisionId, + automationRevision.id + ) + + for (const automationRevisionFunction of automationRevisionFunctions) { + await tables + .automationRevisionFunctions(deps.targetDb) + .insert(automationRevisionFunction) + .onConflict() + .ignore() + } + + // Copy `automation_runs` rows for automation revision + const selectAutomationRuns = tables + .automationRuns(deps.sourceDb) + .select('*') + .where(AutomationRuns.col.automationRevisionId, automationRevision.id) + + for await (const automationRuns of executeBatchedSelect( + selectAutomationRuns + )) { + for (const automationRun of automationRuns) { + // Write `automation_runs` row to target db + await tables + .automationRuns(deps.targetDb) + .insert(automationRun) + .onConflict() + .ignore() + + // Copy `automation_run_triggers` rows for automation run + const automationRunTriggers = await tables + .automationRunTriggers(deps.sourceDb) + .select('*') + .where(AutomationRunTriggers.col.automationRunId, automationRun.id) + + for (const automationRunTrigger of automationRunTriggers) { + await tables + .automationRunTriggers(deps.targetDb) + .insert(automationRunTrigger) + .onConflict() + .ignore() + } + + // Copy `automation_function_runs` rows for automation run + const automationFunctionRuns = await tables + .automationFunctionRuns(deps.sourceDb) + .select('*') + .where(AutomationFunctionRuns.col.runId, automationRun.id) + + for (const automationFunctionRun of automationFunctionRuns) { + await tables + .automationFunctionRuns(deps.targetDb) + .insert(automationFunctionRun) + .onConflict() + .ignore() + } + } + } + } + } + } + } + } + + return copiedAutomationIds + } diff --git a/packages/server/modules/workspaces/services/projectRegions.ts b/packages/server/modules/workspaces/services/projectRegions.ts index f09328e93..dce91ed77 100644 --- a/packages/server/modules/workspaces/services/projectRegions.ts +++ b/packages/server/modules/workspaces/services/projectRegions.ts @@ -1,8 +1,10 @@ +import { GetProjectAutomationCount } from '@/modules/automate/domain/operations' import { GetStreamBranchCount } from '@/modules/core/domain/branches/operations' import { GetStreamCommitCount } from '@/modules/core/domain/commits/operations' import { GetStreamObjectCount } from '@/modules/core/domain/objects/operations' import { GetProject } from '@/modules/core/domain/projects/operations' import { + CopyProjectAutomations, CopyProjectModels, CopyProjectObjects, CopyProjects, @@ -19,12 +21,14 @@ export const updateProjectRegionFactory = countProjectModels: GetStreamBranchCount countProjectVersions: GetStreamCommitCount countProjectObjects: GetStreamObjectCount + countProjectAutomations: GetProjectAutomationCount getAvailableRegions: GetAvailableRegions copyWorkspace: CopyWorkspace copyProjects: CopyProjects copyProjectModels: CopyProjectModels copyProjectVersions: CopyProjectVersions copyProjectObjects: CopyProjectObjects + copyProjectAutomations: CopyProjectAutomations }): UpdateProjectRegion => async (params) => { const { projectId, regionKey } = params @@ -67,7 +71,9 @@ export const updateProjectRegionFactory = // Move objects const objectIds = await deps.copyProjectObjects({ projectIds }) - // TODO: Move automations + // Move automations + const automationIds = await deps.copyProjectAutomations({ projectIds }) + // TODO: Move comments // TODO: Move file blobs // TODO: Move webhooks @@ -78,11 +84,15 @@ export const updateProjectRegionFactory = const sourceProjectObjectCount = await deps.countProjectObjects({ streamId: projectId }) + const sourceProjectAutomationCount = await deps.countProjectAutomations({ + projectId + }) const tests = [ modelIds[projectId].length === sourceProjectModelCount, versionIds[projectId].length === sourceProjectVersionCount, - objectIds[projectId].length === sourceProjectObjectCount + objectIds[projectId].length === sourceProjectObjectCount, + automationIds[projectId].length === sourceProjectAutomationCount ] if (!tests.every((test) => !!test)) { From de3173e87e8e6b25b057e1701588f0b0ec1d1160 Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Tue, 4 Feb 2025 13:51:27 +0000 Subject: [PATCH 22/56] chore(regions): add tests for moving automations --- .../tests/integration/projects.graph.spec.ts | 90 ++++++++++++++++++- .../test/speckle-helpers/automationHelper.ts | 63 +++++++++---- 2 files changed, 135 insertions(+), 18 deletions(-) diff --git a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts index 7813b8fc2..6a9b3eafa 100644 --- a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts @@ -1,4 +1,23 @@ import { db } from '@/db/knex' +import { + AutomationFunctionRunRecord, + AutomationRecord, + AutomationRevisionFunctionRecord, + AutomationRevisionRecord, + AutomationRunRecord, + AutomationRunTriggerRecord, + AutomationTokenRecord, + AutomationTriggerDefinitionRecord +} from '@/modules/automate/helpers/types' +import { + AutomationFunctionRuns, + AutomationRevisionFunctions, + AutomationRevisions, + AutomationRuns, + AutomationRunTriggers, + AutomationTokens, + AutomationTriggers +} from '@/modules/core/dbSchema' import { AllScopes } from '@/modules/core/helpers/mainConstants' import { createRandomEmail } from '@/modules/core/helpers/testHelpers' import { @@ -35,6 +54,7 @@ import { TestApolloServer } from '@/test/graphqlHelper' import { beforeEachContext } from '@/test/hooks' +import { createTestAutomation } from '@/test/speckle-helpers/automationHelper' import { BasicTestBranch, createTestBranch } from '@/test/speckle-helpers/branchHelper' import { BasicTestCommit, @@ -58,7 +78,20 @@ const tables = { versions: (db: Knex) => db.table('commits'), streamCommits: (db: Knex) => db.table('stream_commits'), branchCommits: (db: Knex) => db.table('branch_commits'), - objects: (db: Knex) => db.table('objects') + objects: (db: Knex) => db.table('objects'), + automations: (db: Knex) => db.table('automations'), + automationTokens: (db: Knex) => db(AutomationTokens.name), + automationRevisions: (db: Knex) => + db(AutomationRevisions.name), + automationTriggers: (db: Knex) => + db(AutomationTriggers.name), + automationRevisionFunctions: (db: Knex) => + db(AutomationRevisionFunctions.name), + automationRuns: (db: Knex) => db(AutomationRuns.name), + automationRunTriggers: (db: Knex) => + db(AutomationRunTriggers.name), + automationFunctionRuns: (db: Knex) => + db(AutomationFunctionRuns.name) } const grantStreamPermissions = grantStreamPermissionsFactory({ db }) @@ -344,6 +377,10 @@ isMultiRegionTestMode() authorId: '' } + let testAutomation: AutomationRecord + let testAutomationToken: AutomationTokenRecord + let testAutomationRevision: AutomationRevisionRecord + let apollo: TestApolloServer let targetRegionDb: Knex @@ -382,6 +419,23 @@ isMultiRegionTestMode() owner: adminUser, stream: testProject }) + + const { automation, revision } = await createTestAutomation({ + userId: adminUser.id, + projectId: testProject.id, + revision: { + functionId: cryptoRandomString({ length: 9 }), + functionReleaseId: cryptoRandomString({ length: 9 }) + } + }) + + if (!revision) { + throw new Error('Failed to create automation revision.') + } + + testAutomation = automation.automation + testAutomationToken = automation.token + testAutomationRevision = revision }) it('moves project record to target regional db', async () => { @@ -464,5 +518,39 @@ isMultiRegionTestMode() expect(object).to.not.be.undefined }) + + it('moves project automation data to target regional db', async () => { + const res = await apollo.execute(UpdateProjectRegionDocument, { + projectId: testProject.id, + regionKey: regionKey2 + }) + + expect(res).to.not.haveGraphQLErrors() + + const automation = await tables + .automations(targetRegionDb) + .select('*') + .where({ id: testAutomation.id }) + .first() + expect(automation).to.not.be.undefined + + const automationToken = await tables + .automationTokens(targetRegionDb) + .select('*') + .where({ automationId: testAutomation.id }) + .first() + expect(automationToken).to.not.be.undefined + expect(automationToken?.automateToken).to.equal( + testAutomationToken.automateToken + ) + + const automationRevision = await tables + .automationRevisions(targetRegionDb) + .select('*') + .where({ automationId: testAutomation.id }) + .first() + expect(automationRevision).to.not.be.undefined + expect(automationRevision?.id).to.equal(testAutomationRevision.id) + }) }) : void 0 diff --git a/packages/server/test/speckle-helpers/automationHelper.ts b/packages/server/test/speckle-helpers/automationHelper.ts index 0e94fb315..bf921a6e7 100644 --- a/packages/server/test/speckle-helpers/automationHelper.ts +++ b/packages/server/test/speckle-helpers/automationHelper.ts @@ -42,22 +42,29 @@ import { db } from '@/db/knex' import { validateStreamAccessFactory } from '@/modules/core/services/streams/access' import { authorizeResolver } from '@/modules/shared' import { getEventBus } from '@/modules/shared/services/eventBus' +import { getProjectDbClient } from '@/modules/multiregion/utils/dbSelector' +import { Knex } from 'knex' -const storeAutomation = storeAutomationFactory({ db }) -const storeAutomationToken = storeAutomationTokenFactory({ db }) -const storeAutomationRevision = storeAutomationRevisionFactory({ db }) -const getAutomation = getAutomationFactory({ db }) -const getLatestStreamBranch = getLatestStreamBranchFactory({ db }) const validateStreamAccess = validateStreamAccessFactory({ authorizeResolver }) export const generateFunctionId = () => cryptoRandomString({ length: 10 }) export const generateFunctionReleaseId = () => cryptoRandomString({ length: 10 }) +/** + * @param overrides By default, we mock requests to the execution engine. You can replace those mocks here. + */ export const buildAutomationCreate = ( - overrides?: Partial<{ - createDbAutomation: typeof clientCreateAutomation - }> + params: { + dbClient: Knex + overrides?: Partial<{ + createDbAutomation: typeof clientCreateAutomation + }> + } = { + dbClient: db + } ) => { + const { dbClient, overrides } = params + const create = createAutomationFactory({ createAuthCode: createStoredAuthCodeFactory({ redis: createInmemoryRedisClient() }), automateCreateAutomation: @@ -66,8 +73,8 @@ export const buildAutomationCreate = ( automationId: cryptoRandomString({ length: 10 }), token: cryptoRandomString({ length: 10 }) })), - storeAutomation, - storeAutomationToken, + storeAutomation: storeAutomationFactory({ db: dbClient }), + storeAutomationToken: storeAutomationTokenFactory({ db: dbClient }), validateStreamAccess, eventEmit: getEventBus().emit }) @@ -75,9 +82,19 @@ export const buildAutomationCreate = ( return create } +/** + * @param overrides By default, we mock requests to the execution engine. You can replace those mocks here. + */ export const buildAutomationRevisionCreate = ( - overrides?: Partial + params: { + dbClient: Knex + overrides?: Partial + } = { + dbClient: db + } ) => { + const { dbClient, overrides } = params + const fakeGetRelease = (params: { functionReleaseId: string functionId: string @@ -91,9 +108,9 @@ export const buildAutomationRevisionCreate = ( }) const create = createAutomationRevisionFactory({ - getAutomation, - storeAutomationRevision, - getBranchesByIds: getBranchesByIdsFactory({ db }), + getAutomation: getAutomationFactory({ db: dbClient }), + storeAutomationRevision: storeAutomationRevisionFactory({ db: dbClient }), + getBranchesByIds: getBranchesByIdsFactory({ db: dbClient }), getFunctionRelease: async (params) => fakeGetRelease(params), getFunctionReleases: async (params) => params.ids.map(fakeGetRelease), getEncryptionKeyPair, @@ -128,8 +145,10 @@ export const createTestAutomation = async (params: { revision: { input: revisionInput, functionReleaseId, functionId } = {} } = params - const createAutomation = buildAutomationCreate() - const createRevision = buildAutomationRevisionCreate() + const projectDb = await getProjectDbClient({ projectId }) + + const createAutomation = buildAutomationCreate({ dbClient: projectDb }) + const createRevision = buildAutomationRevisionCreate({ dbClient: projectDb }) const automationRet = await createAutomation({ input: { @@ -143,7 +162,7 @@ export const createTestAutomation = async (params: { let revisionRet: Awaited> | null = null if (functionReleaseId?.length && functionId?.length) { - const firstModel = await getLatestStreamBranch(projectId) + const firstModel = await getLatestStreamBranchFactory({ db: projectDb })(projectId) if (!firstModel) throw new Error( @@ -186,6 +205,16 @@ export type TestAutomationWithRevision = Awaited< ReturnType > +// export const createTestAutomationRun = async (params: { +// automationId: string +// automationRunData?: Partial +// automationFunctionRunData?: Partial[] +// }) => { +// const { automationId, automationRunData = {}, automationFunctionRunData = {} } = params + +// const latestRevision = getLatestAutomationRevisionFactory({ db }) +// } + export const truncateAutomations = async () => { await truncateTables([ AutomationRunTriggers.name, From e371633702711944081217f67c49dfa6104abeab Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Thu, 6 Feb 2025 00:47:29 +0000 Subject: [PATCH 23/56] chore(regions): more tests for moving automate data --- .../modules/automate/services/trigger.ts | 7 +- .../tests/integration/projects.graph.spec.ts | 58 ++++++++++++++++- .../test/speckle-helpers/automationHelper.ts | 64 ++++++++++++++++--- 3 files changed, 116 insertions(+), 13 deletions(-) diff --git a/packages/server/modules/automate/services/trigger.ts b/packages/server/modules/automate/services/trigger.ts index 855e0dfe3..289ee9aa6 100644 --- a/packages/server/modules/automate/services/trigger.ts +++ b/packages/server/modules/automate/services/trigger.ts @@ -46,6 +46,7 @@ import { ValidateStreamAccess } from '@/modules/core/domain/streams/operations' import { CreateAndStoreAppToken } from '@/modules/core/domain/tokens/operations' import { EventBusEmit } from '@/modules/shared/services/eventBus' import { AutomationRunEvents } from '@/modules/automate/domain/events' +import { isTestEnv } from '@/modules/shared/helpers/envHelper' export type OnModelVersionCreateDeps = { getAutomation: GetAutomation @@ -130,7 +131,7 @@ type CreateAutomationRunDataDeps = { getFunctionInputDecryptor: FunctionInputDecryptor } -const createAutomationRunDataFactory = +export const createAutomationRunDataFactory = (deps: CreateAutomationRunDataDeps) => async (params: { manifests: BaseTriggerManifest[] @@ -413,7 +414,7 @@ type ComposeTriggerDataDeps = { getBranchLatestCommits: GetBranchLatestCommits } -const composeTriggerDataFactory = +export const composeTriggerDataFactory = (deps: ComposeTriggerDataDeps) => async (params: { projectId: string @@ -572,7 +573,7 @@ export const createTestAutomationRunFactory = throw new TriggerAutomationError('Automation not found') } - if (!automationRecord.isTestAutomation) { + if (!isTestEnv() && !automationRecord.isTestAutomation) { throw new TriggerAutomationError( 'Automation is not a test automation and cannot create test function runs' ) diff --git a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts index 6a9b3eafa..8481f5393 100644 --- a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts @@ -54,7 +54,10 @@ import { TestApolloServer } from '@/test/graphqlHelper' import { beforeEachContext } from '@/test/hooks' -import { createTestAutomation } from '@/test/speckle-helpers/automationHelper' +import { + createTestAutomation, + createTestAutomationRun +} from '@/test/speckle-helpers/automationHelper' import { BasicTestBranch, createTestBranch } from '@/test/speckle-helpers/branchHelper' import { BasicTestCommit, @@ -380,6 +383,8 @@ isMultiRegionTestMode() let testAutomation: AutomationRecord let testAutomationToken: AutomationTokenRecord let testAutomationRevision: AutomationRevisionRecord + let testAutomationRun: AutomationRunRecord + let testAutomationFunctionRuns: AutomationFunctionRunRecord[] let apollo: TestApolloServer let targetRegionDb: Knex @@ -436,6 +441,15 @@ isMultiRegionTestMode() testAutomation = automation.automation testAutomationToken = automation.token testAutomationRevision = revision + + const { automationRun, functionRuns } = await createTestAutomationRun({ + userId: adminUser.id, + projectId: testProject.id, + automationId: testAutomation.id + }) + + testAutomationRun = automationRun + testAutomationFunctionRuns = functionRuns }) it('moves project record to target regional db', async () => { @@ -551,6 +565,48 @@ isMultiRegionTestMode() .first() expect(automationRevision).to.not.be.undefined expect(automationRevision?.id).to.equal(testAutomationRevision.id) + + const automationTrigger = await tables + .automationTriggers(targetRegionDb) + .select('*') + .where({ automationRevisionId: testAutomationRevision.id }) + .first() + expect(automationTrigger).to.not.be.undefined + }) + + it('moves project automation runs to target regional db', async () => { + const res = await apollo.execute(UpdateProjectRegionDocument, { + projectId: testProject.id, + regionKey: regionKey2 + }) + + expect(res).to.not.haveGraphQLErrors() + + const automationRun = await tables + .automationRuns(targetRegionDb) + .select('*') + .where({ id: testAutomationRun.id }) + .first() + expect(automationRun).to.not.be.undefined + + const automationRunTriggers = await tables + .automationRunTriggers(targetRegionDb) + .select('*') + .where({ automationRunId: testAutomationRun.id }) + expect(automationRunTriggers.length).to.not.equal(0) + + const automationFunctionRuns = await tables + .automationFunctionRuns(targetRegionDb) + .select('*') + .where({ runId: testAutomationRun.id }) + expect(automationFunctionRuns.length).to.equal( + testAutomationFunctionRuns.length + ) + expect( + automationFunctionRuns.every((run) => + testAutomationFunctionRuns.some((testRun) => testRun.id === run.id) + ) + ) }) }) : void 0 diff --git a/packages/server/test/speckle-helpers/automationHelper.ts b/packages/server/test/speckle-helpers/automationHelper.ts index bf921a6e7..809a9bc5d 100644 --- a/packages/server/test/speckle-helpers/automationHelper.ts +++ b/packages/server/test/speckle-helpers/automationHelper.ts @@ -1,8 +1,12 @@ import { getAutomationFactory, + getFullAutomationRevisionMetadataFactory, + getFullAutomationRunByIdFactory, + getLatestAutomationRevisionFactory, storeAutomationFactory, storeAutomationRevisionFactory, - storeAutomationTokenFactory + storeAutomationTokenFactory, + upsertAutomationRunFactory } from '@/modules/automate/repositories/automations' import { CreateAutomationRevisionDeps, @@ -15,6 +19,7 @@ import cryptoRandomString from 'crypto-random-string' import { createAutomation as clientCreateAutomation } from '@/modules/automate/clients/executionEngine' import { getBranchesByIdsFactory, + getBranchLatestCommitsFactory, getLatestStreamBranchFactory } from '@/modules/core/repositories/branches' @@ -35,6 +40,7 @@ import { import { faker } from '@faker-js/faker' import { getEncryptionKeyPair, + getEncryptionKeyPairFor, getFunctionInputDecryptorFactory } from '@/modules/automate/services/encryption' import { buildDecryptor } from '@/modules/shared/utils/libsodium' @@ -44,6 +50,7 @@ import { authorizeResolver } from '@/modules/shared' import { getEventBus } from '@/modules/shared/services/eventBus' import { getProjectDbClient } from '@/modules/multiregion/utils/dbSelector' import { Knex } from 'knex' +import { createTestAutomationRunFactory } from '@/modules/automate/services/trigger' const validateStreamAccess = validateStreamAccessFactory({ authorizeResolver }) @@ -205,15 +212,54 @@ export type TestAutomationWithRevision = Awaited< ReturnType > -// export const createTestAutomationRun = async (params: { -// automationId: string -// automationRunData?: Partial -// automationFunctionRunData?: Partial[] -// }) => { -// const { automationId, automationRunData = {}, automationFunctionRunData = {} } = params +export const createTestAutomationRun = async (params: { + userId: string + projectId: string + automationId: string +}) => { + const { userId, projectId, automationId } = params -// const latestRevision = getLatestAutomationRevisionFactory({ db }) -// } + const projectDb = await getProjectDbClient({ projectId }) + + const { automationRunId } = await createTestAutomationRunFactory({ + getEncryptionKeyPairFor, + getFunctionInputDecryptor: getFunctionInputDecryptorFactory({ + buildDecryptor + }), + getAutomation: getAutomationFactory({ + db: projectDb + }), + getLatestAutomationRevision: getLatestAutomationRevisionFactory({ + db: projectDb + }), + getFullAutomationRevisionMetadata: getFullAutomationRevisionMetadataFactory({ + db: projectDb + }), + upsertAutomationRun: upsertAutomationRunFactory({ + db: projectDb + }), + getBranchLatestCommits: getBranchLatestCommitsFactory({ + db: projectDb + }), + validateStreamAccess + })({ projectId, automationId, userId }) + + const automationRunData = await getFullAutomationRunByIdFactory({ db: projectDb })( + automationRunId + ) + + if (!automationRunData) { + throw new Error('Failed to create test automation run!') + } + + const { triggers, functionRuns, ...automationRun } = automationRunData + + return { + automationRun, + functionRuns, + triggers + } +} export const truncateAutomations = async () => { await truncateTables([ From 04edd9c6af29aa3fd8dddf7ff751ccd8c744b52c Mon Sep 17 00:00:00 2001 From: Chuck Driesler Date: Thu, 6 Feb 2025 16:59:44 +0000 Subject: [PATCH 24/56] fix(regions): speed up inserts --- .../modules/workspaces/domain/operations.ts | 4 +- .../workspaces/graph/resolvers/regions.ts | 8 + .../workspaces/repositories/projectRegions.ts | 192 +++++++++--------- .../workspaces/services/projectRegions.ts | 9 +- .../tests/integration/projects.graph.spec.ts | 5 + packages/shared/src/environment/index.ts | 6 + 6 files changed, 124 insertions(+), 100 deletions(-) diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index 83ee87e85..66f0983c3 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -359,7 +359,7 @@ export type CopyWorkspace = (params: { workspaceId: string }) => Promise export type CopyProjects = (params: { projectIds: string[] }) => Promise export type CopyProjectModels = (params: { projectIds: string[] -}) => Promise> +}) => Promise> export type CopyProjectVersions = (params: { projectIds: string[] -}) => Promise> +}) => Promise> diff --git a/packages/server/modules/workspaces/graph/resolvers/regions.ts b/packages/server/modules/workspaces/graph/resolvers/regions.ts index 73770ec14..bb01c8195 100644 --- a/packages/server/modules/workspaces/graph/resolvers/regions.ts +++ b/packages/server/modules/workspaces/graph/resolvers/regions.ts @@ -29,6 +29,10 @@ import { getProjectFactory } from '@/modules/core/repositories/projects' import { getStreamBranchCountFactory } from '@/modules/core/repositories/branches' import { getStreamCommitCountFactory } from '@/modules/core/repositories/commits' import { withTransaction } from '@/modules/shared/helpers/dbHelper' +import { getFeatureFlags, isTestEnv } from '@/modules/shared/helpers/envHelper' +import { WorkspacesNotYetImplementedError } from '@/modules/workspaces/errors/workspace' + +const { FF_MOVE_PROJECT_REGION_ENABLED } = getFeatureFlags() export default { Workspace: { @@ -67,6 +71,10 @@ export default { }, WorkspaceProjectMutations: { moveToRegion: async (_parent, args, context) => { + if (!FF_MOVE_PROJECT_REGION_ENABLED && !isTestEnv()) { + throw new WorkspacesNotYetImplementedError() + } + await authorizeResolver( context.userId, args.projectId, diff --git a/packages/server/modules/workspaces/repositories/projectRegions.ts b/packages/server/modules/workspaces/repositories/projectRegions.ts index 94da98d14..4c7e5eee7 100644 --- a/packages/server/modules/workspaces/repositories/projectRegions.ts +++ b/packages/server/modules/workspaces/repositories/projectRegions.ts @@ -12,8 +12,10 @@ import { Commit } from '@/modules/core/domain/commits/types' import { Stream } from '@/modules/core/domain/streams/types' import { BranchCommitRecord, + CommitRecord, StreamCommitRecord, - StreamFavoriteRecord + StreamFavoriteRecord, + StreamRecord } from '@/modules/core/helpers/types' import { executeBatchedSelect } from '@/modules/shared/helpers/dbHelper' import { @@ -50,7 +52,11 @@ export const copyWorkspaceFactory = throw new WorkspaceNotFoundError() } - await tables.workspaces(deps.targetDb).insert(workspace) + await tables + .workspaces(deps.targetDb) + .insert(workspace) + .onConflict(Workspaces.withoutTablePrefix.col.id) + .merge(Workspaces.withoutTablePrefix.cols as (keyof Workspace)[]) return workspaceId } @@ -66,15 +72,15 @@ export const copyProjectsFactory = // Copy project record for await (const projects of executeBatchedSelect(selectProjects)) { - for (const project of projects) { - // Store copied project id - copiedProjectIds.push(project.id) - - // Copy `streams` row to target db - await tables.projects(deps.targetDb).insert(project).onConflict().ignore() - } - const projectIds = projects.map((project) => project.id) + copiedProjectIds.push(...projectIds) + + // Copy `streams` rows to target db + await tables + .projects(deps.targetDb) + .insert(projects) + .onConflict(Streams.withoutTablePrefix.col.id) + .merge(Streams.withoutTablePrefix.cols as (keyof StreamRecord)[]) // Fetch `stream_favorites` rows for projects in batch const selectStreamFavorites = tables @@ -83,14 +89,12 @@ export const copyProjectsFactory = .whereIn(StreamFavorites.col.streamId, projectIds) for await (const streamFavorites of executeBatchedSelect(selectStreamFavorites)) { - for (const streamFavorite of streamFavorites) { - // Copy `stream_favorites` row to target db - await tables - .streamFavorites(deps.targetDb) - .insert(streamFavorite) - .onConflict() - .ignore() - } + // Copy `stream_favorites` rows to target db + await tables + .streamFavorites(deps.targetDb) + .insert(streamFavorites) + .onConflict() + .ignore() } // Fetch `streams_meta` rows for projects in batch @@ -102,14 +106,12 @@ export const copyProjectsFactory = for await (const streamsMetadataBatch of executeBatchedSelect( selectStreamsMetadata )) { - for (const streamMetadata of streamsMetadataBatch) { - // Copy `streams_meta` row to target db - await tables - .streamsMeta(deps.targetDb) - .insert(streamMetadata) - .onConflict() - .ignore() - } + // Copy `streams_meta` rows to target db + await tables + .streamsMeta(deps.targetDb) + .insert(streamsMetadataBatch) + .onConflict() + .ignore() } } @@ -119,98 +121,98 @@ export const copyProjectsFactory = export const copyProjectModelsFactory = (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectModels => async ({ projectIds }) => { - const copiedModelIds: Record = projectIds.reduce( - (result, id) => ({ ...result, [id]: [] }), - {} - ) + const copiedModelCountByProjectId: Record = {} - for (const projectId of projectIds) { - const selectModels = tables - .models(deps.sourceDb) - .select('*') - .where({ streamId: projectId }) + // Fetch `branches` rows for projects in batch + const selectModels = tables + .models(deps.sourceDb) + .select('*') + .whereIn(Branches.col.streamId, projectIds) - for await (const models of executeBatchedSelect(selectModels)) { - for (const model of models) { - // Store copied model ids - copiedModelIds[projectId].push(model.id) + for await (const models of executeBatchedSelect(selectModels)) { + // Copy `branches` rows to target db + await tables.models(deps.targetDb).insert(models).onConflict().ignore() - // Copy `branches` row to target db - await tables.models(deps.targetDb).insert(model).onConflict().ignore() - } + for (const model of models) { + copiedModelCountByProjectId[model.streamId] ??= 0 + copiedModelCountByProjectId[model.streamId]++ } } - return copiedModelIds + return copiedModelCountByProjectId } export const copyProjectVersionsFactory = (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectVersions => async ({ projectIds }) => { - const copiedVersionIds: Record = projectIds.reduce( - (result, id) => ({ ...result, [id]: [] }), - {} - ) + const copiedVersionCountByProjectId: Record = {} - for (const projectId of projectIds) { - const selectVersions = tables - .streamCommits(deps.sourceDb) - .select('*') - .join( - Commits.name, - Commits.col.id, - StreamCommits.col.commitId - ) - .where({ streamId: projectId }) + const selectVersions = tables + .streamCommits(deps.sourceDb) + .select('*') + .join( + Commits.name, + Commits.col.id, + StreamCommits.col.commitId + ) + .whereIn(StreamCommits.col.streamId, projectIds) - for await (const versions of executeBatchedSelect(selectVersions)) { - for (const version of versions) { + for await (const versions of executeBatchedSelect(selectVersions)) { + const { commitIds, commits } = versions.reduce( + (all, version) => { const { commitId, streamId, ...commit } = version - // Store copied version id - copiedVersionIds[streamId].push(commitId) + all.commitIds.push(commitId) + all.streamIds.push(streamId) + all.commits.push(commit) - // Copy `commits` row to target db - await tables.versions(deps.targetDb).insert(commit).onConflict().ignore() + return all + }, + { commitIds: [], streamIds: [], commits: [] } as { + commitIds: string[] + streamIds: string[] + commits: CommitRecord[] } + ) - const commitIds = versions.map((version) => version.commitId) + // Copy `commits` rows to target db + await tables.versions(deps.targetDb).insert(commits).onConflict().ignore() - // Fetch `branch_commits` rows for versions in batch - const selectBranchCommits = tables - .branchCommits(deps.sourceDb) - .select('*') - .whereIn(BranchCommits.col.commitId, commitIds) + for (const version of versions) { + copiedVersionCountByProjectId[version.streamId] ??= 0 + copiedVersionCountByProjectId[version.streamId]++ + } - for await (const branchCommits of executeBatchedSelect(selectBranchCommits)) { - for (const branchCommit of branchCommits) { - // Copy `branch_commits` row to target db - await tables - .branchCommits(deps.targetDb) - .insert(branchCommit) - .onConflict() - .ignore() - } - } + // Fetch `branch_commits` rows for versions in batch + const selectBranchCommits = tables + .branchCommits(deps.sourceDb) + .select('*') + .whereIn(BranchCommits.col.commitId, commitIds) - // Fetch `stream_commits` rows for versions in batch - const selectStreamCommits = tables - .streamCommits(deps.sourceDb) - .select('*') - .whereIn(StreamCommits.col.commitId, commitIds) + for await (const branchCommits of executeBatchedSelect(selectBranchCommits)) { + // Copy `branch_commits` row to target db + await tables + .branchCommits(deps.targetDb) + .insert(branchCommits) + .onConflict() + .ignore() + } - for await (const streamCommits of executeBatchedSelect(selectStreamCommits)) { - for (const streamCommit of streamCommits) { - // Copy `stream_commits` row to target db - await tables - .streamCommits(deps.targetDb) - .insert(streamCommit) - .onConflict() - .ignore() - } - } + // Fetch `stream_commits` rows for versions in batch + const selectStreamCommits = tables + .streamCommits(deps.sourceDb) + .select('*') + .whereIn(StreamCommits.col.commitId, commitIds) + + for await (const streamCommits of executeBatchedSelect(selectStreamCommits)) { + // Copy `stream_commits` row to target db + await tables + .streamCommits(deps.targetDb) + .insert(streamCommits) + .onConflict() + .ignore() } } - return copiedVersionIds + return copiedVersionCountByProjectId } diff --git a/packages/server/modules/workspaces/services/projectRegions.ts b/packages/server/modules/workspaces/services/projectRegions.ts index 561aee792..2bc60cd66 100644 --- a/packages/server/modules/workspaces/services/projectRegions.ts +++ b/packages/server/modules/workspaces/services/projectRegions.ts @@ -70,9 +70,12 @@ export const updateProjectRegionFactory = const sourceProjectModelCount = await deps.countProjectModels(projectId) const sourceProjectVersionCount = await deps.countProjectVersions(projectId) - const isReconciled = - modelIds[projectId].length === sourceProjectModelCount && - versionIds[projectId].length === sourceProjectVersionCount + const tests = [ + modelIds[projectId] === sourceProjectModelCount, + versionIds[projectId] === sourceProjectVersionCount + ] + + const isReconciled = tests.every((test) => !!test) if (!isReconciled) { // TODO: Move failed or source project added data while changing regions. Retry move. diff --git a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts index 9b33dc0ad..985e1c6ee 100644 --- a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts @@ -382,6 +382,7 @@ isMultiRegionTestMode() expect(res).to.not.haveGraphQLErrors() + // TODO: Replace with gql query when possible const project = await targetRegionDb .table('streams') .select('*') @@ -399,6 +400,7 @@ isMultiRegionTestMode() expect(res).to.not.haveGraphQLErrors() + // TODO: Replace with gql query when possible const branch = await targetRegionDb .table('branches') .select('*') @@ -416,6 +418,7 @@ isMultiRegionTestMode() expect(res).to.not.haveGraphQLErrors() + // TODO: Replace with gql query when possible const version = await targetRegionDb .table('commits') .select('*') @@ -423,6 +426,7 @@ isMultiRegionTestMode() .first() expect(version).to.not.be.undefined + // TODO: Replace with gql query when possible const streamCommitsRecord = await targetRegionDb .table('stream_commits') .select('*') @@ -430,6 +434,7 @@ isMultiRegionTestMode() .first() expect(streamCommitsRecord).to.not.be.undefined + // TODO: Replace with gql query when possible const branchCommitsRecord = await targetRegionDb .table('branch_commits') .select('*') diff --git a/packages/shared/src/environment/index.ts b/packages/shared/src/environment/index.ts index 0d35eacdb..d81d9fc27 100644 --- a/packages/shared/src/environment/index.ts +++ b/packages/shared/src/environment/index.ts @@ -70,6 +70,11 @@ const parseFeatureFlags = () => { FF_OBJECTS_STREAMING_FIX: { schema: z.boolean(), defaults: { production: false, _: false } + }, + // Enables endpoint(s) for updating a project's region + FF_MOVE_PROJECT_REGION_ENABLED: { + schema: z.boolean(), + defaults: { production: false, _: true } } }) @@ -98,6 +103,7 @@ export function getFeatureFlags(): { FF_FORCE_EMAIL_VERIFICATION: boolean FF_FORCE_ONBOARDING: boolean FF_OBJECTS_STREAMING_FIX: boolean + FF_MOVE_PROJECT_REGION_ENABLED: boolean } { if (!parsedFlags) parsedFlags = parseFeatureFlags() return parsedFlags From 72167e6194c7823729835ef1fb24c23c8841daf8 Mon Sep 17 00:00:00 2001 From: Chuck Driesler Date: Fri, 7 Feb 2025 14:55:33 +0000 Subject: [PATCH 25/56] fix(regions): simplify postgres usage --- .../modules/workspaces/domain/operations.ts | 2 +- .../workspaces/repositories/projectRegions.ts | 78 ++++++------------- .../workspaces/services/projectRegions.ts | 12 +-- 3 files changed, 32 insertions(+), 60 deletions(-) diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index 1f57d9f66..7039c2463 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -365,4 +365,4 @@ export type CopyProjectVersions = (params: { }) => Promise> export type CopyProjectObjects = (params: { projectIds: string[] -}) => Promise> +}) => Promise> diff --git a/packages/server/modules/workspaces/repositories/projectRegions.ts b/packages/server/modules/workspaces/repositories/projectRegions.ts index a09163619..07a92d631 100644 --- a/packages/server/modules/workspaces/repositories/projectRegions.ts +++ b/packages/server/modules/workspaces/repositories/projectRegions.ts @@ -249,68 +249,40 @@ export const copyProjectVersionsFactory = /** * Copies rows from the following tables: * - objects - * - object_children_closure * - object_preview */ export const copyProjectObjectsFactory = (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectObjects => async ({ projectIds }) => { - const copiedObjectIds: Record = {} + const copiedObjectCountByProjectId: Record = {} - for (const projectId of projectIds) { - copiedObjectIds[projectId] = [] + // Copy `objects` table rows in batches + const selectObjects = tables + .objects(deps.sourceDb) + .select('*') + .whereIn(Objects.col.streamId, projectIds) + .orderBy(Objects.col.id) - // Copy `objects` table rows in batches - const selectObjects = tables - .objects(deps.sourceDb) - .select('*') - .where(Objects.col.streamId, projectId) - .orderBy(Objects.col.id) + for await (const objects of executeBatchedSelect(selectObjects)) { + // Write `objects` table rows to target db + await tables.objects(deps.targetDb).insert(objects).onConflict().ignore() - for await (const objects of executeBatchedSelect(selectObjects)) { - for (const object of objects) { - // Store copied object ids by source project - copiedObjectIds[projectId].push(object.id) - - // Write `objects` table row to target db - await tables.objects(deps.targetDb).insert(object).onConflict().ignore() - } - } - - // Copy `object_children_closure` rows in batches - const selectObjectClosures = tables - .objectClosures(deps.sourceDb) - .select('*') - .where('streamId', projectId) - - for await (const closures of executeBatchedSelect(selectObjectClosures)) { - for (const closure of closures) { - // Write `object_children_closure` row to target db - await tables - .objectClosures(deps.targetDb) - .insert(closure) - .onConflict() - .ignore() - } - } - - // Copy `object_preview` rows in batches - const selectObjectPreviews = tables - .objectPreviews(deps.sourceDb) - .select('*') - .where('streamId', projectId) - - for await (const previews of executeBatchedSelect(selectObjectPreviews)) { - for (const preview of previews) { - // Write `object_preview` row to target db - await tables - .objectPreviews(deps.targetDb) - .insert(preview) - .onConflict() - .ignore() - } + for (const object of objects) { + copiedObjectCountByProjectId[object.streamId] ??= 0 + copiedObjectCountByProjectId[object.streamId]++ } } - return copiedObjectIds + // Copy `object_preview` rows in batches + const selectObjectPreviews = tables + .objectPreviews(deps.sourceDb) + .select('*') + .whereIn('streamId', projectIds) + + for await (const previews of executeBatchedSelect(selectObjectPreviews)) { + // Write `object_preview` rows to target db + await tables.objectPreviews(deps.targetDb).insert(previews).onConflict().ignore() + } + + return copiedObjectCountByProjectId } diff --git a/packages/server/modules/workspaces/services/projectRegions.ts b/packages/server/modules/workspaces/services/projectRegions.ts index e318c829e..84f5688ca 100644 --- a/packages/server/modules/workspaces/services/projectRegions.ts +++ b/packages/server/modules/workspaces/services/projectRegions.ts @@ -61,11 +61,11 @@ export const updateProjectRegionFactory = // Move commits const projectIds = await deps.copyProjects({ projectIds: [projectId] }) - const modelIds = await deps.copyProjectModels({ projectIds }) - const versionIds = await deps.copyProjectVersions({ projectIds }) + const copiedModelCount = await deps.copyProjectModels({ projectIds }) + const copiedVersionCount = await deps.copyProjectVersions({ projectIds }) // Move objects - const objectIds = await deps.copyProjectObjects({ projectIds }) + const copiedObjectCount = await deps.copyProjectObjects({ projectIds }) // TODO: Move automations // TODO: Move comments @@ -80,9 +80,9 @@ export const updateProjectRegionFactory = }) const tests = [ - modelIds[projectId] === sourceProjectModelCount, - versionIds[projectId] === sourceProjectVersionCount, - objectIds[projectId].length === sourceProjectObjectCount + copiedModelCount[projectId] === sourceProjectModelCount, + copiedVersionCount[projectId] === sourceProjectVersionCount, + copiedObjectCount[projectId] === sourceProjectObjectCount ] if (!tests.every((test) => !!test)) { From 25eacc30529a6b2f963d6a7037b75e581053735b Mon Sep 17 00:00:00 2001 From: Chuck Driesler Date: Fri, 7 Feb 2025 16:05:07 +0000 Subject: [PATCH 26/56] chore(regions): repair build --- .../modules/automate/tests/automations.spec.ts | 8 +++++--- .../workspaces/repositories/projectRegions.ts | 3 --- .../test/speckle-helpers/automationHelper.ts | 16 ++++++---------- 3 files changed, 11 insertions(+), 16 deletions(-) diff --git a/packages/server/modules/automate/tests/automations.spec.ts b/packages/server/modules/automate/tests/automations.spec.ts index cb916dcd0..a0cf32e0e 100644 --- a/packages/server/modules/automate/tests/automations.spec.ts +++ b/packages/server/modules/automate/tests/automations.spec.ts @@ -516,9 +516,11 @@ const buildAutomationUpdate = () => { it('fails when refering to nonexistent function releases', async () => { const create = buildAutomationRevisionCreate({ - getFunctionRelease: async () => { - // TODO: Update once we know how exec engine should respond - throw new Error('Function release with ID XXX not found') + overrides: { + getFunctionRelease: async () => { + // TODO: Update once we know how exec engine should respond + throw new Error('Function release with ID XXX not found') + } } }) diff --git a/packages/server/modules/workspaces/repositories/projectRegions.ts b/packages/server/modules/workspaces/repositories/projectRegions.ts index 341102921..31c11e5dc 100644 --- a/packages/server/modules/workspaces/repositories/projectRegions.ts +++ b/packages/server/modules/workspaces/repositories/projectRegions.ts @@ -21,7 +21,6 @@ import { Commit } from '@/modules/core/domain/commits/types' import { Stream } from '@/modules/core/domain/streams/types' import { BranchCommitRecord, - ObjectChildrenClosureRecord, ObjectRecord, CommitRecord, StreamCommitRecord, @@ -63,8 +62,6 @@ const tables = { streamFavorites: (db: Knex) => db(StreamFavorites.name), streamsMeta: (db: Knex) => db(StreamsMeta.name), objects: (db: Knex) => db(Objects.name), - objectClosures: (db: Knex) => - db('object_children_closure'), objectPreviews: (db: Knex) => db('object_preview'), automations: (db: Knex) => db(Automations.name), automationTokens: (db: Knex) => db(AutomationTokens.name), diff --git a/packages/server/test/speckle-helpers/automationHelper.ts b/packages/server/test/speckle-helpers/automationHelper.ts index 809a9bc5d..a9b99b3d3 100644 --- a/packages/server/test/speckle-helpers/automationHelper.ts +++ b/packages/server/test/speckle-helpers/automationHelper.ts @@ -62,15 +62,13 @@ export const generateFunctionReleaseId = () => cryptoRandomString({ length: 10 } */ export const buildAutomationCreate = ( params: { - dbClient: Knex + dbClient?: Knex overrides?: Partial<{ createDbAutomation: typeof clientCreateAutomation }> - } = { - dbClient: db - } + } = {} ) => { - const { dbClient, overrides } = params + const { dbClient = db, overrides } = params const create = createAutomationFactory({ createAuthCode: createStoredAuthCodeFactory({ redis: createInmemoryRedisClient() }), @@ -94,13 +92,11 @@ export const buildAutomationCreate = ( */ export const buildAutomationRevisionCreate = ( params: { - dbClient: Knex + dbClient?: Knex overrides?: Partial - } = { - dbClient: db - } + } = {} ) => { - const { dbClient, overrides } = params + const { dbClient = db, overrides } = params const fakeGetRelease = (params: { functionReleaseId: string From 78e8ff8c32eee9f19a749d184d1b7dfae0e0463e Mon Sep 17 00:00:00 2001 From: Chuck Driesler Date: Fri, 7 Feb 2025 16:25:20 +0000 Subject: [PATCH 27/56] fix(regions): improve queries --- .../modules/workspaces/domain/operations.ts | 2 +- .../workspaces/repositories/projectRegions.ts | 236 ++++++++---------- .../workspaces/services/projectRegions.ts | 4 +- 3 files changed, 113 insertions(+), 129 deletions(-) diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index a8b57f911..6252c389e 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -368,4 +368,4 @@ export type CopyProjectObjects = (params: { }) => Promise> export type CopyProjectAutomations = (params: { projectIds: string[] -}) => Promise> +}) => Promise> diff --git a/packages/server/modules/workspaces/repositories/projectRegions.ts b/packages/server/modules/workspaces/repositories/projectRegions.ts index 31c11e5dc..9423b4a4e 100644 --- a/packages/server/modules/workspaces/repositories/projectRegions.ts +++ b/packages/server/modules/workspaces/repositories/projectRegions.ts @@ -330,148 +330,132 @@ export const copyProjectObjectsFactory = export const copyProjectAutomationsFactory = (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectAutomations => async ({ projectIds }) => { - const copiedAutomationIds: Record = {} + const copiedAutomationCountByProjectId: Record = {} - for (const projectId of projectIds) { - copiedAutomationIds[projectId] = [] + // Copy `automations` table rows in batches + const selectAutomations = tables + .automations(deps.sourceDb) + .select('*') + .whereIn(Automations.col.projectId, projectIds) - // Copy `automations` table rows in batches - const selectAutomations = tables - .automations(deps.sourceDb) + for await (const automations of executeBatchedSelect(selectAutomations)) { + const automationIds = automations.map((automation) => automation.id) + + // Write `automations` table rows to target db + await tables + .automations(deps.targetDb) + // Cast ignores unexpected behavior in how knex handles object union types + .insert(automations as unknown as AutomationRecord) + .onConflict() + .ignore() + + for (const automation of automations) { + copiedAutomationCountByProjectId[automation.projectId] ??= 0 + copiedAutomationCountByProjectId[automation.projectId]++ + } + + // Copy `automation_tokens` rows for automation + const selectAutomationTokens = tables + .automationTokens(deps.sourceDb) .select('*') - .where(Automations.col.projectId, projectId) + .whereIn(AutomationTokens.col.automationId, automationIds) - for await (const automations of executeBatchedSelect(selectAutomations)) { - for (const automation of automations) { - // Store copied automation id - copiedAutomationIds[projectId].push(automation.id) + for await (const tokens of executeBatchedSelect(selectAutomationTokens)) { + // Write `automation_tokens` row to target db + await tables + .automationTokens(deps.targetDb) + .insert(tokens) + .onConflict() + .ignore() + } - // Write `automations` table row to target db + // Copy `automation_revisions` rows for automation + const selectAutomationRevisions = tables + .automationRevisions(deps.sourceDb) + .select('*') + .whereIn(AutomationRevisions.col.automationId, automationIds) + + for await (const automationRevisions of executeBatchedSelect( + selectAutomationRevisions + )) { + const automationRevisionIds = automationRevisions.map((revision) => revision.id) + + // Write `automation_revisions` rows to target db + await tables + .automationRevisions(deps.targetDb) + .insert(automationRevisions) + .onConflict() + .ignore() + + // Copy `automation_triggers` rows for automation revisions + const automationTriggers = await tables + .automationTriggers(deps.sourceDb) + .select('*') + .whereIn(AutomationTriggers.col.automationRevisionId, automationRevisionIds) + + await tables + .automationTriggers(deps.targetDb) + .insert(automationTriggers) + .onConflict() + .ignore() + + // Copy `automation_revision_functions` rows for automation revisions + const automationRevisionFunctions = await tables + .automationRevisionFunctions(deps.sourceDb) + .select('*') + .whereIn( + AutomationRevisionFunctions.col.automationRevisionId, + automationRevisionIds + ) + + await tables + .automationRevisionFunctions(deps.targetDb) + .insert(automationRevisionFunctions) + .onConflict() + .ignore() + + // Copy `automation_runs` rows for automation revision + const selectAutomationRuns = tables + .automationRuns(deps.sourceDb) + .select('*') + .whereIn(AutomationRuns.col.automationRevisionId, automationRevisionIds) + + for await (const automationRuns of executeBatchedSelect(selectAutomationRuns)) { + const automationRunIds = automationRuns.map((run) => run.id) + + // Write `automation_runs` row to target db await tables - .automations(deps.targetDb) - .insert(automation) + .automationRuns(deps.targetDb) + .insert(automationRuns) .onConflict() .ignore() - // Copy `automation_tokens` rows for automation - const selectAutomationTokens = tables - .automationTokens(deps.sourceDb) + // Copy `automation_run_triggers` rows for automation run + const automationRunTriggers = await tables + .automationRunTriggers(deps.sourceDb) .select('*') - .where(AutomationTokens.col.automationId, automation.id) + .whereIn(AutomationRunTriggers.col.automationRunId, automationRunIds) - for await (const tokens of executeBatchedSelect(selectAutomationTokens)) { - for (const token of tokens) { - // Write `automation_tokens` row to target db - await tables - .automationTokens(deps.targetDb) - .insert(token) - .onConflict() - .ignore() - } - } + await tables + .automationRunTriggers(deps.targetDb) + .insert(automationRunTriggers) + .onConflict() + .ignore() - // Copy `automation_revisions` rows for automation - const selectAutomationRevisions = tables - .automationRevisions(deps.sourceDb) + // Copy `automation_function_runs` rows for automation run + const automationFunctionRuns = await tables + .automationFunctionRuns(deps.sourceDb) .select('*') - .where(AutomationRevisions.col.automationId, automation.id) + .whereIn(AutomationFunctionRuns.col.runId, automationRunIds) - for await (const automationRevisions of executeBatchedSelect( - selectAutomationRevisions - )) { - for (const automationRevision of automationRevisions) { - // Write `automation_revisions` row to target db - await tables - .automationRevisions(deps.targetDb) - .insert(automationRevision) - .onConflict() - .ignore() - - // Copy `automation_triggers` rows for automation revision - const automationTriggers = await tables - .automationTriggers(deps.sourceDb) - .select('*') - .where( - AutomationTriggers.col.automationRevisionId, - automationRevision.id - ) - - for (const automationTrigger of automationTriggers) { - await tables - .automationTriggers(deps.targetDb) - .insert(automationTrigger) - .onConflict() - .ignore() - } - - // Copy `automation_revision_functions` rows for automation revision - const automationRevisionFunctions = await tables - .automationRevisionFunctions(deps.sourceDb) - .select('*') - .where( - AutomationRevisionFunctions.col.automationRevisionId, - automationRevision.id - ) - - for (const automationRevisionFunction of automationRevisionFunctions) { - await tables - .automationRevisionFunctions(deps.targetDb) - .insert(automationRevisionFunction) - .onConflict() - .ignore() - } - - // Copy `automation_runs` rows for automation revision - const selectAutomationRuns = tables - .automationRuns(deps.sourceDb) - .select('*') - .where(AutomationRuns.col.automationRevisionId, automationRevision.id) - - for await (const automationRuns of executeBatchedSelect( - selectAutomationRuns - )) { - for (const automationRun of automationRuns) { - // Write `automation_runs` row to target db - await tables - .automationRuns(deps.targetDb) - .insert(automationRun) - .onConflict() - .ignore() - - // Copy `automation_run_triggers` rows for automation run - const automationRunTriggers = await tables - .automationRunTriggers(deps.sourceDb) - .select('*') - .where(AutomationRunTriggers.col.automationRunId, automationRun.id) - - for (const automationRunTrigger of automationRunTriggers) { - await tables - .automationRunTriggers(deps.targetDb) - .insert(automationRunTrigger) - .onConflict() - .ignore() - } - - // Copy `automation_function_runs` rows for automation run - const automationFunctionRuns = await tables - .automationFunctionRuns(deps.sourceDb) - .select('*') - .where(AutomationFunctionRuns.col.runId, automationRun.id) - - for (const automationFunctionRun of automationFunctionRuns) { - await tables - .automationFunctionRuns(deps.targetDb) - .insert(automationFunctionRun) - .onConflict() - .ignore() - } - } - } - } - } + await tables + .automationFunctionRuns(deps.targetDb) + .insert(automationFunctionRuns) + .onConflict() + .ignore() } } } - return copiedAutomationIds + return copiedAutomationCountByProjectId } diff --git a/packages/server/modules/workspaces/services/projectRegions.ts b/packages/server/modules/workspaces/services/projectRegions.ts index 869bf5c67..78cc9b7cf 100644 --- a/packages/server/modules/workspaces/services/projectRegions.ts +++ b/packages/server/modules/workspaces/services/projectRegions.ts @@ -72,7 +72,7 @@ export const updateProjectRegionFactory = const copiedObjectCount = await deps.copyProjectObjects({ projectIds }) // Move automations - const automationIds = await deps.copyProjectAutomations({ projectIds }) + const copiedAutomationCount = await deps.copyProjectAutomations({ projectIds }) // TODO: Move comments // TODO: Move file blobs @@ -92,7 +92,7 @@ export const updateProjectRegionFactory = copiedModelCount[projectId] === sourceProjectModelCount, copiedVersionCount[projectId] === sourceProjectVersionCount, copiedObjectCount[projectId] === sourceProjectObjectCount, - automationIds[projectId].length === sourceProjectAutomationCount + copiedAutomationCount[projectId] === sourceProjectAutomationCount ] if (!tests.every((test) => !!test)) { From 613eb3c2d78d4779a775b3ae6466afc93f5d650e Mon Sep 17 00:00:00 2001 From: Chuck Driesler Date: Fri, 7 Feb 2025 17:03:42 +0000 Subject: [PATCH 28/56] chore(regions): failing test for moving comments --- .../tests/integration/projects.graph.spec.ts | 33 +++++++- .../test/speckle-helpers/commentHelper.ts | 78 +++++++++++++++++++ 2 files changed, 109 insertions(+), 2 deletions(-) create mode 100644 packages/server/test/speckle-helpers/commentHelper.ts diff --git a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts index 635142871..9cf6d2971 100644 --- a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts @@ -9,6 +9,7 @@ import { AutomationTokenRecord, AutomationTriggerDefinitionRecord } from '@/modules/automate/helpers/types' +import { CommentRecord } from '@/modules/comments/helpers/types' import { AutomationFunctionRuns, AutomationRevisionFunctions, @@ -16,7 +17,8 @@ import { AutomationRuns, AutomationRunTriggers, AutomationTokens, - AutomationTriggers + AutomationTriggers, + Comments } from '@/modules/core/dbSchema' import { AllScopes } from '@/modules/core/helpers/mainConstants' import { createRandomEmail } from '@/modules/core/helpers/testHelpers' @@ -59,6 +61,7 @@ import { createTestAutomationRun } from '@/test/speckle-helpers/automationHelper' import { BasicTestBranch, createTestBranch } from '@/test/speckle-helpers/branchHelper' +import { createTestComment } from '@/test/speckle-helpers/commentHelper' import { BasicTestCommit, createTestCommit, @@ -94,7 +97,8 @@ const tables = { automationRunTriggers: (db: Knex) => db(AutomationRunTriggers.name), automationFunctionRuns: (db: Knex) => - db(AutomationFunctionRuns.name) + db(AutomationFunctionRuns.name), + comments: (db: Knex) => db.table(Comments.name) } const grantStreamPermissions = grantStreamPermissionsFactory({ db }) @@ -386,6 +390,8 @@ isMultiRegionTestMode() let testAutomationRun: AutomationRunRecord let testAutomationFunctionRuns: AutomationFunctionRunRecord[] + let testComment: CommentRecord + let apollo: TestApolloServer let targetRegionDb: Knex @@ -450,6 +456,12 @@ isMultiRegionTestMode() testAutomationRun = automationRun testAutomationFunctionRuns = functionRuns + + testComment = await createTestComment({ + userId: adminUser.id, + projectId: testProject.id, + objectId: testVersion.objectId + }) }) it('moves project record to target regional db', async () => { @@ -612,5 +624,22 @@ isMultiRegionTestMode() ) ) }) + + it('moves project comments to target regional db', async () => { + const res = await apollo.execute(UpdateProjectRegionDocument, { + projectId: testProject.id, + regionKey: regionKey2 + }) + + expect(res).to.not.haveGraphQLErrors() + + const comment = await tables + .comments(targetRegionDb) + .select('*') + .where({ id: testComment.id }) + .first() + + expect(comment).to.not.be.undefined + }) }) : void 0 diff --git a/packages/server/test/speckle-helpers/commentHelper.ts b/packages/server/test/speckle-helpers/commentHelper.ts new file mode 100644 index 000000000..fd51f327e --- /dev/null +++ b/packages/server/test/speckle-helpers/commentHelper.ts @@ -0,0 +1,78 @@ +import { getBlobsFactory } from '@/modules/blobstorage/repositories' +import { CommentRecord } from '@/modules/comments/helpers/types' +import { + insertCommentLinksFactory, + insertCommentsFactory, + markCommentViewedFactory +} from '@/modules/comments/repositories/comments' +import { validateInputAttachmentsFactory } from '@/modules/comments/services/commentTextService' +import { createCommentThreadAndNotifyFactory } from '@/modules/comments/services/management' +import { + getBranchLatestCommitsFactory, + getStreamBranchesByNameFactory +} from '@/modules/core/repositories/branches' +import { + getAllBranchCommitsFactory, + getSpecificBranchCommitsFactory +} from '@/modules/core/repositories/commits' +import { getStreamObjectsFactory } from '@/modules/core/repositories/objects' +import { + getViewerResourceGroupsFactory, + getViewerResourceItemsUngroupedFactory +} from '@/modules/core/services/commit/viewerResources' +import { getProjectDbClient } from '@/modules/multiregion/utils/dbSelector' +import cryptoRandomString from 'crypto-random-string' + +export const createTestComment = async (params: { + userId: string + projectId: string + objectId: string +}): Promise => { + const { userId, projectId, objectId } = params + + const projectDb = await getProjectDbClient({ projectId }) + + const createComment = createCommentThreadAndNotifyFactory({ + getViewerResourceItemsUngrouped: getViewerResourceItemsUngroupedFactory({ + getViewerResourceGroups: getViewerResourceGroupsFactory({ + getStreamObjects: getStreamObjectsFactory({ db: projectDb }), + getBranchLatestCommits: getBranchLatestCommitsFactory({ db: projectDb }), + getStreamBranchesByName: getStreamBranchesByNameFactory({ db: projectDb }), + getSpecificBranchCommits: getSpecificBranchCommitsFactory({ db: projectDb }), + getAllBranchCommits: getAllBranchCommitsFactory({ db: projectDb }) + }) + }), + validateInputAttachments: validateInputAttachmentsFactory({ + getBlobs: getBlobsFactory({ db: projectDb }) + }), + insertComments: insertCommentsFactory({ db: projectDb }), + insertCommentLinks: insertCommentLinksFactory({ db: projectDb }), + markCommentViewed: markCommentViewedFactory({ db: projectDb }), + emitEvent: async () => {}, + addCommentCreatedActivity: async () => {} + }) + + return await createComment( + { + content: { + doc: { + type: 'doc', + content: [ + { + type: 'paragraph', + content: [ + { + type: 'text', + text: cryptoRandomString({ length: 9 }) + } + ] + } + ] + } + }, + projectId, + resourceIdString: objectId + }, + userId + ) +} From c416484974bfaf9f3aaae1d56f79d8479d59a566 Mon Sep 17 00:00:00 2001 From: Chuck Driesler Date: Fri, 7 Feb 2025 17:23:23 +0000 Subject: [PATCH 29/56] feat(regions): move project comments --- .../modules/comments/domain/operations.ts | 5 ++ .../modules/workspaces/domain/operations.ts | 3 + .../workspaces/graph/resolvers/regions.ts | 6 +- .../workspaces/repositories/projectRegions.ts | 70 ++++++++++++++++++- .../workspaces/services/projectRegions.ts | 11 ++- 5 files changed, 91 insertions(+), 4 deletions(-) diff --git a/packages/server/modules/comments/domain/operations.ts b/packages/server/modules/comments/domain/operations.ts index 7fe16a57b..c45bb1f00 100644 --- a/packages/server/modules/comments/domain/operations.ts +++ b/packages/server/modules/comments/domain/operations.ts @@ -319,3 +319,8 @@ export type GetPaginatedProjectComments = ( items: CommentRecord[] cursor: string | null }> + +export type GetStreamCommentCount = ( + streamId: string, + options?: Partial<{ threadsOnly: boolean; includeArchived: boolean }> +) => Promise diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index 6252c389e..802fe7ee2 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -369,3 +369,6 @@ export type CopyProjectObjects = (params: { export type CopyProjectAutomations = (params: { projectIds: string[] }) => Promise> +export type CopyProjectComments = (params: { + projectIds: string[] +}) => Promise> diff --git a/packages/server/modules/workspaces/graph/resolvers/regions.ts b/packages/server/modules/workspaces/graph/resolvers/regions.ts index 1d7b890b8..ba24be96c 100644 --- a/packages/server/modules/workspaces/graph/resolvers/regions.ts +++ b/packages/server/modules/workspaces/graph/resolvers/regions.ts @@ -11,6 +11,7 @@ import { } from '@/modules/workspaces/repositories/regions' import { copyProjectAutomationsFactory, + copyProjectCommentsFactory, copyProjectModelsFactory, copyProjectObjectsFactory, copyProjectsFactory, @@ -35,6 +36,7 @@ import { getStreamObjectCountFactory } from '@/modules/core/repositories/objects import { getProjectAutomationsTotalCountFactory } from '@/modules/automate/repositories/automations' import { getFeatureFlags, isTestEnv } from '@/modules/shared/helpers/envHelper' import { WorkspacesNotYetImplementedError } from '@/modules/workspaces/errors/workspace' +import { getStreamCommentCountFactory } from '@/modules/comments/repositories/comments' const { FF_MOVE_PROJECT_REGION_ENABLED } = getFeatureFlags() @@ -97,6 +99,7 @@ export default { countProjectAutomations: getProjectAutomationsTotalCountFactory({ db: sourceDb }), + countProjectComments: getStreamCommentCountFactory({ db: sourceDb }), getAvailableRegions: getAvailableRegionsFactory({ getRegions: getRegionsFactory({ db }), canWorkspaceUseRegions: canWorkspaceUseRegionsFactory({ @@ -108,7 +111,8 @@ export default { copyProjectModels: copyProjectModelsFactory({ sourceDb, targetDb }), copyProjectVersions: copyProjectVersionsFactory({ sourceDb, targetDb }), copyProjectObjects: copyProjectObjectsFactory({ sourceDb, targetDb }), - copyProjectAutomations: copyProjectAutomationsFactory({ sourceDb, targetDb }) + copyProjectAutomations: copyProjectAutomationsFactory({ sourceDb, targetDb }), + copyProjectComments: copyProjectCommentsFactory({ sourceDb, targetDb }) }) return await withTransaction(updateProjectRegion(args), targetDb) diff --git a/packages/server/modules/workspaces/repositories/projectRegions.ts b/packages/server/modules/workspaces/repositories/projectRegions.ts index 9423b4a4e..b5268a383 100644 --- a/packages/server/modules/workspaces/repositories/projectRegions.ts +++ b/packages/server/modules/workspaces/repositories/projectRegions.ts @@ -9,6 +9,9 @@ import { AutomationTriggers, BranchCommits, Branches, + CommentLinks, + Comments, + CommentViews, Commits, Objects, StreamCommits, @@ -30,6 +33,7 @@ import { import { executeBatchedSelect } from '@/modules/shared/helpers/dbHelper' import { CopyProjectAutomations, + CopyProjectComments, CopyProjectModels, CopyProjectObjects, CopyProjects, @@ -51,6 +55,11 @@ import { AutomationTokenRecord, AutomationTriggerDefinitionRecord } from '@/modules/automate/helpers/types' +import { + CommentLinkRecord, + CommentRecord, + CommentViewRecord +} from '@/modules/comments/helpers/types' const tables = { workspaces: (db: Knex) => db(Workspaces.name), @@ -75,7 +84,10 @@ const tables = { automationRunTriggers: (db: Knex) => db(AutomationRunTriggers.name), automationFunctionRuns: (db: Knex) => - db(AutomationFunctionRuns.name) + db(AutomationFunctionRuns.name), + comments: (db: Knex) => db.table(Comments.name), + commentViews: (db: Knex) => db.table(CommentViews.name), + commentLinks: (db: Knex) => db.table(CommentLinks.name) } /** @@ -459,3 +471,59 @@ export const copyProjectAutomationsFactory = return copiedAutomationCountByProjectId } + +/** + * Copies rows from the following tables: + * - comments + * - comment_views + * - comment_links + */ +export const copyProjectCommentsFactory = + (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectComments => + async ({ projectIds }) => { + const copiedCommentCountByProjectId: Record = {} + + // Copy `comments` table rows in batches + const selectComments = tables + .comments(deps.sourceDb) + .select('*') + .whereIn(Comments.col.streamId, projectIds) + + for await (const comments of executeBatchedSelect(selectComments)) { + const commentIds = comments.map((comment) => comment.id) + + // Write `comments` rows to target db + await tables.comments(deps.targetDb).insert(comments).onConflict().ignore() + + for (const comment of comments) { + copiedCommentCountByProjectId[comment.streamId] ??= 0 + copiedCommentCountByProjectId[comment.streamId]++ + } + + // Copy `comment_views` table rows + const commentViews = await tables + .commentViews(deps.sourceDb) + .select('*') + .whereIn(CommentViews.col.commentId, commentIds) + + await tables + .commentViews(deps.targetDb) + .insert(commentViews) + .onConflict() + .ignore() + + // Copy `comment_links` table rows + const commentLinks = await tables + .commentLinks(deps.sourceDb) + .select('*') + .whereIn(CommentLinks.col.commentId, commentIds) + + await tables + .commentLinks(deps.targetDb) + .insert(commentLinks) + .onConflict() + .ignore() + } + + return copiedCommentCountByProjectId + } diff --git a/packages/server/modules/workspaces/services/projectRegions.ts b/packages/server/modules/workspaces/services/projectRegions.ts index 78cc9b7cf..fb4bdcd6d 100644 --- a/packages/server/modules/workspaces/services/projectRegions.ts +++ b/packages/server/modules/workspaces/services/projectRegions.ts @@ -1,10 +1,12 @@ import { GetProjectAutomationCount } from '@/modules/automate/domain/operations' +import { GetStreamCommentCount } from '@/modules/comments/domain/operations' import { GetStreamBranchCount } from '@/modules/core/domain/branches/operations' import { GetStreamCommitCount } from '@/modules/core/domain/commits/operations' import { GetStreamObjectCount } from '@/modules/core/domain/objects/operations' import { GetProject } from '@/modules/core/domain/projects/operations' import { CopyProjectAutomations, + CopyProjectComments, CopyProjectModels, CopyProjectObjects, CopyProjects, @@ -22,6 +24,7 @@ export const updateProjectRegionFactory = countProjectVersions: GetStreamCommitCount countProjectObjects: GetStreamObjectCount countProjectAutomations: GetProjectAutomationCount + countProjectComments: GetStreamCommentCount getAvailableRegions: GetAvailableRegions copyWorkspace: CopyWorkspace copyProjects: CopyProjects @@ -29,6 +32,7 @@ export const updateProjectRegionFactory = copyProjectVersions: CopyProjectVersions copyProjectObjects: CopyProjectObjects copyProjectAutomations: CopyProjectAutomations + copyProjectComments: CopyProjectComments }): UpdateProjectRegion => async (params) => { const { projectId, regionKey } = params @@ -74,7 +78,8 @@ export const updateProjectRegionFactory = // Move automations const copiedAutomationCount = await deps.copyProjectAutomations({ projectIds }) - // TODO: Move comments + // Move comments + const copiedCommentCount = await deps.copyProjectComments({ projectIds }) // TODO: Move file blobs // TODO: Move webhooks @@ -87,12 +92,14 @@ export const updateProjectRegionFactory = const sourceProjectAutomationCount = await deps.countProjectAutomations({ projectId }) + const sourceProjectCommentCount = await deps.countProjectComments(projectId) const tests = [ copiedModelCount[projectId] === sourceProjectModelCount, copiedVersionCount[projectId] === sourceProjectVersionCount, copiedObjectCount[projectId] === sourceProjectObjectCount, - copiedAutomationCount[projectId] === sourceProjectAutomationCount + copiedAutomationCount[projectId] === sourceProjectAutomationCount, + copiedCommentCount[projectId] === sourceProjectCommentCount ] if (!tests.every((test) => !!test)) { From 21bf57f514d5e9cb9ef433b4e4100651bfb4a50e Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Tue, 11 Feb 2025 00:43:15 +0000 Subject: [PATCH 30/56] fix(regions): move webhooks because they deserve love too --- .../modules/workspaces/domain/operations.ts | 3 ++ .../workspaces/graph/resolvers/regions.ts | 6 ++- .../workspaces/repositories/projectRegions.ts | 52 ++++++++++++++++++- .../workspaces/services/projectRegions.ts | 13 ++++- .../tests/integration/projects.graph.spec.ts | 50 +++++++++++++++++- 5 files changed, 119 insertions(+), 5 deletions(-) diff --git a/packages/server/modules/workspaces/domain/operations.ts b/packages/server/modules/workspaces/domain/operations.ts index 802fe7ee2..c31f877f6 100644 --- a/packages/server/modules/workspaces/domain/operations.ts +++ b/packages/server/modules/workspaces/domain/operations.ts @@ -372,3 +372,6 @@ export type CopyProjectAutomations = (params: { export type CopyProjectComments = (params: { projectIds: string[] }) => Promise> +export type CopyProjectWebhooks = (params: { + projectIds: string[] +}) => Promise> diff --git a/packages/server/modules/workspaces/graph/resolvers/regions.ts b/packages/server/modules/workspaces/graph/resolvers/regions.ts index ba24be96c..88d5dc8eb 100644 --- a/packages/server/modules/workspaces/graph/resolvers/regions.ts +++ b/packages/server/modules/workspaces/graph/resolvers/regions.ts @@ -16,6 +16,7 @@ import { copyProjectObjectsFactory, copyProjectsFactory, copyProjectVersionsFactory, + copyProjectWebhooksFactory, copyWorkspaceFactory } from '@/modules/workspaces/repositories/projectRegions' import { @@ -37,6 +38,7 @@ import { getProjectAutomationsTotalCountFactory } from '@/modules/automate/repos import { getFeatureFlags, isTestEnv } from '@/modules/shared/helpers/envHelper' import { WorkspacesNotYetImplementedError } from '@/modules/workspaces/errors/workspace' import { getStreamCommentCountFactory } from '@/modules/comments/repositories/comments' +import { getStreamWebhooksFactory } from '@/modules/webhooks/repositories/webhooks' const { FF_MOVE_PROJECT_REGION_ENABLED } = getFeatureFlags() @@ -100,6 +102,7 @@ export default { db: sourceDb }), countProjectComments: getStreamCommentCountFactory({ db: sourceDb }), + getProjectWebhooks: getStreamWebhooksFactory({ db: sourceDb }), getAvailableRegions: getAvailableRegionsFactory({ getRegions: getRegionsFactory({ db }), canWorkspaceUseRegions: canWorkspaceUseRegionsFactory({ @@ -112,7 +115,8 @@ export default { copyProjectVersions: copyProjectVersionsFactory({ sourceDb, targetDb }), copyProjectObjects: copyProjectObjectsFactory({ sourceDb, targetDb }), copyProjectAutomations: copyProjectAutomationsFactory({ sourceDb, targetDb }), - copyProjectComments: copyProjectCommentsFactory({ sourceDb, targetDb }) + copyProjectComments: copyProjectCommentsFactory({ sourceDb, targetDb }), + copyProjectWebhooks: copyProjectWebhooksFactory({ sourceDb, targetDb }) }) return await withTransaction(updateProjectRegion(args), targetDb) diff --git a/packages/server/modules/workspaces/repositories/projectRegions.ts b/packages/server/modules/workspaces/repositories/projectRegions.ts index b5268a383..154e0724a 100644 --- a/packages/server/modules/workspaces/repositories/projectRegions.ts +++ b/packages/server/modules/workspaces/repositories/projectRegions.ts @@ -38,6 +38,7 @@ import { CopyProjectObjects, CopyProjects, CopyProjectVersions, + CopyProjectWebhooks, CopyWorkspace } from '@/modules/workspaces/domain/operations' import { WorkspaceNotFoundError } from '@/modules/workspaces/errors/workspace' @@ -60,6 +61,7 @@ import { CommentRecord, CommentViewRecord } from '@/modules/comments/helpers/types' +import { Webhook, WebhookEvent } from '@/modules/webhooks/domain/types' const tables = { workspaces: (db: Knex) => db(Workspaces.name), @@ -87,7 +89,9 @@ const tables = { db(AutomationFunctionRuns.name), comments: (db: Knex) => db.table(Comments.name), commentViews: (db: Knex) => db.table(CommentViews.name), - commentLinks: (db: Knex) => db.table(CommentLinks.name) + commentLinks: (db: Knex) => db.table(CommentLinks.name), + webhooks: (db: Knex) => db.table('webhooks_config'), + webhookEvents: (db: Knex) => db.table('webhooks_events') } /** @@ -527,3 +531,49 @@ export const copyProjectCommentsFactory = return copiedCommentCountByProjectId } + +/** + * Copies rows from the following tables: + * - webhooks_config + * - webhooks_events + */ +export const copyProjectWebhooksFactory = + (deps: { sourceDb: Knex; targetDb: Knex }): CopyProjectWebhooks => + async ({ projectIds }) => { + const copiedWebhookCountByProjectId: Record = {} + + // Copy `webhooks_config` table rows in batches + const selectWebhooks = tables + .webhooks(deps.sourceDb) + .select('*') + .whereIn('streamId', projectIds) + + for await (const webhooks of executeBatchedSelect(selectWebhooks)) { + const webhookIds = webhooks.map((webhook) => webhook.id) + + // Write `webhooks_config` rows to target db + await tables.webhooks(deps.targetDb).insert(webhooks).onConflict().ignore() + + for (const webhook of webhooks) { + copiedWebhookCountByProjectId[webhook.streamId] ??= 0 + copiedWebhookCountByProjectId[webhook.streamId]++ + } + + // Copy `webhooks_events` table rows in batches + const selectWebhookEvents = tables + .webhookEvents(deps.sourceDb) + .select('*') + .whereIn('webhookId', webhookIds) + + for await (const webhookEvents of executeBatchedSelect(selectWebhookEvents)) { + // Write `webhooks_events` rows to target db + await tables + .webhookEvents(deps.targetDb) + .insert(webhookEvents) + .onConflict() + .ignore() + } + } + + return copiedWebhookCountByProjectId + } diff --git a/packages/server/modules/workspaces/services/projectRegions.ts b/packages/server/modules/workspaces/services/projectRegions.ts index fb4bdcd6d..2b8dce7b1 100644 --- a/packages/server/modules/workspaces/services/projectRegions.ts +++ b/packages/server/modules/workspaces/services/projectRegions.ts @@ -4,6 +4,7 @@ import { GetStreamBranchCount } from '@/modules/core/domain/branches/operations' import { GetStreamCommitCount } from '@/modules/core/domain/commits/operations' import { GetStreamObjectCount } from '@/modules/core/domain/objects/operations' import { GetProject } from '@/modules/core/domain/projects/operations' +import { GetStreamWebhooks } from '@/modules/webhooks/domain/operations' import { CopyProjectAutomations, CopyProjectComments, @@ -11,6 +12,7 @@ import { CopyProjectObjects, CopyProjects, CopyProjectVersions, + CopyProjectWebhooks, CopyWorkspace, GetAvailableRegions, UpdateProjectRegion @@ -25,6 +27,7 @@ export const updateProjectRegionFactory = countProjectObjects: GetStreamObjectCount countProjectAutomations: GetProjectAutomationCount countProjectComments: GetStreamCommentCount + getProjectWebhooks: GetStreamWebhooks getAvailableRegions: GetAvailableRegions copyWorkspace: CopyWorkspace copyProjects: CopyProjects @@ -33,6 +36,7 @@ export const updateProjectRegionFactory = copyProjectObjects: CopyProjectObjects copyProjectAutomations: CopyProjectAutomations copyProjectComments: CopyProjectComments + copyProjectWebhooks: CopyProjectWebhooks }): UpdateProjectRegion => async (params) => { const { projectId, regionKey } = params @@ -80,8 +84,11 @@ export const updateProjectRegionFactory = // Move comments const copiedCommentCount = await deps.copyProjectComments({ projectIds }) + + // Move webhooks + const copiedWebhookCount = await deps.copyProjectWebhooks({ projectIds }) + // TODO: Move file blobs - // TODO: Move webhooks // TODO: Validate state after move captures latest state of project const sourceProjectModelCount = await deps.countProjectModels(projectId) @@ -93,13 +100,15 @@ export const updateProjectRegionFactory = projectId }) const sourceProjectCommentCount = await deps.countProjectComments(projectId) + const sourceProjectWebhooks = await deps.getProjectWebhooks({ streamId: projectId }) const tests = [ copiedModelCount[projectId] === sourceProjectModelCount, copiedVersionCount[projectId] === sourceProjectVersionCount, copiedObjectCount[projectId] === sourceProjectObjectCount, copiedAutomationCount[projectId] === sourceProjectAutomationCount, - copiedCommentCount[projectId] === sourceProjectCommentCount + copiedCommentCount[projectId] === sourceProjectCommentCount, + copiedWebhookCount[projectId] === sourceProjectWebhooks.length ] if (!tests.every((test) => !!test)) { diff --git a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts index 9cf6d2971..49518969d 100644 --- a/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/projects.graph.spec.ts @@ -32,6 +32,11 @@ import { } from '@/modules/core/helpers/types' import { grantStreamPermissionsFactory } from '@/modules/core/repositories/streams' import { getDb } from '@/modules/multiregion/utils/dbSelector' +import { Webhook, WebhookEvent } from '@/modules/webhooks/domain/types' +import { + createWebhookConfigFactory, + createWebhookEventFactory +} from '@/modules/webhooks/repositories/webhooks' import { BasicTestWorkspace, createTestWorkspace @@ -98,7 +103,9 @@ const tables = { db(AutomationRunTriggers.name), automationFunctionRuns: (db: Knex) => db(AutomationFunctionRuns.name), - comments: (db: Knex) => db.table(Comments.name) + comments: (db: Knex) => db.table(Comments.name), + webhooks: (db: Knex) => db.table('webhooks_config'), + webhookEvents: (db: Knex) => db.table('webhooks_events') } const grantStreamPermissions = grantStreamPermissionsFactory({ db }) @@ -391,8 +398,10 @@ isMultiRegionTestMode() let testAutomationFunctionRuns: AutomationFunctionRunRecord[] let testComment: CommentRecord + let testWebhookId: string let apollo: TestApolloServer + let sourceRegionDb: Knex let targetRegionDb: Knex before(async () => { @@ -400,6 +409,7 @@ isMultiRegionTestMode() await waitForRegionUser(adminUser) apollo = await testApolloServer({ authUserId: adminUser.id }) + sourceRegionDb = await getDb({ regionKey: regionKey1 }) targetRegionDb = await getDb({ regionKey: regionKey2 }) }) @@ -462,6 +472,21 @@ isMultiRegionTestMode() projectId: testProject.id, objectId: testVersion.objectId }) + + testWebhookId = await createWebhookConfigFactory({ db: sourceRegionDb })({ + id: cryptoRandomString({ length: 9 }), + streamId: testProject.id, + url: 'https://example.org', + description: cryptoRandomString({ length: 9 }), + secret: cryptoRandomString({ length: 9 }), + enabled: false, + triggers: ['branch_create'] + }) + await createWebhookEventFactory({ db: sourceRegionDb })({ + id: cryptoRandomString({ length: 9 }), + webhookId: testWebhookId, + payload: cryptoRandomString({ length: 9 }) + }) }) it('moves project record to target regional db', async () => { @@ -641,5 +666,28 @@ isMultiRegionTestMode() expect(comment).to.not.be.undefined }) + + it('moves project webhooks to target regional db', async () => { + const res = await apollo.execute(UpdateProjectRegionDocument, { + projectId: testProject.id, + regionKey: regionKey2 + }) + + expect(res).to.not.haveGraphQLErrors() + + const webhook = await tables + .webhooks(targetRegionDb) + .select('*') + .where({ id: testWebhookId }) + .first() + expect(webhook).to.not.be.undefined + + const webhookEvent = await tables + .webhookEvents(targetRegionDb) + .select('*') + .where({ webhookId: testWebhookId }) + .first() + expect(webhookEvent).to.not.be.undefined + }) }) : void 0 From 979cc3cd95c4f2d1e9667dd34d82c327e618b08a Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Sun, 23 Feb 2025 19:15:31 +0000 Subject: [PATCH 31/56] chore(regions): lint --- packages/server/test/speckle-helpers/automationHelper.ts | 3 ++- packages/server/test/speckle-helpers/commentHelper.ts | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/server/test/speckle-helpers/automationHelper.ts b/packages/server/test/speckle-helpers/automationHelper.ts index a9b99b3d3..4e9272a5c 100644 --- a/packages/server/test/speckle-helpers/automationHelper.ts +++ b/packages/server/test/speckle-helpers/automationHelper.ts @@ -237,7 +237,8 @@ export const createTestAutomationRun = async (params: { getBranchLatestCommits: getBranchLatestCommitsFactory({ db: projectDb }), - validateStreamAccess + validateStreamAccess, + emitEvent: async () => {} })({ projectId, automationId, userId }) const automationRunData = await getFullAutomationRunByIdFactory({ db: projectDb })( diff --git a/packages/server/test/speckle-helpers/commentHelper.ts b/packages/server/test/speckle-helpers/commentHelper.ts index fd51f327e..d643c9aef 100644 --- a/packages/server/test/speckle-helpers/commentHelper.ts +++ b/packages/server/test/speckle-helpers/commentHelper.ts @@ -48,8 +48,7 @@ export const createTestComment = async (params: { insertComments: insertCommentsFactory({ db: projectDb }), insertCommentLinks: insertCommentLinksFactory({ db: projectDb }), markCommentViewed: markCommentViewedFactory({ db: projectDb }), - emitEvent: async () => {}, - addCommentCreatedActivity: async () => {} + emitEvent: async () => {} }) return await createComment( From 378e1ab1dc2b04f5a241e1bbb020c9c0edf32f86 Mon Sep 17 00:00:00 2001 From: Charles Driesler Date: Mon, 24 Feb 2025 16:38:15 +0000 Subject: [PATCH 32/56] chore(regions): 100 => 10 once again --- packages/server/modules/core/services/projects.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/modules/core/services/projects.ts b/packages/server/modules/core/services/projects.ts index 7a9196146..8c88dca4a 100644 --- a/packages/server/modules/core/services/projects.ts +++ b/packages/server/modules/core/services/projects.ts @@ -61,7 +61,7 @@ export const createNewProjectFactory = const replicatedProject = await getProject({ projectId }) if (!replicatedProject) throw new StreamNotFoundError() }, - { maxAttempts: 100 } + { maxAttempts: 10 } ) } catch (err) { if (err instanceof StreamNotFoundError) { From fd68c6ef2a496a2744ae65d57b73eccd9304b5d6 Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Tue, 25 Feb 2025 12:19:21 +0100 Subject: [PATCH 33/56] feat(workspaces): user workspace join requests (#4023) * feat(workspaces): user workspace join requests * chore(workspaces): return limited workspace * chore(workspaces): fix tests * chore(workspaces): add index for userId * chore(shared): fix totalcount on getpaginateditems * chore(workspaces): add workspace core resolvers to throw specific error --- .../lib/common/generated/gql/graphql.ts | 49 ++++ .../typedefs/workspaceJoinRequests.graphql | 29 ++ packages/server/codegen.yml | 1 + .../modules/core/graph/generated/graphql.ts | 59 +++- .../graph/generated/graphql.ts | 32 +++ .../modules/shared/services/paginatedItems.ts | 9 +- .../graph/resolvers/workspaceJoinRequests.ts | 260 +++++++++++------- .../repositories/workspaceJoinRequests.ts | 56 +++- .../workspaceJoinRequests.graph.spec.ts | 73 +++++ .../graph/resolvers/workspaceJoinRequests.ts | 47 ++++ .../workspacesCore/helpers/graphTypes.ts | 1 + ...6_index_user_id_workspace_join_requests.ts | 15 + .../server/test/graphql/generated/graphql.ts | 38 +++ packages/server/test/graphql/users.ts | 25 ++ 14 files changed, 581 insertions(+), 113 deletions(-) create mode 100644 packages/server/modules/workspacesCore/graph/resolvers/workspaceJoinRequests.ts create mode 100644 packages/server/modules/workspacesCore/migrations/20250219100906_index_user_id_workspace_join_requests.ts diff --git a/packages/frontend-2/lib/common/generated/gql/graphql.ts b/packages/frontend-2/lib/common/generated/gql/graphql.ts index 238ff3d07..9100e2e82 100644 --- a/packages/frontend-2/lib/common/generated/gql/graphql.ts +++ b/packages/frontend-2/lib/common/generated/gql/graphql.ts @@ -1153,6 +1153,22 @@ export type LimitedWorkspace = { slug: Scalars['String']['output']; }; +export type LimitedWorkspaceJoinRequest = { + __typename?: 'LimitedWorkspaceJoinRequest'; + createdAt: Scalars['DateTime']['output']; + id: Scalars['String']['output']; + status: WorkspaceJoinRequestStatus; + user: LimitedUser; + workspace: LimitedWorkspace; +}; + +export type LimitedWorkspaceJoinRequestCollection = { + __typename?: 'LimitedWorkspaceJoinRequestCollection'; + cursor?: Maybe; + items: Array; + totalCount: Scalars['Int']['output']; +}; + export type MarkCommentViewedInput = { commentId: Scalars['String']['input']; projectId: Scalars['String']['input']; @@ -3707,6 +3723,7 @@ export type User = { versions: CountOnlyCollection; /** Get all invitations to workspaces that the active user has */ workspaceInvites: Array; + workspaceJoinRequests?: Maybe; /** Get the workspaces for the user */ workspaces: WorkspaceCollection; }; @@ -3808,6 +3825,17 @@ export type UserVersionsArgs = { }; +/** + * Full user type, should only be used in the context of admin operations or + * when a user is reading/writing info about himself + */ +export type UserWorkspaceJoinRequestsArgs = { + cursor?: InputMaybe; + filter?: InputMaybe; + limit?: Scalars['Int']['input']; +}; + + /** * Full user type, should only be used in the context of admin operations or * when a user is reading/writing info about himself @@ -4420,6 +4448,10 @@ export type WorkspaceJoinRequestCollection = { totalCount: Scalars['Int']['output']; }; +export type WorkspaceJoinRequestFilter = { + status?: InputMaybe; +}; + export type WorkspaceJoinRequestMutations = { __typename?: 'WorkspaceJoinRequestMutations'; approve: Scalars['Boolean']['output']; @@ -6993,6 +7025,8 @@ export type AllObjectTypes = { LegacyCommentViewerData: LegacyCommentViewerData, LimitedUser: LimitedUser, LimitedWorkspace: LimitedWorkspace, + LimitedWorkspaceJoinRequest: LimitedWorkspaceJoinRequest, + LimitedWorkspaceJoinRequestCollection: LimitedWorkspaceJoinRequestCollection, Model: Model, ModelCollection: ModelCollection, ModelMutations: ModelMutations, @@ -7474,6 +7508,18 @@ export type LimitedWorkspaceFieldArgs = { name: {}, slug: {}, } +export type LimitedWorkspaceJoinRequestFieldArgs = { + createdAt: {}, + id: {}, + status: {}, + user: {}, + workspace: {}, +} +export type LimitedWorkspaceJoinRequestCollectionFieldArgs = { + cursor: {}, + items: {}, + totalCount: {}, +} export type ModelFieldArgs = { author: {}, automationsStatus: {}, @@ -8078,6 +8124,7 @@ export type UserFieldArgs = { verified: {}, versions: UserVersionsArgs, workspaceInvites: {}, + workspaceJoinRequests: UserWorkspaceJoinRequestsArgs, workspaces: UserWorkspacesArgs, } export type UserAutomateInfoFieldArgs = { @@ -8401,6 +8448,8 @@ export type AllObjectFieldArgTypes = { LegacyCommentViewerData: LegacyCommentViewerDataFieldArgs, LimitedUser: LimitedUserFieldArgs, LimitedWorkspace: LimitedWorkspaceFieldArgs, + LimitedWorkspaceJoinRequest: LimitedWorkspaceJoinRequestFieldArgs, + LimitedWorkspaceJoinRequestCollection: LimitedWorkspaceJoinRequestCollectionFieldArgs, Model: ModelFieldArgs, ModelCollection: ModelCollectionFieldArgs, ModelMutations: ModelMutationsFieldArgs, diff --git a/packages/server/assets/workspacesCore/typedefs/workspaceJoinRequests.graphql b/packages/server/assets/workspacesCore/typedefs/workspaceJoinRequests.graphql index 370ab50a1..53e8393d2 100644 --- a/packages/server/assets/workspacesCore/typedefs/workspaceJoinRequests.graphql +++ b/packages/server/assets/workspacesCore/typedefs/workspaceJoinRequests.graphql @@ -23,3 +23,32 @@ type WorkspaceJoinRequestMutations { @hasScope(scope: "workspace:update") @hasWorkspaceRole(role: ADMIN) } + +type LimitedWorkspaceJoinRequest { + id: String! + workspace: LimitedWorkspace! + user: LimitedUser! + status: WorkspaceJoinRequestStatus! + createdAt: DateTime! +} + +type LimitedWorkspaceJoinRequestCollection { + totalCount: Int! + cursor: String + items: [LimitedWorkspaceJoinRequest!]! +} + +input WorkspaceJoinRequestFilter { + status: WorkspaceJoinRequestStatus +} + +extend type User { + workspaceJoinRequests( + filter: WorkspaceJoinRequestFilter + cursor: String + limit: Int! = 25 + ): LimitedWorkspaceJoinRequestCollection + @hasServerRole(role: SERVER_GUEST) + @hasScope(scope: "workspace:read") + @isOwner +} diff --git a/packages/server/codegen.yml b/packages/server/codegen.yml index fcd4c7933..da3e0a63b 100644 --- a/packages/server/codegen.yml +++ b/packages/server/codegen.yml @@ -66,6 +66,7 @@ generates: PendingWorkspaceCollaborator: '@/modules/workspacesCore/helpers/graphTypes#PendingWorkspaceCollaboratorGraphQLReturn' WorkspaceCollaborator: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceCollaboratorGraphQLReturn' WorkspaceJoinRequest: '@/modules/workspacesCore/helpers/graphTypes#WorkspaceJoinRequestGraphQLReturn' + LimitedWorkspaceJoinRequest: '@/modules/workspacesCore/helpers/graphTypes#LimitedWorkspaceJoinRequestGraphQLReturn' Webhook: '@/modules/webhooks/helpers/graphTypes#WebhookGraphQLReturn' SmartTextEditorValue: '@/modules/core/services/richTextEditorService#SmartTextEditorValueGraphQLReturn' BlobMetadata: '@/modules/blobstorage/domain/types#BlobStorageItem' diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index 3874bb632..a3aca3949 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -5,7 +5,7 @@ import { CommentReplyAuthorCollectionGraphQLReturn, CommentGraphQLReturn } from import { PendingStreamCollaboratorGraphQLReturn } from '@/modules/serverinvites/helpers/graphTypes'; import { FileUploadGraphQLReturn } from '@/modules/fileuploads/helpers/types'; import { AutomateFunctionGraphQLReturn, AutomateFunctionReleaseGraphQLReturn, AutomationGraphQLReturn, AutomationRevisionGraphQLReturn, AutomationRevisionFunctionGraphQLReturn, AutomateRunGraphQLReturn, AutomationRunTriggerGraphQLReturn, AutomationRevisionTriggerDefinitionGraphQLReturn, AutomateFunctionRunGraphQLReturn, TriggeredAutomationsStatusGraphQLReturn, ProjectAutomationMutationsGraphQLReturn, ProjectTriggeredAutomationsStatusUpdatedMessageGraphQLReturn, ProjectAutomationsUpdatedMessageGraphQLReturn, UserAutomateInfoGraphQLReturn } from '@/modules/automate/helpers/graphTypes'; -import { WorkspaceGraphQLReturn, WorkspaceSsoGraphQLReturn, WorkspaceMutationsGraphQLReturn, WorkspaceJoinRequestMutationsGraphQLReturn, WorkspaceInviteMutationsGraphQLReturn, WorkspaceProjectMutationsGraphQLReturn, PendingWorkspaceCollaboratorGraphQLReturn, WorkspaceCollaboratorGraphQLReturn, WorkspaceJoinRequestGraphQLReturn, ProjectRoleGraphQLReturn } from '@/modules/workspacesCore/helpers/graphTypes'; +import { WorkspaceGraphQLReturn, WorkspaceSsoGraphQLReturn, WorkspaceMutationsGraphQLReturn, WorkspaceJoinRequestMutationsGraphQLReturn, WorkspaceInviteMutationsGraphQLReturn, WorkspaceProjectMutationsGraphQLReturn, PendingWorkspaceCollaboratorGraphQLReturn, WorkspaceCollaboratorGraphQLReturn, WorkspaceJoinRequestGraphQLReturn, LimitedWorkspaceJoinRequestGraphQLReturn, ProjectRoleGraphQLReturn } from '@/modules/workspacesCore/helpers/graphTypes'; import { WorkspaceBillingMutationsGraphQLReturn } from '@/modules/gatekeeper/helpers/graphTypes'; import { WebhookGraphQLReturn } from '@/modules/webhooks/helpers/graphTypes'; import { SmartTextEditorValueGraphQLReturn } from '@/modules/core/services/richTextEditorService'; @@ -1175,6 +1175,22 @@ export type LimitedWorkspace = { slug: Scalars['String']['output']; }; +export type LimitedWorkspaceJoinRequest = { + __typename?: 'LimitedWorkspaceJoinRequest'; + createdAt: Scalars['DateTime']['output']; + id: Scalars['String']['output']; + status: WorkspaceJoinRequestStatus; + user: LimitedUser; + workspace: LimitedWorkspace; +}; + +export type LimitedWorkspaceJoinRequestCollection = { + __typename?: 'LimitedWorkspaceJoinRequestCollection'; + cursor?: Maybe; + items: Array; + totalCount: Scalars['Int']['output']; +}; + export type MarkCommentViewedInput = { commentId: Scalars['String']['input']; projectId: Scalars['String']['input']; @@ -3729,6 +3745,7 @@ export type User = { versions: CountOnlyCollection; /** Get all invitations to workspaces that the active user has */ workspaceInvites: Array; + workspaceJoinRequests?: Maybe; /** Get the workspaces for the user */ workspaces: WorkspaceCollection; }; @@ -3830,6 +3847,17 @@ export type UserVersionsArgs = { }; +/** + * Full user type, should only be used in the context of admin operations or + * when a user is reading/writing info about himself + */ +export type UserWorkspaceJoinRequestsArgs = { + cursor?: InputMaybe; + filter?: InputMaybe; + limit?: Scalars['Int']['input']; +}; + + /** * Full user type, should only be used in the context of admin operations or * when a user is reading/writing info about himself @@ -4442,6 +4470,10 @@ export type WorkspaceJoinRequestCollection = { totalCount: Scalars['Int']['output']; }; +export type WorkspaceJoinRequestFilter = { + status?: InputMaybe; +}; + export type WorkspaceJoinRequestMutations = { __typename?: 'WorkspaceJoinRequestMutations'; approve: Scalars['Boolean']['output']; @@ -4929,6 +4961,8 @@ export type ResolversTypes = { LegacyCommentViewerData: ResolverTypeWrapper; LimitedUser: ResolverTypeWrapper; LimitedWorkspace: ResolverTypeWrapper; + LimitedWorkspaceJoinRequest: ResolverTypeWrapper; + LimitedWorkspaceJoinRequestCollection: ResolverTypeWrapper & { items: Array }>; MarkCommentViewedInput: MarkCommentViewedInput; MarkReceivedVersionInput: MarkReceivedVersionInput; Model: ResolverTypeWrapper; @@ -5097,6 +5131,7 @@ export type ResolversTypes = { WorkspaceInviteUseInput: WorkspaceInviteUseInput; WorkspaceJoinRequest: ResolverTypeWrapper; WorkspaceJoinRequestCollection: ResolverTypeWrapper & { items: Array }>; + WorkspaceJoinRequestFilter: WorkspaceJoinRequestFilter; WorkspaceJoinRequestMutations: ResolverTypeWrapper; WorkspaceJoinRequestStatus: WorkspaceJoinRequestStatus; WorkspaceMutations: ResolverTypeWrapper; @@ -5231,6 +5266,8 @@ export type ResolversParentTypes = { LegacyCommentViewerData: LegacyCommentViewerData; LimitedUser: LimitedUserGraphQLReturn; LimitedWorkspace: LimitedWorkspace; + LimitedWorkspaceJoinRequest: LimitedWorkspaceJoinRequestGraphQLReturn; + LimitedWorkspaceJoinRequestCollection: Omit & { items: Array }; MarkCommentViewedInput: MarkCommentViewedInput; MarkReceivedVersionInput: MarkReceivedVersionInput; Model: ModelGraphQLReturn; @@ -5379,6 +5416,7 @@ export type ResolversParentTypes = { WorkspaceInviteUseInput: WorkspaceInviteUseInput; WorkspaceJoinRequest: WorkspaceJoinRequestGraphQLReturn; WorkspaceJoinRequestCollection: Omit & { items: Array }; + WorkspaceJoinRequestFilter: WorkspaceJoinRequestFilter; WorkspaceJoinRequestMutations: WorkspaceJoinRequestMutationsGraphQLReturn; WorkspaceMutations: WorkspaceMutationsGraphQLReturn; WorkspacePlan: WorkspacePlan; @@ -5928,6 +5966,22 @@ export type LimitedWorkspaceResolvers; }; +export type LimitedWorkspaceJoinRequestResolvers = { + createdAt?: Resolver; + id?: Resolver; + status?: Resolver; + user?: Resolver; + workspace?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type LimitedWorkspaceJoinRequestCollectionResolvers = { + cursor?: Resolver, ParentType, ContextType>; + items?: Resolver, ParentType, ContextType>; + totalCount?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type ModelResolvers = { author?: Resolver, ParentType, ContextType>; automationsStatus?: Resolver, ParentType, ContextType>; @@ -6653,6 +6707,7 @@ export type UserResolvers, ParentType, ContextType>; versions?: Resolver>; workspaceInvites?: Resolver, ParentType, ContextType>; + workspaceJoinRequests?: Resolver, ParentType, ContextType, RequireFields>; workspaces?: Resolver>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -7065,6 +7120,8 @@ export type Resolvers = { LegacyCommentViewerData?: LegacyCommentViewerDataResolvers; LimitedUser?: LimitedUserResolvers; LimitedWorkspace?: LimitedWorkspaceResolvers; + LimitedWorkspaceJoinRequest?: LimitedWorkspaceJoinRequestResolvers; + LimitedWorkspaceJoinRequestCollection?: LimitedWorkspaceJoinRequestCollectionResolvers; Model?: ModelResolvers; ModelCollection?: ModelCollectionResolvers; ModelMutations?: ModelMutationsResolvers; diff --git a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts index 347db92f8..96f54d600 100644 --- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts +++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts @@ -1156,6 +1156,22 @@ export type LimitedWorkspace = { slug: Scalars['String']['output']; }; +export type LimitedWorkspaceJoinRequest = { + __typename?: 'LimitedWorkspaceJoinRequest'; + createdAt: Scalars['DateTime']['output']; + id: Scalars['String']['output']; + status: WorkspaceJoinRequestStatus; + user: LimitedUser; + workspace: LimitedWorkspace; +}; + +export type LimitedWorkspaceJoinRequestCollection = { + __typename?: 'LimitedWorkspaceJoinRequestCollection'; + cursor?: Maybe; + items: Array; + totalCount: Scalars['Int']['output']; +}; + export type MarkCommentViewedInput = { commentId: Scalars['String']['input']; projectId: Scalars['String']['input']; @@ -3710,6 +3726,7 @@ export type User = { versions: CountOnlyCollection; /** Get all invitations to workspaces that the active user has */ workspaceInvites: Array; + workspaceJoinRequests?: Maybe; /** Get the workspaces for the user */ workspaces: WorkspaceCollection; }; @@ -3811,6 +3828,17 @@ export type UserVersionsArgs = { }; +/** + * Full user type, should only be used in the context of admin operations or + * when a user is reading/writing info about himself + */ +export type UserWorkspaceJoinRequestsArgs = { + cursor?: InputMaybe; + filter?: InputMaybe; + limit?: Scalars['Int']['input']; +}; + + /** * Full user type, should only be used in the context of admin operations or * when a user is reading/writing info about himself @@ -4423,6 +4451,10 @@ export type WorkspaceJoinRequestCollection = { totalCount: Scalars['Int']['output']; }; +export type WorkspaceJoinRequestFilter = { + status?: InputMaybe; +}; + export type WorkspaceJoinRequestMutations = { __typename?: 'WorkspaceJoinRequestMutations'; approve: Scalars['Boolean']['output']; diff --git a/packages/server/modules/shared/services/paginatedItems.ts b/packages/server/modules/shared/services/paginatedItems.ts index 4960b46d5..99f9e3d6f 100644 --- a/packages/server/modules/shared/services/paginatedItems.ts +++ b/packages/server/modules/shared/services/paginatedItems.ts @@ -23,12 +23,19 @@ export const getPaginatedItemsFactory = getTotalCount: (args: Omit) => Promise }) => async (args: TArgs): Promise> => { + const totalCount = await getTotalCount(args) + if (args.limit === 0) { + return { + cursor: null, + items: [], + totalCount + } + } const maybeDecodedCursor = args.cursor ? decodeIsoDateCursor(args.cursor) : null const items = await getItems({ ...args, cursor: maybeDecodedCursor ?? undefined }) - const totalCount = await getTotalCount(args) let cursor = null if (items.length === args.limit) { diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts b/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts index 45888a9a4..9d0406dc9 100644 --- a/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/graph/resolvers/workspaceJoinRequests.ts @@ -6,6 +6,7 @@ import { getUserFactory } from '@/modules/core/repositories/users' import { renderEmail } from '@/modules/emails/services/emailRendering' import { sendEmail } from '@/modules/emails/services/sending' import { commandFactory } from '@/modules/shared/command' +import { getFeatureFlags } from '@/modules/shared/helpers/envHelper' import { getEventBus } from '@/modules/shared/services/eventBus' import { getPaginatedItemsFactory } from '@/modules/shared/services/paginatedItems' import { @@ -14,8 +15,10 @@ import { } from '@/modules/workspaces/domain/operations' import { countAdminWorkspaceJoinRequestsFactory, + countWorkspaceJoinRequestsFactory, getAdminWorkspaceJoinRequestsFactory, getWorkspaceJoinRequestFactory, + getWorkspaceJoinRequestsFactory, updateWorkspaceJoinRequestStatusFactory } from '@/modules/workspaces/repositories/workspaceJoinRequests' import { @@ -33,117 +36,160 @@ import { WorkspaceJoinRequestGraphQLReturn } from '@/modules/workspacesCore/help const eventBus = getEventBus() -export default { - Workspace: { - adminWorkspacesJoinRequests: async (parent, args, ctx) => { - const { filter, cursor, limit } = args +const { FF_WORKSPACES_MODULE_ENABLED } = getFeatureFlags() - return await getPaginatedItemsFactory< - { - limit: number - cursor?: string - filter: { - workspaceId: string - userId: string - status?: WorkspaceJoinRequestStatus | null - } - }, - WorkspaceJoinRequestGraphQLReturn - >({ - getItems: getAdminWorkspaceJoinRequestsFactory({ db }), - getTotalCount: countAdminWorkspaceJoinRequestsFactory({ db }) - })({ - filter: { - workspaceId: parent.id, - status: filter?.status ?? undefined, - userId: ctx.userId! // This is the worskpace admin, not the request userId - }, - cursor: cursor ?? undefined, - limit - }) - } - }, - WorkspaceJoinRequest: { - id: async (parent) => { - return parent.userId + parent.workspaceId - }, - user: async (parent, _args, ctx) => { - return await ctx.loaders.users.getUser.load(parent.userId) - }, - workspace: async (parent, _args, ctx) => { - return await ctx.loaders.workspaces!.getWorkspace.load(parent.workspaceId) - } - }, - Mutation: { - workspaceJoinRequestMutations: () => ({}) - }, - WorkspaceJoinRequestMutations: { - approve: async (_parent, args) => { - const approveWorkspaceJoinRequest = commandFactory({ - db, - eventBus, - operationFactory: ({ db, emit }) => { - const updateWorkspaceJoinRequestStatus = - updateWorkspaceJoinRequestStatusFactory({ - db - }) - const sendWorkspaceJoinRequestApprovedEmail = - sendWorkspaceJoinRequestApprovedEmailFactory({ - renderEmail, - sendEmail, - getServerInfo: getServerInfoFactory({ db }), - getUserEmails: findEmailsByUserIdFactory({ db }) - }) - return approveWorkspaceJoinRequestFactory({ - updateWorkspaceJoinRequestStatus, - sendWorkspaceJoinRequestApprovedEmail, - getUserById: getUserFactory({ db }), - getWorkspace: getWorkspaceFactory({ db }), - getWorkspaceJoinRequest: getWorkspaceJoinRequestFactory({ - db - }), - upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }), - emit +export default FF_WORKSPACES_MODULE_ENABLED + ? ({ + Workspace: { + adminWorkspacesJoinRequests: async (parent, args, ctx) => { + const { filter, cursor, limit } = args + + return await getPaginatedItemsFactory< + { + limit: number + cursor?: string + filter: { + workspaceId: string + userId: string + status?: WorkspaceJoinRequestStatus | null + } + }, + WorkspaceJoinRequestGraphQLReturn + >({ + getItems: getAdminWorkspaceJoinRequestsFactory({ db }), + getTotalCount: countAdminWorkspaceJoinRequestsFactory({ db }) + })({ + filter: { + workspaceId: parent.id, + status: filter?.status ?? undefined, + userId: ctx.userId! // This is the worskpace admin, not the request userId + }, + cursor: cursor ?? undefined, + limit }) } - }) - return await approveWorkspaceJoinRequest({ - userId: args.input.userId, - workspaceId: args.input.workspaceId - }) - }, - deny: async (_parent, args) => { - const denyWorkspaceJoinRequest = commandFactory({ - db, - operationFactory: ({ db }) => { - const updateWorkspaceJoinRequestStatus = - updateWorkspaceJoinRequestStatusFactory({ - db - }) - const sendWorkspaceJoinRequestDeniedEmail = - sendWorkspaceJoinRequestDeniedEmailFactory({ - renderEmail, - sendEmail, - getServerInfo: getServerInfoFactory({ db }), - getUserEmails: findEmailsByUserIdFactory({ db }) - }) + }, + WorkspaceJoinRequest: { + id: async (parent) => { + return parent.userId + parent.workspaceId + }, + user: async (parent, _args, ctx) => { + return await ctx.loaders.users.getUser.load(parent.userId) + }, + workspace: async (parent, _args, ctx) => { + return await ctx.loaders.workspaces!.getWorkspace.load(parent.workspaceId) + } + }, + LimitedWorkspaceJoinRequest: { + id: async (parent) => { + return parent.userId + parent.workspaceId + }, + user: async (parent, _args, ctx) => { + return await ctx.loaders.users.getUser.load(parent.userId) + }, + workspace: async (parent, _args, ctx) => { + return await ctx.loaders.workspaces!.getWorkspace.load(parent.workspaceId) + } + }, + User: { + workspaceJoinRequests: async (parent, args) => { + const { filter, cursor, limit } = args - return denyWorkspaceJoinRequestFactory({ - updateWorkspaceJoinRequestStatus, - sendWorkspaceJoinRequestDeniedEmail, - getUserById: getUserFactory({ db }), - getWorkspace: getWorkspaceFactory({ db }), - getWorkspaceJoinRequest: getWorkspaceJoinRequestFactory({ - db - }) + return await getPaginatedItemsFactory< + { + limit: number + cursor?: string + filter: { + userId: string + status?: WorkspaceJoinRequestStatus | null + } + }, + WorkspaceJoinRequestGraphQLReturn + >({ + getItems: getWorkspaceJoinRequestsFactory({ db }), + getTotalCount: countWorkspaceJoinRequestsFactory({ db }) + })({ + filter: { + userId: parent.id, + status: filter?.status ?? undefined + }, + cursor: cursor ?? undefined, + limit }) } - }) + }, + Mutation: { + workspaceJoinRequestMutations: () => ({}) + }, + WorkspaceJoinRequestMutations: { + approve: async (_parent, args) => { + const approveWorkspaceJoinRequest = + commandFactory({ + db, + eventBus, + operationFactory: ({ db, emit }) => { + const updateWorkspaceJoinRequestStatus = + updateWorkspaceJoinRequestStatusFactory({ + db + }) + const sendWorkspaceJoinRequestApprovedEmail = + sendWorkspaceJoinRequestApprovedEmailFactory({ + renderEmail, + sendEmail, + getServerInfo: getServerInfoFactory({ db }), + getUserEmails: findEmailsByUserIdFactory({ db }) + }) + return approveWorkspaceJoinRequestFactory({ + updateWorkspaceJoinRequestStatus, + sendWorkspaceJoinRequestApprovedEmail, + getUserById: getUserFactory({ db }), + getWorkspace: getWorkspaceFactory({ db }), + getWorkspaceJoinRequest: getWorkspaceJoinRequestFactory({ + db + }), + upsertWorkspaceRole: upsertWorkspaceRoleFactory({ db }), + emit + }) + } + }) + return await approveWorkspaceJoinRequest({ + userId: args.input.userId, + workspaceId: args.input.workspaceId + }) + }, + deny: async (_parent, args) => { + const denyWorkspaceJoinRequest = commandFactory({ + db, + operationFactory: ({ db }) => { + const updateWorkspaceJoinRequestStatus = + updateWorkspaceJoinRequestStatusFactory({ + db + }) + const sendWorkspaceJoinRequestDeniedEmail = + sendWorkspaceJoinRequestDeniedEmailFactory({ + renderEmail, + sendEmail, + getServerInfo: getServerInfoFactory({ db }), + getUserEmails: findEmailsByUserIdFactory({ db }) + }) - return await denyWorkspaceJoinRequest({ - userId: args.input.userId, - workspaceId: args.input.workspaceId - }) - } - } -} as Resolvers + return denyWorkspaceJoinRequestFactory({ + updateWorkspaceJoinRequestStatus, + sendWorkspaceJoinRequestDeniedEmail, + getUserById: getUserFactory({ db }), + getWorkspace: getWorkspaceFactory({ db }), + getWorkspaceJoinRequest: getWorkspaceJoinRequestFactory({ + db + }) + }) + } + }) + + return await denyWorkspaceJoinRequest({ + userId: args.input.userId, + workspaceId: args.input.workspaceId + }) + } + } + } as Resolvers) + : {} diff --git a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts index b85b331d9..49faaa428 100644 --- a/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts +++ b/packages/server/modules/workspaces/repositories/workspaceJoinRequests.ts @@ -13,6 +13,7 @@ import { } from '@/modules/workspacesCore/helpers/db' import { Roles } from '@speckle/shared' import { Knex } from 'knex' +import { SetRequired } from 'type-fest' const tables = { workspaceJoinRequests: (db: Knex) => @@ -51,13 +52,13 @@ export const getWorkspaceJoinRequestFactory = } type WorkspaceJoinRequestFilter = { - workspaceId: string + workspaceId?: string status?: WorkspaceJoinRequestStatus | null userId: string } const adminWorkspaceJoinRequestsBaseQueryFactory = - (db: Knex) => (filter: WorkspaceJoinRequestFilter) => { + (db: Knex) => (filter: SetRequired) => { const query = tables .workspaceJoinRequests(db) .innerJoin( @@ -79,7 +80,7 @@ export const getAdminWorkspaceJoinRequestsFactory = cursor, limit }: { - filter: WorkspaceJoinRequestFilter + filter: SetRequired cursor?: string limit: number }) => { @@ -96,9 +97,56 @@ export const getAdminWorkspaceJoinRequestsFactory = export const countAdminWorkspaceJoinRequestsFactory = ({ db }: { db: Knex }) => - async ({ filter }: { filter: WorkspaceJoinRequestFilter }) => { + async ({ + filter + }: { + filter: SetRequired + }) => { const query = adminWorkspaceJoinRequestsBaseQueryFactory(db)(filter) const [res] = await query.count() return parseInt(res.count.toString()) } + +const workspaceJoinRequestsBaseQueryFactory = + (db: Knex) => (filter: WorkspaceJoinRequestFilter) => { + const query = tables + .workspaceJoinRequests(db) + .where(WorkspaceJoinRequests.col.userId, filter.userId) + if (filter.status) query.andWhere(WorkspaceJoinRequests.col.status, filter.status) + if (filter.userId) query.andWhere(WorkspaceJoinRequests.col.userId, filter.userId) + if (filter.workspaceId) + query.andWhere(WorkspaceJoinRequests.col.workspaceId, filter.workspaceId) + return query + } + +export const getWorkspaceJoinRequestsFactory = + ({ db }: { db: Knex }) => + ({ + filter, + cursor, + limit + }: { + filter: WorkspaceJoinRequestFilter + cursor?: string + limit: number + }) => { + const query = workspaceJoinRequestsBaseQueryFactory(db)(filter) + + if (cursor) { + query.andWhere(WorkspaceJoinRequests.col.createdAt, '<', cursor) + } + return query + .select(WorkspaceJoinRequests.cols) + .orderBy(WorkspaceJoinRequests.col.createdAt, 'desc') + .limit(limit) + } + +export const countWorkspaceJoinRequestsFactory = + ({ db }: { db: Knex }) => + async ({ filter }: { filter: WorkspaceJoinRequestFilter }) => { + const query = workspaceJoinRequestsBaseQueryFactory(db)(filter) + + const [res] = await query.count() + return parseInt(res.count.toString()) + } diff --git a/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts index 2fcaba4de..6a1b498f7 100644 --- a/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/workspaceJoinRequests.graph.spec.ts @@ -7,6 +7,7 @@ import { createTestUser } from '@/test/authHelper' import { + GetActiveUserWithWorkspaceJoinRequestsDocument, GetWorkspaceWithJoinRequestsDocument, RequestToJoinWorkspaceDocument } from '@/test/graphql/generated/graphql' @@ -154,4 +155,76 @@ describe('WorkspaceJoinRequests GQL', () => { expect(items2[0].user.id).to.equal(user2.id) }) }) + + describe('User.workspaceJoinRequests', () => { + it('should return the workspace join requests for the user', async () => { + const admin = await createTestUser({ + name: 'admin user', + role: Roles.Server.User, + email: `${createRandomString()}@example.org`, + verified: true + }) + + const user = await createTestUser({ + name: 'user 1', + role: Roles.Server.User, + email: `${createRandomString()}@example.org`, + verified: true + }) + + const workspace1 = { + id: createRandomString(), + name: 'Workspace 1', + ownerId: admin.id, + description: '', + discoverabilityEnabled: true + } + await createTestWorkspace(workspace1, admin, { domain: 'example.org' }) + + const workspace2 = { + id: createRandomString(), + name: 'Workspace 2', + ownerId: admin.id, + description: '', + discoverabilityEnabled: true + } + await createTestWorkspace(workspace2, admin, { domain: 'example.org' }) + + const sessionUser = await login(user) + + // User requests to join workspace1 + const joinReq1 = await sessionUser.execute(RequestToJoinWorkspaceDocument, { + input: { + workspaceId: workspace1.id + } + }) + expect(joinReq1).to.not.haveGraphQLErrors() + + // User requests to join workspace2 + const joinReq2 = await sessionUser.execute(RequestToJoinWorkspaceDocument, { + input: { + workspaceId: workspace2.id + } + }) + expect(joinReq2).to.not.haveGraphQLErrors() + + const res = await sessionUser.execute( + GetActiveUserWithWorkspaceJoinRequestsDocument, + {} + ) + expect(res).to.not.haveGraphQLErrors() + + const { items, totalCount } = res.data!.activeUser!.workspaceJoinRequests! + + expect(totalCount).to.equal(2) + + expect(items).to.have.length(2) + expect(items[0].status).to.equal('pending') + expect(items[0].workspace.id).to.equal(workspace2.id) + expect(items[0].user.id).to.equal(user.id) + expect(items[1].status).to.equal('pending') + expect(items[1].workspace.id).to.equal(workspace1.id) + expect(items[1].user.id).to.equal(user.id) + }) + }) }) diff --git a/packages/server/modules/workspacesCore/graph/resolvers/workspaceJoinRequests.ts b/packages/server/modules/workspacesCore/graph/resolvers/workspaceJoinRequests.ts new file mode 100644 index 000000000..9c234dc5d --- /dev/null +++ b/packages/server/modules/workspacesCore/graph/resolvers/workspaceJoinRequests.ts @@ -0,0 +1,47 @@ +import { WorkspacesModuleDisabledError } from '@/modules/core/errors/workspaces' +import { Resolvers } from '@/modules/core/graph/generated/graphql' +import { getFeatureFlags } from '@/modules/shared/helpers/envHelper' + +const { FF_WORKSPACES_MODULE_ENABLED } = getFeatureFlags() + +export default !FF_WORKSPACES_MODULE_ENABLED + ? ({ + Workspace: { + adminWorkspacesJoinRequests: async () => { + throw new WorkspacesModuleDisabledError() + } + }, + WorkspaceJoinRequest: { + id: async () => { + throw new WorkspacesModuleDisabledError() + }, + user: async () => { + throw new WorkspacesModuleDisabledError() + }, + workspace: async () => { + throw new WorkspacesModuleDisabledError() + } + }, + LimitedWorkspaceJoinRequest: { + id: async () => { + throw new WorkspacesModuleDisabledError() + }, + user: async () => { + throw new WorkspacesModuleDisabledError() + }, + workspace: async () => { + throw new WorkspacesModuleDisabledError() + } + }, + User: { + workspaceJoinRequests: async () => { + throw new WorkspacesModuleDisabledError() + } + }, + Mutation: { + workspaceJoinRequestMutations: () => { + throw new WorkspacesModuleDisabledError() + } + } + } as Resolvers) + : {} diff --git a/packages/server/modules/workspacesCore/helpers/graphTypes.ts b/packages/server/modules/workspacesCore/helpers/graphTypes.ts index 548ae4a51..2b5347b69 100644 --- a/packages/server/modules/workspacesCore/helpers/graphTypes.ts +++ b/packages/server/modules/workspacesCore/helpers/graphTypes.ts @@ -7,6 +7,7 @@ import { WorkspaceRoles } from '@speckle/shared' export type WorkspaceGraphQLReturn = Workspace export type WorkspaceJoinRequestGraphQLReturn = WorkspaceJoinRequest +export type LimitedWorkspaceJoinRequestGraphQLReturn = WorkspaceJoinRequest export type WorkspaceBillingGraphQLReturn = { parent: Workspace } export type WorkspaceSsoGraphQLReturn = WorkspaceSsoProviderRecord export type WorkspaceMutationsGraphQLReturn = MutationsObjectGraphQLReturn diff --git a/packages/server/modules/workspacesCore/migrations/20250219100906_index_user_id_workspace_join_requests.ts b/packages/server/modules/workspacesCore/migrations/20250219100906_index_user_id_workspace_join_requests.ts new file mode 100644 index 000000000..c89649386 --- /dev/null +++ b/packages/server/modules/workspacesCore/migrations/20250219100906_index_user_id_workspace_join_requests.ts @@ -0,0 +1,15 @@ +import { Knex } from 'knex' + +const WORKSPACE_JOIN_REQUESTS_TABLE = 'workspace_join_requests' + +export async function up(knex: Knex): Promise { + await knex.schema.alterTable(WORKSPACE_JOIN_REQUESTS_TABLE, (table) => { + table.index('userId') + }) +} + +export async function down(knex: Knex): Promise { + await knex.schema.alterTable(WORKSPACE_JOIN_REQUESTS_TABLE, (table) => { + table.dropIndex('userId') + }) +} diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index a68025ac6..8b2df4e9c 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -1157,6 +1157,22 @@ export type LimitedWorkspace = { slug: Scalars['String']['output']; }; +export type LimitedWorkspaceJoinRequest = { + __typename?: 'LimitedWorkspaceJoinRequest'; + createdAt: Scalars['DateTime']['output']; + id: Scalars['String']['output']; + status: WorkspaceJoinRequestStatus; + user: LimitedUser; + workspace: LimitedWorkspace; +}; + +export type LimitedWorkspaceJoinRequestCollection = { + __typename?: 'LimitedWorkspaceJoinRequestCollection'; + cursor?: Maybe; + items: Array; + totalCount: Scalars['Int']['output']; +}; + export type MarkCommentViewedInput = { commentId: Scalars['String']['input']; projectId: Scalars['String']['input']; @@ -3711,6 +3727,7 @@ export type User = { versions: CountOnlyCollection; /** Get all invitations to workspaces that the active user has */ workspaceInvites: Array; + workspaceJoinRequests?: Maybe; /** Get the workspaces for the user */ workspaces: WorkspaceCollection; }; @@ -3812,6 +3829,17 @@ export type UserVersionsArgs = { }; +/** + * Full user type, should only be used in the context of admin operations or + * when a user is reading/writing info about himself + */ +export type UserWorkspaceJoinRequestsArgs = { + cursor?: InputMaybe; + filter?: InputMaybe; + limit?: Scalars['Int']['input']; +}; + + /** * Full user type, should only be used in the context of admin operations or * when a user is reading/writing info about himself @@ -4424,6 +4452,10 @@ export type WorkspaceJoinRequestCollection = { totalCount: Scalars['Int']['output']; }; +export type WorkspaceJoinRequestFilter = { + status?: InputMaybe; +}; + export type WorkspaceJoinRequestMutations = { __typename?: 'WorkspaceJoinRequestMutations'; approve: Scalars['Boolean']['output']; @@ -5526,6 +5558,11 @@ export type GetActiveUserQueryVariables = Exact<{ [key: string]: never; }>; export type GetActiveUserQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', id: string, email?: string | null, name: string, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, role?: string | null } | null }; +export type GetActiveUserWithWorkspaceJoinRequestsQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetActiveUserWithWorkspaceJoinRequestsQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', id: string, email?: string | null, name: string, bio?: string | null, company?: string | null, avatar?: string | null, verified?: boolean | null, role?: string | null, workspaceJoinRequests?: { __typename?: 'LimitedWorkspaceJoinRequestCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'LimitedWorkspaceJoinRequest', status: WorkspaceJoinRequestStatus, workspace: { __typename?: 'LimitedWorkspace', id: string, name: string }, user: { __typename?: 'LimitedUser', id: string, name: string } }> } | null } | null }; + export type GetOtherUserQueryVariables = Exact<{ id: Scalars['String']['input']; }>; @@ -5811,6 +5848,7 @@ export const CreateUserEmailDocument = {"kind":"Document","definitions":[{"kind" export const DeleteUserEmailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteUserEmail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DeleteUserEmailInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUserMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"emailMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"delete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserWithEmails"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserWithEmails"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"emails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"primary"}}]}}]}}]} as unknown as DocumentNode; export const SetPrimaryUserEmailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SetPrimaryUserEmail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SetPrimaryUserEmailInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUserMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"emailMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"setPrimary"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"UserWithEmails"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UserWithEmails"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"emails"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"primary"}}]}}]}}]} as unknown as DocumentNode; export const GetActiveUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetActiveUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BaseUserFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BaseUserFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]} as unknown as DocumentNode; +export const GetActiveUserWithWorkspaceJoinRequestsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetActiveUserWithWorkspaceJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BaseUserFields"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BaseUserFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]} as unknown as DocumentNode; export const GetOtherUserDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetOtherUser"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"otherUser"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BaseLimitedUserFields"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BaseLimitedUserFields"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"bio"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}}]}}]} as unknown as DocumentNode; export const GetAdminUsersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetAdminUsers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},"defaultValue":{"kind":"IntValue","value":"25"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"offset"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},"defaultValue":{"kind":"IntValue","value":"0"}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"query"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}},"defaultValue":{"kind":"NullValue"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"adminUsers"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"offset"},"value":{"kind":"Variable","name":{"kind":"Name","value":"offset"}}},{"kind":"Argument","name":{"kind":"Name","value":"query"},"value":{"kind":"Variable","name":{"kind":"Name","value":"query"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"registeredUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"invitedUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const GetPendingEmailVerificationStatusDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetPendingEmailVerificationStatus"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasPendingVerification"}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/server/test/graphql/users.ts b/packages/server/test/graphql/users.ts index 1421600fa..87d2ab25b 100644 --- a/packages/server/test/graphql/users.ts +++ b/packages/server/test/graphql/users.ts @@ -47,6 +47,31 @@ const getActiveUserQuery = gql` ${baseUserFieldsFragment} ` +export const getActiveUserWithWorkspaceJoinRequestsQuery = gql` + query GetActiveUserWithWorkspaceJoinRequests { + activeUser { + ...BaseUserFields + workspaceJoinRequests { + totalCount + cursor + items { + workspace { + id + name + } + user { + id + name + } + status + } + } + } + } + + ${baseUserFieldsFragment} +` + const getOtherUserQuery = gql` query GetOtherUser($id: String!) { otherUser(id: $id) { From a9dd835de93cb4be08e31c4ea90f45e86a498883 Mon Sep 17 00:00:00 2001 From: Alessandro Magionami Date: Tue, 25 Feb 2025 12:19:50 +0100 Subject: [PATCH 34/56] feat(workspaces): team resolver for limited workspace (#4040) --- .../typedefs/workspaces.graphql | 14 ++++ .../modules/core/graph/generated/graphql.ts | 40 +++++++++ .../graph/generated/graphql.ts | 21 +++++ .../workspaces/graph/resolvers/workspaces.ts | 18 ++++ .../integration/workspaces.graph.spec.ts | 84 ++++++++++++++++++- .../server/test/graphql/generated/graphql.ts | 25 +++++- packages/server/test/graphql/workspaces.ts | 7 ++ 7 files changed, 205 insertions(+), 4 deletions(-) diff --git a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql index 2242272e9..6b0528061 100644 --- a/packages/server/assets/workspacesCore/typedefs/workspaces.graphql +++ b/packages/server/assets/workspacesCore/typedefs/workspaces.graphql @@ -367,6 +367,20 @@ type LimitedWorkspace { Optional base64 encoded workspace logo image """ logo: String + """ + Workspace members visible to people with verified email domain + """ + team(cursor: String, limit: Int! = 25): DiscoverableWorkspaceCollaboratorCollection +} + +type DiscoverableWorkspaceCollaboratorCollection { + totalCount: Int! + cursor: String + items: [DiscoverableWorkspaceCollaborator!]! +} + +type DiscoverableWorkspaceCollaborator { + avatar: String } type WorkspaceDomain { diff --git a/packages/server/modules/core/graph/generated/graphql.ts b/packages/server/modules/core/graph/generated/graphql.ts index a3aca3949..b70c8fdc0 100644 --- a/packages/server/modules/core/graph/generated/graphql.ts +++ b/packages/server/modules/core/graph/generated/graphql.ts @@ -960,6 +960,18 @@ export type DiscoverableStreamsSortingInput = { type: DiscoverableStreamsSortType; }; +export type DiscoverableWorkspaceCollaborator = { + __typename?: 'DiscoverableWorkspaceCollaborator'; + avatar: Scalars['String']['output']; +}; + +export type DiscoverableWorkspaceCollaboratorCollection = { + __typename?: 'DiscoverableWorkspaceCollaboratorCollection'; + cursor?: Maybe; + items: Array; + totalCount: Scalars['Int']['output']; +}; + export type EditCommentInput = { commentId: Scalars['String']['input']; content: CommentContentInput; @@ -1173,6 +1185,15 @@ export type LimitedWorkspace = { name: Scalars['String']['output']; /** Unique workspace short id. Used for navigation. */ slug: Scalars['String']['output']; + /** Workspace members visible to people with verified email domain */ + team?: Maybe; +}; + + +/** Workspace metadata visible to non-workspace members. */ +export type LimitedWorkspaceTeamArgs = { + cursor?: InputMaybe; + limit?: Scalars['Int']['input']; }; export type LimitedWorkspaceJoinRequest = { @@ -4947,6 +4968,8 @@ export type ResolversTypes = { DenyWorkspaceJoinRequestInput: DenyWorkspaceJoinRequestInput; DiscoverableStreamsSortType: DiscoverableStreamsSortType; DiscoverableStreamsSortingInput: DiscoverableStreamsSortingInput; + DiscoverableWorkspaceCollaborator: ResolverTypeWrapper; + DiscoverableWorkspaceCollaboratorCollection: ResolverTypeWrapper; EditCommentInput: EditCommentInput; EmailVerificationRequestInput: EmailVerificationRequestInput; FileUpload: ResolverTypeWrapper; @@ -5252,6 +5275,8 @@ export type ResolversParentTypes = { DeleteVersionsInput: DeleteVersionsInput; DenyWorkspaceJoinRequestInput: DenyWorkspaceJoinRequestInput; DiscoverableStreamsSortingInput: DiscoverableStreamsSortingInput; + DiscoverableWorkspaceCollaborator: DiscoverableWorkspaceCollaborator; + DiscoverableWorkspaceCollaboratorCollection: DiscoverableWorkspaceCollaboratorCollection; EditCommentInput: EditCommentInput; EmailVerificationRequestInput: EmailVerificationRequestInput; FileUpload: FileUploadGraphQLReturn; @@ -5882,6 +5907,18 @@ export interface DateTimeScalarConfig extends GraphQLScalarTypeConfig = { + avatar?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + +export type DiscoverableWorkspaceCollaboratorCollectionResolvers = { + cursor?: Resolver, ParentType, ContextType>; + items?: Resolver, ParentType, ContextType>; + totalCount?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; +}; + export type FileUploadResolvers = { branchName?: Resolver; convertedCommitId?: Resolver, ParentType, ContextType>; @@ -5963,6 +6000,7 @@ export type LimitedWorkspaceResolvers, ParentType, ContextType>; name?: Resolver; slug?: Resolver; + team?: Resolver, ParentType, ContextType, RequireFields>; __isTypeOf?: IsTypeOfResolverFn; }; @@ -7113,6 +7151,8 @@ export type Resolvers = { CommitCollection?: CommitCollectionResolvers; CountOnlyCollection?: CountOnlyCollectionResolvers; DateTime?: GraphQLScalarType; + DiscoverableWorkspaceCollaborator?: DiscoverableWorkspaceCollaboratorResolvers; + DiscoverableWorkspaceCollaboratorCollection?: DiscoverableWorkspaceCollaboratorCollectionResolvers; FileUpload?: FileUploadResolvers; GendoAIRender?: GendoAiRenderResolvers; GendoAIRenderCollection?: GendoAiRenderCollectionResolvers; diff --git a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts index 96f54d600..4559b4ecd 100644 --- a/packages/server/modules/cross-server-sync/graph/generated/graphql.ts +++ b/packages/server/modules/cross-server-sync/graph/generated/graphql.ts @@ -941,6 +941,18 @@ export type DiscoverableStreamsSortingInput = { type: DiscoverableStreamsSortType; }; +export type DiscoverableWorkspaceCollaborator = { + __typename?: 'DiscoverableWorkspaceCollaborator'; + avatar: Scalars['String']['output']; +}; + +export type DiscoverableWorkspaceCollaboratorCollection = { + __typename?: 'DiscoverableWorkspaceCollaboratorCollection'; + cursor?: Maybe; + items: Array; + totalCount: Scalars['Int']['output']; +}; + export type EditCommentInput = { commentId: Scalars['String']['input']; content: CommentContentInput; @@ -1154,6 +1166,15 @@ export type LimitedWorkspace = { name: Scalars['String']['output']; /** Unique workspace short id. Used for navigation. */ slug: Scalars['String']['output']; + /** Workspace members visible to people with verified email domain */ + team?: Maybe; +}; + + +/** Workspace metadata visible to non-workspace members. */ +export type LimitedWorkspaceTeamArgs = { + cursor?: InputMaybe; + limit?: Scalars['Int']['input']; }; export type LimitedWorkspaceJoinRequest = { diff --git a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts index 1b4995e87..a35dde9d4 100644 --- a/packages/server/modules/workspaces/graph/resolvers/workspaces.ts +++ b/packages/server/modules/workspaces/graph/resolvers/workspaces.ts @@ -200,6 +200,8 @@ import { sendWorkspaceJoinRequestReceivedEmailFactory } from '@/modules/workspac import { getProjectFactory } from '@/modules/core/repositories/projects' import { OperationTypeNode } from 'graphql' import { updateWorkspacePlanFactory } from '@/modules/gatekeeper/services/workspacePlans' +import { GetWorkspaceCollaboratorsArgs } from '@/modules/workspaces/domain/operations' +import { WorkspaceTeamMember } from '@/modules/workspaces/domain/types' const eventBus = getEventBus() const getServerInfo = getServerInfoFactory({ db }) @@ -1368,6 +1370,22 @@ export = FF_WORKSPACES_MODULE_ENABLED ServerWorkspacesInfo: { workspacesEnabled: () => true }, + LimitedWorkspace: { + team: async (parent, args) => { + const team = await getPaginatedItemsFactory< + Pick, + WorkspaceTeamMember + >({ + getItems: getWorkspaceCollaboratorsFactory({ db }), + getTotalCount: getWorkspaceCollaboratorsTotalCountFactory({ db }) + })({ + workspaceId: parent.id, + limit: args.limit, + cursor: args.cursor ?? undefined + }) + return team + } + }, Subscription: { workspaceProjectsUpdated: { subscribe: filteredSubscribe( diff --git a/packages/server/modules/workspaces/tests/integration/workspaces.graph.spec.ts b/packages/server/modules/workspaces/tests/integration/workspaces.graph.spec.ts index 261665f2c..e7079d554 100644 --- a/packages/server/modules/workspaces/tests/integration/workspaces.graph.spec.ts +++ b/packages/server/modules/workspaces/tests/integration/workspaces.graph.spec.ts @@ -9,7 +9,8 @@ import { BasicTestUser, createAuthTokenForUser, createTestUser, - createTestUsers + createTestUsers, + login } from '@/test/authHelper' import { Roles, wait, WorkspaceRoles } from '@speckle/shared' import { @@ -27,7 +28,8 @@ import { AddWorkspaceDomainDocument, DeleteWorkspaceDomainDocument, CreateWorkspaceProjectDocument, - DismissWorkspaceDocument + DismissWorkspaceDocument, + GetActiveUserDiscoverableWorkspacesDocument } from '@/test/graphql/generated/graphql' import { beforeEachContext } from '@/test/hooks' import { AllScopes } from '@/modules/core/helpers/mainConstants' @@ -490,6 +492,84 @@ describe('Workspaces GQL CRUD', () => { }) }) + describe('LimitedWorkspace.team', () => { + it('should return a limited workspace team', async () => { + const email = createRandomEmail() + const domain = email.split('@')[1] + const user = await createTestUser({ + id: createRandomString(), + name: createRandomString(), + email, + role: Roles.Server.Admin, + verified: true + }) + const session = await login(user) + + const workspaceAdmin = await createTestUser({ + id: createRandomString(), + name: 'John Speckle', + email: createRandomEmail(), + role: Roles.Server.Admin, + verified: true, + avatar: 'data:image/png;base64,foobar' + }) + + await createTestWorkspace( + { + id: createRandomString(), + ownerId: workspaceAdmin.id, + name: 'Workspace A', + slug: cryptoRandomString({ length: 10 }), + discoverabilityEnabled: true + }, + workspaceAdmin, + { + domain + } + ) + await createTestWorkspace( + { + id: createRandomString(), + ownerId: workspaceAdmin.id, + name: 'Workspace B', + slug: cryptoRandomString({ length: 10 }), + discoverabilityEnabled: true + }, + workspaceAdmin, + { + domain + } + ) + // Workspace without domain should not be returned + await createTestWorkspace( + { + id: createRandomString(), + ownerId: workspaceAdmin.id, + name: 'Workspace C', + slug: cryptoRandomString({ length: 10 }) + }, + workspaceAdmin + ) + + const res = await session.execute( + GetActiveUserDiscoverableWorkspacesDocument, + {} + ) + expect(res).to.not.haveGraphQLErrors() + + expect(res.data?.activeUser?.discoverableWorkspaces).to.have.length(2) + const discoverableWorkspaces = res.data?.activeUser?.discoverableWorkspaces + expect(discoverableWorkspaces?.[0].team?.items).to.have.length(1) + expect(discoverableWorkspaces?.[0].team?.items[0].avatar).to.eq( + workspaceAdmin.avatar + ) + expect(discoverableWorkspaces?.[1].team?.items).to.have.length(1) + expect(discoverableWorkspaces?.[1].team?.items[0].avatar).to.eq( + workspaceAdmin.avatar + ) + }) + }) + describe('query activeUser.workspaces', () => { it('should return all workspaces for a user', async () => { const testUser: BasicTestUser = { diff --git a/packages/server/test/graphql/generated/graphql.ts b/packages/server/test/graphql/generated/graphql.ts index 8b2df4e9c..f5ceb015e 100644 --- a/packages/server/test/graphql/generated/graphql.ts +++ b/packages/server/test/graphql/generated/graphql.ts @@ -942,6 +942,18 @@ export type DiscoverableStreamsSortingInput = { type: DiscoverableStreamsSortType; }; +export type DiscoverableWorkspaceCollaborator = { + __typename?: 'DiscoverableWorkspaceCollaborator'; + avatar: Scalars['String']['output']; +}; + +export type DiscoverableWorkspaceCollaboratorCollection = { + __typename?: 'DiscoverableWorkspaceCollaboratorCollection'; + cursor?: Maybe; + items: Array; + totalCount: Scalars['Int']['output']; +}; + export type EditCommentInput = { commentId: Scalars['String']['input']; content: CommentContentInput; @@ -1155,6 +1167,15 @@ export type LimitedWorkspace = { name: Scalars['String']['output']; /** Unique workspace short id. Used for navigation. */ slug: Scalars['String']['output']; + /** Workspace members visible to people with verified email domain */ + team?: Maybe; +}; + + +/** Workspace metadata visible to non-workspace members. */ +export type LimitedWorkspaceTeamArgs = { + cursor?: InputMaybe; + limit?: Scalars['Int']['input']; }; export type LimitedWorkspaceJoinRequest = { @@ -5642,7 +5663,7 @@ export type GetWorkspaceBySlugQuery = { __typename?: 'Query', workspaceBySlug: { export type GetActiveUserDiscoverableWorkspacesQueryVariables = Exact<{ [key: string]: never; }>; -export type GetActiveUserDiscoverableWorkspacesQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', discoverableWorkspaces: Array<{ __typename?: 'LimitedWorkspace', id: string, name: string, description?: string | null }> } | null }; +export type GetActiveUserDiscoverableWorkspacesQuery = { __typename?: 'Query', activeUser?: { __typename?: 'User', discoverableWorkspaces: Array<{ __typename?: 'LimitedWorkspace', id: string, name: string, description?: string | null, team?: { __typename?: 'DiscoverableWorkspaceCollaboratorCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'DiscoverableWorkspaceCollaborator', avatar: string }> } | null }> } | null }; export type UpdateWorkspaceMutationVariables = Exact<{ input: WorkspaceUpdateInput; @@ -5859,7 +5880,7 @@ export const CreateWorkspaceDocument = {"kind":"Document","definitions":[{"kind" export const DeleteWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"delete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}]}]}}]}}]} as unknown as DocumentNode; export const GetWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceCollaborator"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; export const GetWorkspaceBySlugDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWorkspaceBySlug"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceCollaborator"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; -export const GetActiveUserDiscoverableWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getActiveUserDiscoverableWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"discoverableWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]}}]}}]} as unknown as DocumentNode; +export const GetActiveUserDiscoverableWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"getActiveUserDiscoverableWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"discoverableWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const UpdateWorkspaceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateWorkspace"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"update"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]} as unknown as DocumentNode; export const GetActiveUserWorkspacesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetActiveUserWorkspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaces"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspace"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]} as unknown as DocumentNode; export const UpdateWorkspaceRoleDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateWorkspaceRole"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceRoleUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"updateRole"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"TestWorkspaceCollaborator"}}]}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TestWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/server/test/graphql/workspaces.ts b/packages/server/test/graphql/workspaces.ts index 6fcceda74..a0840d567 100644 --- a/packages/server/test/graphql/workspaces.ts +++ b/packages/server/test/graphql/workspaces.ts @@ -99,6 +99,13 @@ export const getActiveUserDiscoverableWorkspacesQuery = gql` id name description + team { + items { + avatar + } + totalCount + cursor + } } } } From 169f4204a88b9a7a3d20907a4de05fd46132ac69 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 25 Feb 2025 16:16:29 +0100 Subject: [PATCH 35/56] Feat: Show approved/denied workspace request in table, various bug fixes (#4063) --- .../settings/user/email/DeleteDialog.vue | 17 ++++--- .../settings/user/email/ListItem.vue | 6 +-- .../workspaces/members/GuestsTable.vue | 9 +--- .../workspaces/members/JoinRequestsTable.vue | 44 +++++++++++++--- .../workspaces/members/MembersTable.vue | 9 +--- .../components/workspace/sidebar/Members.vue | 22 ++++---- .../lib/common/generated/gql/gql.ts | 17 +++---- .../lib/common/generated/gql/graphql.ts | 51 +++++++++---------- .../frontend-2/lib/user/composables/emails.ts | 18 ++++--- .../workspaces/composables/joinRequests.ts | 41 ++++----------- .../lib/workspaces/graphql/fragments.ts | 4 ++ .../settings/workspaces/[slug]/members.vue | 13 +++-- packages/frontend-2/pages/verify-email.vue | 2 +- 13 files changed, 123 insertions(+), 130 deletions(-) diff --git a/packages/frontend-2/components/settings/user/email/DeleteDialog.vue b/packages/frontend-2/components/settings/user/email/DeleteDialog.vue index 6e9f7ef44..28d903269 100644 --- a/packages/frontend-2/components/settings/user/email/DeleteDialog.vue +++ b/packages/frontend-2/components/settings/user/email/DeleteDialog.vue @@ -1,14 +1,14 @@ diff --git a/packages/frontend-2/components/settings/workspaces/members/GuestsTable.vue b/packages/frontend-2/components/settings/workspaces/members/GuestsTable.vue index ae2b45a78..d42db5c23 100644 --- a/packages/frontend-2/components/settings/workspaces/members/GuestsTable.vue +++ b/packages/frontend-2/components/settings/workspaces/members/GuestsTable.vue @@ -11,9 +11,8 @@ :columns="[ { id: 'name', header: 'Name', classes: 'col-span-3' }, { id: 'company', header: 'Company', classes: 'col-span-3' }, - { id: 'verified', header: 'Status', classes: 'col-span-3' }, { id: 'projects', header: 'Projects', classes: 'col-span-2' }, - { id: 'actions', header: '', classes: 'col-span-1 flex justify-end' } + { id: 'actions', header: '', classes: 'col-span-4 flex justify-end' } ]" :items="guests" :loading="searchResultLoading" @@ -36,11 +35,6 @@ {{ item.user.company ? item.user.company : '-' }} - + @@ -45,10 +55,13 @@ diff --git a/packages/frontend-2/lib/common/generated/gql/gql.ts b/packages/frontend-2/lib/common/generated/gql/gql.ts index 7f6a6569d..3e3e46815 100644 --- a/packages/frontend-2/lib/common/generated/gql/gql.ts +++ b/packages/frontend-2/lib/common/generated/gql/gql.ts @@ -119,12 +119,12 @@ const documents = { "\n fragment SettingsWorkspacesGeneralEditAvatar_Workspace on Workspace {\n id\n logo\n name\n }\n": types.SettingsWorkspacesGeneralEditAvatar_WorkspaceFragmentDoc, "\n fragment SettingsWorkspacesGeneralEditSlugDialog_Workspace on Workspace {\n id\n name\n slug\n }\n": types.SettingsWorkspacesGeneralEditSlugDialog_WorkspaceFragmentDoc, "\n fragment SettingsWorkspacesMembersChangeRoleDialog_Workspace on Workspace {\n id\n plan {\n status\n name\n }\n subscription {\n currentBillingCycleEnd\n seats {\n guest\n plan\n }\n }\n }\n": types.SettingsWorkspacesMembersChangeRoleDialog_WorkspaceFragmentDoc, - "\n fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n verified\n }\n projectRoles {\n role\n project {\n id\n name\n }\n }\n }\n": types.SettingsWorkspacesMembersGuestsTable_WorkspaceCollaboratorFragmentDoc, + "\n fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n }\n projectRoles {\n role\n project {\n id\n name\n }\n }\n }\n": types.SettingsWorkspacesMembersGuestsTable_WorkspaceCollaboratorFragmentDoc, "\n fragment SettingsWorkspacesMembersGuestsTable_Workspace on Workspace {\n id\n ...SettingsWorkspacesMembersTableHeader_Workspace\n ...SettingsSharedDeleteUserDialog_Workspace\n ...SettingsWorkspacesMembersChangeRoleDialog_Workspace\n team {\n items {\n id\n ...SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator\n }\n }\n }\n": types.SettingsWorkspacesMembersGuestsTable_WorkspaceFragmentDoc, "\n fragment SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n inviteId\n role\n title\n updatedAt\n user {\n id\n ...LimitedUserAvatar\n }\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n }\n": types.SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaboratorFragmentDoc, "\n fragment SettingsWorkspacesMembersInvitesTable_Workspace on Workspace {\n id\n ...SettingsWorkspacesMembersTableHeader_Workspace\n invitedTeam(filter: $invitesFilter) {\n ...SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator\n }\n }\n": types.SettingsWorkspacesMembersInvitesTable_WorkspaceFragmentDoc, "\n fragment SettingsWorkspacesMembersRequestsTable_Workspace on Workspace {\n ...SettingsWorkspacesMembersTableHeader_Workspace\n id\n adminWorkspacesJoinRequests(filter: $joinRequestsFilter) {\n totalCount\n items {\n ...WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest\n id\n createdAt\n status\n user {\n id\n avatar\n name\n }\n }\n }\n }\n": types.SettingsWorkspacesMembersRequestsTable_WorkspaceFragmentDoc, - "\n fragment SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n verified\n workspaceDomainPolicyCompliant\n }\n }\n": types.SettingsWorkspacesMembersMembersTable_WorkspaceCollaboratorFragmentDoc, + "\n fragment SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n workspaceDomainPolicyCompliant\n }\n }\n": types.SettingsWorkspacesMembersMembersTable_WorkspaceCollaboratorFragmentDoc, "\n fragment SettingsWorkspacesMembersMembersTable_Workspace on Workspace {\n id\n name\n ...SettingsSharedDeleteUserDialog_Workspace\n ...SettingsWorkspacesMembersTableHeader_Workspace\n ...SettingsWorkspacesMembersChangeRoleDialog_Workspace\n team {\n items {\n id\n ...SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator\n }\n }\n }\n": types.SettingsWorkspacesMembersMembersTable_WorkspaceFragmentDoc, "\n fragment SettingsWorkspacesMembersTableHeader_Workspace on Workspace {\n id\n role\n ...InviteDialogWorkspace_Workspace\n }\n": types.SettingsWorkspacesMembersTableHeader_WorkspaceFragmentDoc, "\n fragment SettingsWorkspacesRegionsSelect_ServerRegionItem on ServerRegionItem {\n id\n key\n name\n description\n }\n": types.SettingsWorkspacesRegionsSelect_ServerRegionItemFragmentDoc, @@ -145,7 +145,6 @@ const documents = { "\n fragment WorkspaceInviteDiscoverableWorkspaceBanner_LimitedWorkspace on LimitedWorkspace {\n id\n name\n slug\n description\n logo\n }\n": types.WorkspaceInviteDiscoverableWorkspaceBanner_LimitedWorkspaceFragmentDoc, "\n fragment WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest on WorkspaceJoinRequest {\n id\n user {\n id\n name\n }\n workspace {\n id\n }\n }\n": types.WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequestFragmentDoc, "\n fragment WorkspaceSidebarAbout_Workspace on Workspace {\n ...WorkspaceDashboardAbout_Workspace\n }\n": types.WorkspaceSidebarAbout_WorkspaceFragmentDoc, - "\n fragment WorkspaceSidebarMembers_Workspace on Workspace {\n ...WorkspaceTeam_Workspace\n }\n": types.WorkspaceSidebarMembers_WorkspaceFragmentDoc, "\n fragment WorkspaceSidebarSecurity_Workspace on Workspace {\n ...WorkspaceSecurity_Workspace\n }\n": types.WorkspaceSidebarSecurity_WorkspaceFragmentDoc, "\n fragment WorkspaceSidebar_Workspace on Workspace {\n ...WorkspaceDashboardAbout_Workspace\n ...WorkspaceTeam_Workspace\n ...WorkspaceSecurity_Workspace\n slug\n plan {\n status\n }\n }\n": types.WorkspaceSidebar_WorkspaceFragmentDoc, "\n fragment WorkspaceWizard_Workspace on Workspace {\n creationState {\n completed\n state\n }\n name\n slug\n }\n": types.WorkspaceWizard_WorkspaceFragmentDoc, @@ -350,7 +349,7 @@ const documents = { "\n fragment WorkspaceBase_Workspace on Workspace {\n id\n name\n slug\n role\n description\n logo\n plan {\n status\n createdAt\n }\n }\n": types.WorkspaceBase_WorkspaceFragmentDoc, "\n fragment WorkspaceDashboardAbout_Workspace on Workspace {\n id\n name\n description\n }\n": types.WorkspaceDashboardAbout_WorkspaceFragmentDoc, "\n fragment WorkspaceInvitedTeam_Workspace on Workspace {\n id\n invitedTeam(filter: $invitesFilter) {\n id\n role\n email\n }\n }\n": types.WorkspaceInvitedTeam_WorkspaceFragmentDoc, - "\n fragment WorkspaceTeam_Workspace on Workspace {\n id\n slug\n team {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n adminWorkspacesJoinRequests {\n totalCount\n }\n ...WorkspaceInvitedTeam_Workspace\n }\n": types.WorkspaceTeam_WorkspaceFragmentDoc, + "\n fragment WorkspaceTeam_Workspace on Workspace {\n id\n slug\n team {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n adminWorkspacesJoinRequests {\n totalCount\n items {\n status\n id\n }\n }\n ...WorkspaceInvitedTeam_Workspace\n }\n": types.WorkspaceTeam_WorkspaceFragmentDoc, "\n fragment WorkspaceSecurity_Workspace on Workspace {\n id\n slug\n domains {\n id\n domain\n }\n }\n": types.WorkspaceSecurity_WorkspaceFragmentDoc, "\n mutation UpdateRole($input: WorkspaceRoleUpdateInput!) {\n workspaceMutations {\n updateRole(input: $input) {\n team {\n items {\n id\n role\n }\n }\n }\n }\n }\n": types.UpdateRoleDocument, "\n mutation InviteToWorkspace(\n $workspaceId: String!\n $input: [WorkspaceInviteCreateInput!]!\n ) {\n workspaceMutations {\n invites {\n batchCreate(workspaceId: $workspaceId, input: $input) {\n id\n invitedTeam {\n ...SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator\n }\n }\n }\n }\n }\n": types.InviteToWorkspaceDocument, @@ -836,7 +835,7 @@ export function graphql(source: "\n fragment SettingsWorkspacesMembersChangeRol /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n verified\n }\n projectRoles {\n role\n project {\n id\n name\n }\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n verified\n }\n projectRoles {\n role\n project {\n id\n name\n }\n }\n }\n"]; +export function graphql(source: "\n fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n }\n projectRoles {\n role\n project {\n id\n name\n }\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n }\n projectRoles {\n role\n project {\n id\n name\n }\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -856,7 +855,7 @@ export function graphql(source: "\n fragment SettingsWorkspacesMembersRequestsT /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n fragment SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n verified\n workspaceDomainPolicyCompliant\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n verified\n workspaceDomainPolicyCompliant\n }\n }\n"]; +export function graphql(source: "\n fragment SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n workspaceDomainPolicyCompliant\n }\n }\n"): (typeof documents)["\n fragment SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n workspaceDomainPolicyCompliant\n }\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -937,10 +936,6 @@ export function graphql(source: "\n fragment WorkspaceJoinRequestApproveDialog_ * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ export function graphql(source: "\n fragment WorkspaceSidebarAbout_Workspace on Workspace {\n ...WorkspaceDashboardAbout_Workspace\n }\n"): (typeof documents)["\n fragment WorkspaceSidebarAbout_Workspace on Workspace {\n ...WorkspaceDashboardAbout_Workspace\n }\n"]; -/** - * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. - */ -export function graphql(source: "\n fragment WorkspaceSidebarMembers_Workspace on Workspace {\n ...WorkspaceTeam_Workspace\n }\n"): (typeof documents)["\n fragment WorkspaceSidebarMembers_Workspace on Workspace {\n ...WorkspaceTeam_Workspace\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ @@ -1760,7 +1755,7 @@ export function graphql(source: "\n fragment WorkspaceInvitedTeam_Workspace on /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n fragment WorkspaceTeam_Workspace on Workspace {\n id\n slug\n team {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n adminWorkspacesJoinRequests {\n totalCount\n }\n ...WorkspaceInvitedTeam_Workspace\n }\n"): (typeof documents)["\n fragment WorkspaceTeam_Workspace on Workspace {\n id\n slug\n team {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n adminWorkspacesJoinRequests {\n totalCount\n }\n ...WorkspaceInvitedTeam_Workspace\n }\n"]; +export function graphql(source: "\n fragment WorkspaceTeam_Workspace on Workspace {\n id\n slug\n team {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n adminWorkspacesJoinRequests {\n totalCount\n items {\n status\n id\n }\n }\n ...WorkspaceInvitedTeam_Workspace\n }\n"): (typeof documents)["\n fragment WorkspaceTeam_Workspace on Workspace {\n id\n slug\n team {\n totalCount\n items {\n id\n user {\n id\n name\n ...LimitedUserAvatar\n }\n }\n }\n adminWorkspacesJoinRequests {\n totalCount\n items {\n status\n id\n }\n }\n ...WorkspaceInvitedTeam_Workspace\n }\n"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/packages/frontend-2/lib/common/generated/gql/graphql.ts b/packages/frontend-2/lib/common/generated/gql/graphql.ts index 9100e2e82..09fa05c13 100644 --- a/packages/frontend-2/lib/common/generated/gql/graphql.ts +++ b/packages/frontend-2/lib/common/generated/gql/graphql.ts @@ -5000,9 +5000,9 @@ export type SettingsWorkspacesGeneralEditSlugDialog_WorkspaceFragment = { __type export type SettingsWorkspacesMembersChangeRoleDialog_WorkspaceFragment = { __typename?: 'Workspace', id: string, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, name: WorkspacePlans } | null, subscription?: { __typename?: 'WorkspaceSubscription', currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null }; -export type SettingsWorkspacesMembersGuestsTable_WorkspaceCollaboratorFragment = { __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string, company?: string | null, verified?: boolean | null }, projectRoles: Array<{ __typename?: 'ProjectRole', role: string, project: { __typename?: 'Project', id: string, name: string } }> }; +export type SettingsWorkspacesMembersGuestsTable_WorkspaceCollaboratorFragment = { __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string, company?: string | null }, projectRoles: Array<{ __typename?: 'ProjectRole', role: string, project: { __typename?: 'Project', id: string, name: string } }> }; -export type SettingsWorkspacesMembersGuestsTable_WorkspaceFragment = { __typename?: 'Workspace', id: string, role?: string | null, domainBasedMembershipProtectionEnabled: boolean, team: { __typename?: 'WorkspaceCollaboratorCollection', items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string, company?: string | null, verified?: boolean | null }, projectRoles: Array<{ __typename?: 'ProjectRole', role: string, project: { __typename?: 'Project', id: string, name: string } }> }> }, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, name: WorkspacePlans } | null, subscription?: { __typename?: 'WorkspaceSubscription', currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', domain: string, id: string }> | null }; +export type SettingsWorkspacesMembersGuestsTable_WorkspaceFragment = { __typename?: 'Workspace', id: string, role?: string | null, domainBasedMembershipProtectionEnabled: boolean, team: { __typename?: 'WorkspaceCollaboratorCollection', items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string, company?: string | null }, projectRoles: Array<{ __typename?: 'ProjectRole', role: string, project: { __typename?: 'Project', id: string, name: string } }> }> }, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, name: WorkspacePlans } | null, subscription?: { __typename?: 'WorkspaceSubscription', currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', domain: string, id: string }> | null }; export type SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaboratorFragment = { __typename?: 'PendingWorkspaceCollaborator', id: string, inviteId: string, role: string, title: string, updatedAt: string, user?: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }; @@ -5010,9 +5010,9 @@ export type SettingsWorkspacesMembersInvitesTable_WorkspaceFragment = { __typena export type SettingsWorkspacesMembersRequestsTable_WorkspaceFragment = { __typename?: 'Workspace', id: string, role?: string | null, domainBasedMembershipProtectionEnabled: boolean, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', id: string, createdAt: string, status: WorkspaceJoinRequestStatus, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string }, workspace: { __typename?: 'Workspace', id: string } }> } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', domain: string, id: string }> | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, name: WorkspacePlans } | null, subscription?: { __typename?: 'WorkspaceSubscription', seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null }; -export type SettingsWorkspacesMembersMembersTable_WorkspaceCollaboratorFragment = { __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string, company?: string | null, verified?: boolean | null, workspaceDomainPolicyCompliant?: boolean | null } }; +export type SettingsWorkspacesMembersMembersTable_WorkspaceCollaboratorFragment = { __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string, company?: string | null, workspaceDomainPolicyCompliant?: boolean | null } }; -export type SettingsWorkspacesMembersMembersTable_WorkspaceFragment = { __typename?: 'Workspace', id: string, name: string, role?: string | null, domainBasedMembershipProtectionEnabled: boolean, team: { __typename?: 'WorkspaceCollaboratorCollection', items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string, company?: string | null, verified?: boolean | null, workspaceDomainPolicyCompliant?: boolean | null } }> }, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, name: WorkspacePlans } | null, subscription?: { __typename?: 'WorkspaceSubscription', currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', domain: string, id: string }> | null }; +export type SettingsWorkspacesMembersMembersTable_WorkspaceFragment = { __typename?: 'Workspace', id: string, name: string, role?: string | null, domainBasedMembershipProtectionEnabled: boolean, team: { __typename?: 'WorkspaceCollaboratorCollection', items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string, company?: string | null, workspaceDomainPolicyCompliant?: boolean | null } }> }, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, name: WorkspacePlans } | null, subscription?: { __typename?: 'WorkspaceSubscription', currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', domain: string, id: string }> | null }; export type SettingsWorkspacesMembersTableHeader_WorkspaceFragment = { __typename?: 'Workspace', id: string, role?: string | null, domainBasedMembershipProtectionEnabled: boolean, domains?: Array<{ __typename?: 'WorkspaceDomain', domain: string, id: string }> | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, name: WorkspacePlans } | null, subscription?: { __typename?: 'WorkspaceSubscription', seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null }; @@ -5036,11 +5036,11 @@ export type MoveProjectsDialog_WorkspaceFragment = { __typename?: 'Workspace', i export type MoveProjectsDialog_UserFragment = { __typename?: 'User', projects: { __typename?: 'UserProjectCollection', items: Array<{ __typename?: 'Project', role?: string | null, id: string, name: string, workspace?: { __typename?: 'Workspace', id: string } | null, modelCount: { __typename?: 'ModelCollection', totalCount: number }, versions: { __typename?: 'VersionCollection', totalCount: number } }> } }; -export type WorkspaceProjectList_WorkspaceFragment = { __typename?: 'Workspace', id: string, readOnly: boolean, name: string, slug: string, role?: string | null, description?: string | null, logo?: string | null, domainBasedMembershipProtectionEnabled: boolean, projects: { __typename?: 'ProjectCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, visibility: ProjectVisibility, modelCount: { __typename?: 'ModelCollection', totalCount: number }, versions: { __typename?: 'VersionCollection', totalCount: number }, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationsStatus?: { __typename?: 'TriggeredAutomationsStatus', id: string, automationRuns: Array<{ __typename?: 'AutomateRun', id: string, functionRuns: Array<{ __typename?: 'AutomateFunctionRun', id: string, updatedAt: string, status: AutomateRunStatus, results?: {} | null, statusMessage?: string | null, contextView?: string | null, createdAt: string, function?: { __typename?: 'AutomateFunction', id: string, logo?: string | null, name: string } | null }>, automation: { __typename?: 'Automation', id: string, name: string } }> } | null }> }, workspace?: { __typename?: 'Workspace', id: string, readOnly: boolean, slug: string, name: string, logo?: string | null } | null, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }> }, creationState?: { __typename?: 'WorkspaceCreationState', completed: boolean, state: {} } | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, createdAt: string, name: WorkspacePlans } | null, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', id: string, domain: string }> | null, subscription?: { __typename?: 'WorkspaceSubscription', billingInterval: BillingInterval, currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null, defaultRegion?: { __typename?: 'ServerRegionItem', id: string, name: string } | null }; +export type WorkspaceProjectList_WorkspaceFragment = { __typename?: 'Workspace', id: string, readOnly: boolean, name: string, slug: string, role?: string | null, description?: string | null, logo?: string | null, domainBasedMembershipProtectionEnabled: boolean, projects: { __typename?: 'ProjectCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, visibility: ProjectVisibility, modelCount: { __typename?: 'ModelCollection', totalCount: number }, versions: { __typename?: 'VersionCollection', totalCount: number }, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationsStatus?: { __typename?: 'TriggeredAutomationsStatus', id: string, automationRuns: Array<{ __typename?: 'AutomateRun', id: string, functionRuns: Array<{ __typename?: 'AutomateFunctionRun', id: string, updatedAt: string, status: AutomateRunStatus, results?: {} | null, statusMessage?: string | null, contextView?: string | null, createdAt: string, function?: { __typename?: 'AutomateFunction', id: string, logo?: string | null, name: string } | null }>, automation: { __typename?: 'Automation', id: string, name: string } }> } | null }> }, workspace?: { __typename?: 'Workspace', id: string, readOnly: boolean, slug: string, name: string, logo?: string | null } | null, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }> }, creationState?: { __typename?: 'WorkspaceCreationState', completed: boolean, state: {} } | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, createdAt: string, name: WorkspacePlans } | null, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', status: WorkspaceJoinRequestStatus, id: string }> } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', id: string, domain: string }> | null, subscription?: { __typename?: 'WorkspaceSubscription', billingInterval: BillingInterval, currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null, defaultRegion?: { __typename?: 'ServerRegionItem', id: string, name: string } | null }; export type WorkspaceProjectList_ProjectCollectionFragment = { __typename?: 'ProjectCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, visibility: ProjectVisibility, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationsStatus?: { __typename?: 'TriggeredAutomationsStatus', id: string, automationRuns: Array<{ __typename?: 'AutomateRun', id: string, functionRuns: Array<{ __typename?: 'AutomateFunctionRun', id: string, updatedAt: string, status: AutomateRunStatus, results?: {} | null, statusMessage?: string | null, contextView?: string | null, createdAt: string, function?: { __typename?: 'AutomateFunction', id: string, logo?: string | null, name: string } | null }>, automation: { __typename?: 'Automation', id: string, name: string } }> } | null }> }, workspace?: { __typename?: 'Workspace', id: string, readOnly: boolean, slug: string, name: string, logo?: string | null } | null, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }> }; -export type WorkspaceHeader_WorkspaceFragment = { __typename?: 'Workspace', slug: string, readOnly: boolean, id: string, name: string, role?: string | null, description?: string | null, logo?: string | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, createdAt: string, name: WorkspacePlans } | null, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number } | null, subscription?: { __typename?: 'WorkspaceSubscription', billingInterval: BillingInterval, currentBillingCycleEnd: string } | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null }; +export type WorkspaceHeader_WorkspaceFragment = { __typename?: 'Workspace', slug: string, readOnly: boolean, id: string, name: string, role?: string | null, description?: string | null, logo?: string | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, createdAt: string, name: WorkspacePlans } | null, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', status: WorkspaceJoinRequestStatus, id: string }> } | null, subscription?: { __typename?: 'WorkspaceSubscription', billingInterval: BillingInterval, currentBillingCycleEnd: string } | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null }; export type WorkspaceInviteBanner_PendingWorkspaceCollaboratorFragment = { __typename?: 'PendingWorkspaceCollaborator', id: string, workspaceId: string, workspaceName: string, token?: string | null, workspaceSlug: string, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, user?: { __typename?: 'LimitedUser', id: string } | null }; @@ -5052,11 +5052,9 @@ export type WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequestFragment = { _ export type WorkspaceSidebarAbout_WorkspaceFragment = { __typename?: 'Workspace', id: string, name: string, description?: string | null }; -export type WorkspaceSidebarMembers_WorkspaceFragment = { __typename?: 'Workspace', id: string, slug: string, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number } | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null }; - export type WorkspaceSidebarSecurity_WorkspaceFragment = { __typename?: 'Workspace', id: string, slug: string, domains?: Array<{ __typename?: 'WorkspaceDomain', id: string, domain: string }> | null }; -export type WorkspaceSidebar_WorkspaceFragment = { __typename?: 'Workspace', slug: string, id: string, name: string, description?: string | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses } | null, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', id: string, domain: string }> | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null }; +export type WorkspaceSidebar_WorkspaceFragment = { __typename?: 'Workspace', slug: string, id: string, name: string, description?: string | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses } | null, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', status: WorkspaceJoinRequestStatus, id: string }> } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', id: string, domain: string }> | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null }; export type WorkspaceWizard_WorkspaceFragment = { __typename?: 'Workspace', name: string, slug: string, creationState?: { __typename?: 'WorkspaceCreationState', completed: boolean, state: {} } | null }; @@ -6099,7 +6097,7 @@ export type SettingsWorkspacesMembersQueryVariables = Exact<{ }>; -export type SettingsWorkspacesMembersQuery = { __typename?: 'Query', workspaceBySlug: { __typename?: 'Workspace', id: string, role?: string | null, name: string, domainBasedMembershipProtectionEnabled: boolean, team: { __typename?: 'WorkspaceCollaboratorCollection', items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string, company?: string | null, verified?: boolean | null, workspaceDomainPolicyCompliant?: boolean | null }, projectRoles: Array<{ __typename?: 'ProjectRole', role: string, project: { __typename?: 'Project', id: string, name: string } }> }> }, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, inviteId: string, role: string, title: string, updatedAt: string, user?: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string } | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> | null, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', id: string, createdAt: string, status: WorkspaceJoinRequestStatus, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string }, workspace: { __typename?: 'Workspace', id: string } }> } | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, name: WorkspacePlans } | null, subscription?: { __typename?: 'WorkspaceSubscription', currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', id: string, domain: string }> | null } }; +export type SettingsWorkspacesMembersQuery = { __typename?: 'Query', workspaceBySlug: { __typename?: 'Workspace', id: string, role?: string | null, name: string, domainBasedMembershipProtectionEnabled: boolean, team: { __typename?: 'WorkspaceCollaboratorCollection', items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string, company?: string | null, workspaceDomainPolicyCompliant?: boolean | null }, projectRoles: Array<{ __typename?: 'ProjectRole', role: string, project: { __typename?: 'Project', id: string, name: string } }> }> }, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, inviteId: string, role: string, title: string, updatedAt: string, user?: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string } | null, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> | null, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', id: string, createdAt: string, status: WorkspaceJoinRequestStatus, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string }, workspace: { __typename?: 'Workspace', id: string } }> } | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, name: WorkspacePlans } | null, subscription?: { __typename?: 'WorkspaceSubscription', currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', id: string, domain: string }> | null } }; export type SettingsWorkspacesMembersSearchQueryVariables = Exact<{ slug: Scalars['String']['input']; @@ -6107,7 +6105,7 @@ export type SettingsWorkspacesMembersSearchQueryVariables = Exact<{ }>; -export type SettingsWorkspacesMembersSearchQuery = { __typename?: 'Query', workspaceBySlug: { __typename?: 'Workspace', id: string, team: { __typename?: 'WorkspaceCollaboratorCollection', items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string, company?: string | null, verified?: boolean | null, workspaceDomainPolicyCompliant?: boolean | null } }> } } }; +export type SettingsWorkspacesMembersSearchQuery = { __typename?: 'Query', workspaceBySlug: { __typename?: 'Workspace', id: string, team: { __typename?: 'WorkspaceCollaboratorCollection', items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, role: string, user: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string, company?: string | null, workspaceDomainPolicyCompliant?: boolean | null } }> } } }; export type SettingsWorkspacesJoinRequestsSearchQueryVariables = Exact<{ slug: Scalars['String']['input']; @@ -6330,7 +6328,7 @@ export type WorkspaceDashboardAbout_WorkspaceFragment = { __typename?: 'Workspac export type WorkspaceInvitedTeam_WorkspaceFragment = { __typename?: 'Workspace', id: string, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null }; -export type WorkspaceTeam_WorkspaceFragment = { __typename?: 'Workspace', id: string, slug: string, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number } | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null }; +export type WorkspaceTeam_WorkspaceFragment = { __typename?: 'Workspace', id: string, slug: string, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', status: WorkspaceJoinRequestStatus, id: string }> } | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null }; export type WorkspaceSecurity_WorkspaceFragment = { __typename?: 'Workspace', id: string, slug: string, domains?: Array<{ __typename?: 'WorkspaceDomain', id: string, domain: string }> | null }; @@ -6427,7 +6425,7 @@ export type WorkspacePageQueryQueryVariables = Exact<{ }>; -export type WorkspacePageQueryQuery = { __typename?: 'Query', workspaceBySlug: { __typename?: 'Workspace', id: string, readOnly: boolean, name: string, slug: string, role?: string | null, description?: string | null, logo?: string | null, domainBasedMembershipProtectionEnabled: boolean, projects: { __typename?: 'ProjectCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, visibility: ProjectVisibility, modelCount: { __typename?: 'ModelCollection', totalCount: number }, versions: { __typename?: 'VersionCollection', totalCount: number }, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationsStatus?: { __typename?: 'TriggeredAutomationsStatus', id: string, automationRuns: Array<{ __typename?: 'AutomateRun', id: string, functionRuns: Array<{ __typename?: 'AutomateFunctionRun', id: string, updatedAt: string, status: AutomateRunStatus, results?: {} | null, statusMessage?: string | null, contextView?: string | null, createdAt: string, function?: { __typename?: 'AutomateFunction', id: string, logo?: string | null, name: string } | null }>, automation: { __typename?: 'Automation', id: string, name: string } }> } | null }> }, workspace?: { __typename?: 'Workspace', id: string, readOnly: boolean, slug: string, name: string, logo?: string | null } | null, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }> }, creationState?: { __typename?: 'WorkspaceCreationState', completed: boolean, state: {} } | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, createdAt: string, name: WorkspacePlans } | null, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', id: string, domain: string }> | null, subscription?: { __typename?: 'WorkspaceSubscription', billingInterval: BillingInterval, currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null, defaultRegion?: { __typename?: 'ServerRegionItem', id: string, name: string } | null }, workspaceInvite?: { __typename?: 'PendingWorkspaceCollaborator', id: string, workspaceId: string, workspaceName: string, token?: string | null, title: string, email?: string | null, workspaceSlug: string, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, user?: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string } | null } | null }; +export type WorkspacePageQueryQuery = { __typename?: 'Query', workspaceBySlug: { __typename?: 'Workspace', id: string, readOnly: boolean, name: string, slug: string, role?: string | null, description?: string | null, logo?: string | null, domainBasedMembershipProtectionEnabled: boolean, projects: { __typename?: 'ProjectCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, visibility: ProjectVisibility, modelCount: { __typename?: 'ModelCollection', totalCount: number }, versions: { __typename?: 'VersionCollection', totalCount: number }, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationsStatus?: { __typename?: 'TriggeredAutomationsStatus', id: string, automationRuns: Array<{ __typename?: 'AutomateRun', id: string, functionRuns: Array<{ __typename?: 'AutomateFunctionRun', id: string, updatedAt: string, status: AutomateRunStatus, results?: {} | null, statusMessage?: string | null, contextView?: string | null, createdAt: string, function?: { __typename?: 'AutomateFunction', id: string, logo?: string | null, name: string } | null }>, automation: { __typename?: 'Automation', id: string, name: string } }> } | null }> }, workspace?: { __typename?: 'Workspace', id: string, readOnly: boolean, slug: string, name: string, logo?: string | null } | null, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }> }, creationState?: { __typename?: 'WorkspaceCreationState', completed: boolean, state: {} } | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, createdAt: string, name: WorkspacePlans } | null, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', status: WorkspaceJoinRequestStatus, id: string }> } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', id: string, domain: string }> | null, subscription?: { __typename?: 'WorkspaceSubscription', billingInterval: BillingInterval, currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null, defaultRegion?: { __typename?: 'ServerRegionItem', id: string, name: string } | null }, workspaceInvite?: { __typename?: 'PendingWorkspaceCollaborator', id: string, workspaceId: string, workspaceName: string, token?: string | null, title: string, email?: string | null, workspaceSlug: string, invitedBy: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null }, user?: { __typename?: 'LimitedUser', id: string, avatar?: string | null, name: string } | null } | null }; export type WorkspaceProjectsQueryQueryVariables = Exact<{ workspaceSlug: Scalars['String']['input']; @@ -6499,7 +6497,7 @@ export type OnWorkspaceUpdatedSubscriptionVariables = Exact<{ }>; -export type OnWorkspaceUpdatedSubscription = { __typename?: 'Subscription', workspaceUpdated: { __typename?: 'WorkspaceUpdatedMessage', id: string, workspace: { __typename?: 'Workspace', id: string, readOnly: boolean, name: string, slug: string, role?: string | null, description?: string | null, logo?: string | null, domainBasedMembershipProtectionEnabled: boolean, projects: { __typename?: 'ProjectCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, visibility: ProjectVisibility, modelCount: { __typename?: 'ModelCollection', totalCount: number }, versions: { __typename?: 'VersionCollection', totalCount: number }, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationsStatus?: { __typename?: 'TriggeredAutomationsStatus', id: string, automationRuns: Array<{ __typename?: 'AutomateRun', id: string, functionRuns: Array<{ __typename?: 'AutomateFunctionRun', id: string, updatedAt: string, status: AutomateRunStatus, results?: {} | null, statusMessage?: string | null, contextView?: string | null, createdAt: string, function?: { __typename?: 'AutomateFunction', id: string, logo?: string | null, name: string } | null }>, automation: { __typename?: 'Automation', id: string, name: string } }> } | null }> }, workspace?: { __typename?: 'Workspace', id: string, readOnly: boolean, slug: string, name: string, logo?: string | null } | null, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }> }, creationState?: { __typename?: 'WorkspaceCreationState', completed: boolean, state: {} } | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, createdAt: string, name: WorkspacePlans } | null, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', id: string, domain: string }> | null, subscription?: { __typename?: 'WorkspaceSubscription', billingInterval: BillingInterval, currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null, defaultRegion?: { __typename?: 'ServerRegionItem', id: string, name: string } | null } } }; +export type OnWorkspaceUpdatedSubscription = { __typename?: 'Subscription', workspaceUpdated: { __typename?: 'WorkspaceUpdatedMessage', id: string, workspace: { __typename?: 'Workspace', id: string, readOnly: boolean, name: string, slug: string, role?: string | null, description?: string | null, logo?: string | null, domainBasedMembershipProtectionEnabled: boolean, projects: { __typename?: 'ProjectCollection', totalCount: number, cursor?: string | null, items: Array<{ __typename?: 'Project', id: string, name: string, createdAt: string, updatedAt: string, role?: string | null, visibility: ProjectVisibility, modelCount: { __typename?: 'ModelCollection', totalCount: number }, versions: { __typename?: 'VersionCollection', totalCount: number }, models: { __typename?: 'ModelCollection', totalCount: number, items: Array<{ __typename?: 'Model', id: string, name: string, displayName: string, previewUrl?: string | null, createdAt: string, updatedAt: string, description?: string | null, versionCount: { __typename?: 'VersionCollection', totalCount: number }, commentThreadCount: { __typename?: 'CommentCollection', totalCount: number }, pendingImportedVersions: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, automationsStatus?: { __typename?: 'TriggeredAutomationsStatus', id: string, automationRuns: Array<{ __typename?: 'AutomateRun', id: string, functionRuns: Array<{ __typename?: 'AutomateFunctionRun', id: string, updatedAt: string, status: AutomateRunStatus, results?: {} | null, statusMessage?: string | null, contextView?: string | null, createdAt: string, function?: { __typename?: 'AutomateFunction', id: string, logo?: string | null, name: string } | null }>, automation: { __typename?: 'Automation', id: string, name: string } }> } | null }> }, workspace?: { __typename?: 'Workspace', id: string, readOnly: boolean, slug: string, name: string, logo?: string | null } | null, pendingImportedModels: Array<{ __typename?: 'FileUpload', id: string, projectId: string, modelName: string, convertedStatus: number, convertedMessage?: string | null, uploadDate: string, convertedLastUpdate: string, fileType: string, fileName: string }>, team: Array<{ __typename?: 'ProjectCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }> }, creationState?: { __typename?: 'WorkspaceCreationState', completed: boolean, state: {} } | null, plan?: { __typename?: 'WorkspacePlan', status: WorkspacePlanStatuses, createdAt: string, name: WorkspacePlans } | null, team: { __typename?: 'WorkspaceCollaboratorCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceCollaborator', id: string, user: { __typename?: 'LimitedUser', id: string, name: string, avatar?: string | null } }> }, adminWorkspacesJoinRequests?: { __typename?: 'WorkspaceJoinRequestCollection', totalCount: number, items: Array<{ __typename?: 'WorkspaceJoinRequest', status: WorkspaceJoinRequestStatus, id: string }> } | null, domains?: Array<{ __typename?: 'WorkspaceDomain', id: string, domain: string }> | null, subscription?: { __typename?: 'WorkspaceSubscription', billingInterval: BillingInterval, currentBillingCycleEnd: string, seats: { __typename?: 'WorkspaceSubscriptionSeats', guest: number, plan: number } } | null, invitedTeam?: Array<{ __typename?: 'PendingWorkspaceCollaborator', id: string, role: string, email?: string | null }> | null, defaultRegion?: { __typename?: 'ServerRegionItem', id: string, name: string } | null } } }; export type LegacyBranchRedirectMetadataQueryVariables = Exact<{ streamId: Scalars['String']['input']; @@ -6691,14 +6689,14 @@ export const InviteDialogWorkspace_WorkspaceFragmentDoc = {"kind":"Document","de export const SettingsWorkspacesMembersTableHeader_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}}]} as unknown as DocumentNode; export const SettingsSharedDeleteUserDialog_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}}]} as unknown as DocumentNode; export const SettingsWorkspacesMembersChangeRoleDialog_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}}]} as unknown as DocumentNode; -export const SettingsWorkspacesMembersGuestsTable_WorkspaceCollaboratorFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; -export const SettingsWorkspacesMembersGuestsTable_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; +export const SettingsWorkspacesMembersGuestsTable_WorkspaceCollaboratorFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; +export const SettingsWorkspacesMembersGuestsTable_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]} as unknown as DocumentNode; export const SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaboratorFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]} as unknown as DocumentNode; export const SettingsWorkspacesMembersInvitesTable_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]} as unknown as DocumentNode; export const WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequestFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceJoinRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const SettingsWorkspacesMembersRequestsTable_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersRequestsTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"joinRequestsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceJoinRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; -export const SettingsWorkspacesMembersMembersTable_WorkspaceCollaboratorFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceDomainPolicyCompliant"}}]}}]}}]} as unknown as DocumentNode; -export const SettingsWorkspacesMembersMembersTable_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceDomainPolicyCompliant"}}]}}]}}]} as unknown as DocumentNode; +export const SettingsWorkspacesMembersMembersTable_WorkspaceCollaboratorFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceDomainPolicyCompliant"}}]}}]}}]} as unknown as DocumentNode; +export const SettingsWorkspacesMembersMembersTable_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceDomainPolicyCompliant"}}]}}]}}]} as unknown as DocumentNode; export const SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomainFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceDomain"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domain"}}]}}]} as unknown as DocumentNode; export const SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesSecurityDomainRemoveDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceDomain"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domain"}}]}}]} as unknown as DocumentNode; export const ModelPageProjectFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ModelPageProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]} as unknown as DocumentNode; @@ -6707,19 +6705,18 @@ export const ProjectsMoveToWorkspaceDialog_ProjectFragmentDoc = {"kind":"Documen export const MoveProjectsDialog_UserFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MoveProjectsDialog_User"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Project"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","alias":{"kind":"Name","value":"modelCount"},"name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]} as unknown as DocumentNode; export const WorkspaceBase_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceBase_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}}]} as unknown as DocumentNode; export const WorkspaceInvitedTeam_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}}]} as unknown as DocumentNode; -export const WorkspaceTeam_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}}]} as unknown as DocumentNode; +export const WorkspaceTeam_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}}]} as unknown as DocumentNode; export const WorkspaceSecurity_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domain"}}]}}]}}]} as unknown as DocumentNode; export const BillingAlert_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BillingAlert_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billingInterval"}},{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}}]}}]}}]} as unknown as DocumentNode; export const MoveProjectsDialog_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","alias":{"kind":"Name","value":"modelCount"},"name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"}}]}}]} as unknown as DocumentNode; export const WorkspaceProjectList_ProjectCollectionFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardProject"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PendingFileUpload"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FileUpload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"modelName"}},{"kind":"Field","name":{"kind":"Name","value":"convertedStatus"}},{"kind":"Field","name":{"kind":"Name","value":"convertedMessage"}},{"kind":"Field","name":{"kind":"Name","value":"uploadDate"}},{"kind":"Field","name":{"kind":"Name","value":"convertedLastUpdate"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FunctionRunStatusForSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FunctionRunStatusForSummary"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"contextView"}},{"kind":"Field","name":{"kind":"Name","value":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","alias":{"kind":"Name","value":"versionCount"},"name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedVersions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions"}},{"kind":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedModels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}}]}}]} as unknown as DocumentNode; -export const WorkspaceProjectList_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceBase_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"BillingAlert_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"}}]}},{"kind":"Field","name":{"kind":"Name","value":"creationState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"completed"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardProject"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PendingFileUpload"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FileUpload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"modelName"}},{"kind":"Field","name":{"kind":"Name","value":"convertedStatus"}},{"kind":"Field","name":{"kind":"Name","value":"convertedMessage"}},{"kind":"Field","name":{"kind":"Name","value":"uploadDate"}},{"kind":"Field","name":{"kind":"Name","value":"convertedLastUpdate"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FunctionRunStatusForSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FunctionRunStatusForSummary"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"contextView"}},{"kind":"Field","name":{"kind":"Name","value":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","alias":{"kind":"Name","value":"versionCount"},"name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedVersions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions"}},{"kind":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedModels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceBase_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domain"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BillingAlert_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billingInterval"}},{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","alias":{"kind":"Name","value":"modelCount"},"name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}}]} as unknown as DocumentNode; -export const WorkspaceHeader_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceBase_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"BillingAlert_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceBase_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BillingAlert_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billingInterval"}},{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}}]}}]}}]} as unknown as DocumentNode; +export const WorkspaceProjectList_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceBase_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"BillingAlert_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"}}]}},{"kind":"Field","name":{"kind":"Name","value":"creationState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"completed"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardProject"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PendingFileUpload"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FileUpload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"modelName"}},{"kind":"Field","name":{"kind":"Name","value":"convertedStatus"}},{"kind":"Field","name":{"kind":"Name","value":"convertedMessage"}},{"kind":"Field","name":{"kind":"Name","value":"uploadDate"}},{"kind":"Field","name":{"kind":"Name","value":"convertedLastUpdate"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FunctionRunStatusForSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FunctionRunStatusForSummary"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"contextView"}},{"kind":"Field","name":{"kind":"Name","value":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","alias":{"kind":"Name","value":"versionCount"},"name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedVersions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions"}},{"kind":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedModels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceBase_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domain"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BillingAlert_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billingInterval"}},{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","alias":{"kind":"Name","value":"modelCount"},"name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}}]} as unknown as DocumentNode; +export const WorkspaceHeader_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceBase_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"BillingAlert_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceBase_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BillingAlert_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billingInterval"}},{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}}]}}]}}]} as unknown as DocumentNode; export const WorkspaceInviteBlock_PendingWorkspaceCollaboratorFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInviteBlock_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceSlug"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; export const WorkspaceDashboardAbout_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceDashboardAbout_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]} as unknown as DocumentNode; export const WorkspaceSidebarAbout_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSidebarAbout_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceDashboardAbout_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceDashboardAbout_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]} as unknown as DocumentNode; -export const WorkspaceSidebarMembers_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSidebarMembers_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}}]} as unknown as DocumentNode; export const WorkspaceSidebarSecurity_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSidebarSecurity_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domain"}}]}}]}}]} as unknown as DocumentNode; -export const WorkspaceSidebar_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSidebar_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceDashboardAbout_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceDashboardAbout_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domain"}}]}}]}}]} as unknown as DocumentNode; +export const WorkspaceSidebar_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSidebar_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceDashboardAbout_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceDashboardAbout_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domain"}}]}}]}}]} as unknown as DocumentNode; export const WorkspaceWizard_WorkspaceFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceWizard_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"creationState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"completed"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}}]} as unknown as DocumentNode; export const SettingsWorkspacesRegionsSelect_ServerRegionItemFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesRegionsSelect_ServerRegionItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerRegionItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]} as unknown as DocumentNode; export const WorkspaceWizardStepRegion_ServerInfoFragmentDoc = {"kind":"Document","definitions":[{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceWizardStepRegion_ServerInfo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerInfo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"multiRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"regions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesRegionsSelect_ServerRegionItem"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesRegionsSelect_ServerRegionItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerRegionItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}}]} as unknown as DocumentNode; @@ -6917,8 +6914,8 @@ export const SettingsWorkspaceGeneralDocument = {"kind":"Document","definitions" export const SettingsWorkspaceBillingDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SettingsWorkspaceBilling"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesBilling_Workspace"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BillingAlert_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billingInterval"}},{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesBilling_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"BillingAlert_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"paymentMethod"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billingInterval"}},{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}}]}}]} as unknown as DocumentNode; export const SettingsWorkspaceBillingCustomerPortalDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SettingsWorkspaceBillingCustomerPortal"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"customerPortalUrl"}}]}}]}}]} as unknown as DocumentNode; export const SettingsWorkspaceRegionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SettingsWorkspaceRegions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesRegions_Workspace"}}]}},{"kind":"Field","name":{"kind":"Name","value":"serverInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesRegions_ServerInfo"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesRegionsSelect_ServerRegionItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerRegionItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesRegions_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesRegionsSelect_ServerRegionItem"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"hasAccessToMultiRegion"},"name":{"kind":"Name","value":"hasAccessToFeature"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"featureName"},"value":{"kind":"EnumValue","value":"workspaceDataRegionSpecificity"}}]},{"kind":"Field","alias":{"kind":"Name","value":"hasProjects"},"name":{"kind":"Name","value":"projects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesRegions_ServerInfo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerInfo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"multiRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"regions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesRegionsSelect_ServerRegionItem"}}]}}]}}]}}]} as unknown as DocumentNode; -export const SettingsWorkspacesMembersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SettingsWorkspacesMembers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaboratorsFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"joinRequestsFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"AdminWorkspaceJoinRequestFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembers_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersRequestsTable_Workspace"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceDomainPolicyCompliant"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceJoinRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembers_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"joinRequestsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersRequestsTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"joinRequestsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]} as unknown as DocumentNode; -export const SettingsWorkspacesMembersSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SettingsWorkspacesMembersSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceTeamFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"verified"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceDomainPolicyCompliant"}}]}}]}}]} as unknown as DocumentNode; +export const SettingsWorkspacesMembersDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SettingsWorkspacesMembers"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaboratorsFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"joinRequestsFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"AdminWorkspaceJoinRequestFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembers_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersRequestsTable_Workspace"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}},{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceDomainPolicyCompliant"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}}]}},{"kind":"Field","name":{"kind":"Name","value":"projectRoles"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"project"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceJoinRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembers_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"joinRequestsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsSharedDeleteUserDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersChangeRoleDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersRequestsTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"joinRequestsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const SettingsWorkspacesMembersSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SettingsWorkspacesMembersSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceTeamFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceDomainPolicyCompliant"}}]}}]}}]} as unknown as DocumentNode; export const SettingsWorkspacesJoinRequestsSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SettingsWorkspacesJoinRequestsSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"joinRequestsFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"AdminWorkspaceJoinRequestFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersRequestsTable_Workspace"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceJoinRequest"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersRequestsTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"joinRequestsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const SettingsWorkspacesInvitesSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SettingsWorkspacesInvitesSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaboratorsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_Workspace"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inviteId"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersTableHeader_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator"}}]}}]}}]} as unknown as DocumentNode; export const SettingsWorkspacesProjectsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"SettingsWorkspacesProjects"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceProjectsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesProjects_ProjectCollection"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsDeleteDialog_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsSharedProjects_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsDeleteDialog_Project"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesProjects_ProjectCollection"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsSharedProjects_Project"}}]}}]}}]} as unknown as DocumentNode; @@ -6956,7 +6953,7 @@ export const WorkspaceUpdateDiscoverabilityMutationDocument = {"kind":"Document" export const ApproveWorkspaceJoinRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"ApproveWorkspaceJoinRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ApproveWorkspaceJoinRequestInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceJoinRequestMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"approve"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]}}]} as unknown as DocumentNode; export const DenyWorkspaceJoinRequestDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DenyWorkspaceJoinRequest"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DenyWorkspaceJoinRequestInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceJoinRequestMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"deny"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"input"},"value":{"kind":"Variable","name":{"kind":"Name","value":"input"}}}]}]}}]}}]} as unknown as DocumentNode; export const WorkspaceAccessCheckDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceAccessCheck"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode; -export const WorkspacePageQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspacePageQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaboratorsFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjectList_Workspace"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspaceInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}},{"kind":"Argument","name":{"kind":"Name","value":"options"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"useSlug"},"value":{"kind":"BooleanValue","value":true}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInviteBanner_PendingWorkspaceCollaborator"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInviteBlock_PendingWorkspaceCollaborator"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceBase_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domain"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BillingAlert_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billingInterval"}},{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","alias":{"kind":"Name","value":"modelCount"},"name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardProject"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PendingFileUpload"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FileUpload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"modelName"}},{"kind":"Field","name":{"kind":"Name","value":"convertedStatus"}},{"kind":"Field","name":{"kind":"Name","value":"convertedMessage"}},{"kind":"Field","name":{"kind":"Name","value":"uploadDate"}},{"kind":"Field","name":{"kind":"Name","value":"convertedLastUpdate"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FunctionRunStatusForSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FunctionRunStatusForSummary"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"contextView"}},{"kind":"Field","name":{"kind":"Name","value":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","alias":{"kind":"Name","value":"versionCount"},"name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedVersions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions"}},{"kind":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedModels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceSlug"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceBase_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"BillingAlert_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"}}]}},{"kind":"Field","name":{"kind":"Name","value":"creationState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"completed"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInviteBanner_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInviteBlock_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"}}]}}]} as unknown as DocumentNode; +export const WorkspacePageQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspacePageQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaboratorsFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjectList_Workspace"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspaceInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}},{"kind":"Argument","name":{"kind":"Name","value":"options"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"useSlug"},"value":{"kind":"BooleanValue","value":true}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInviteBanner_PendingWorkspaceCollaborator"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInviteBlock_PendingWorkspaceCollaborator"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceBase_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domain"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BillingAlert_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billingInterval"}},{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","alias":{"kind":"Name","value":"modelCount"},"name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardProject"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PendingFileUpload"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FileUpload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"modelName"}},{"kind":"Field","name":{"kind":"Name","value":"convertedStatus"}},{"kind":"Field","name":{"kind":"Name","value":"convertedMessage"}},{"kind":"Field","name":{"kind":"Name","value":"uploadDate"}},{"kind":"Field","name":{"kind":"Name","value":"convertedLastUpdate"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FunctionRunStatusForSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FunctionRunStatusForSummary"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"contextView"}},{"kind":"Field","name":{"kind":"Name","value":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","alias":{"kind":"Name","value":"versionCount"},"name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedVersions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions"}},{"kind":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedModels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceSlug"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceBase_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"BillingAlert_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"}}]}},{"kind":"Field","name":{"kind":"Name","value":"creationState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"completed"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInviteBanner_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInviteBlock_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"}}]}}]} as unknown as DocumentNode; export const WorkspaceProjectsQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceProjectsQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceProjectsFilter"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"10"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardProject"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PendingFileUpload"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FileUpload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"modelName"}},{"kind":"Field","name":{"kind":"Name","value":"convertedStatus"}},{"kind":"Field","name":{"kind":"Name","value":"convertedMessage"}},{"kind":"Field","name":{"kind":"Name","value":"uploadDate"}},{"kind":"Field","name":{"kind":"Name","value":"convertedLastUpdate"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FunctionRunStatusForSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FunctionRunStatusForSummary"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"contextView"}},{"kind":"Field","name":{"kind":"Name","value":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","alias":{"kind":"Name","value":"versionCount"},"name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedVersions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions"}},{"kind":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedModels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}}]} as unknown as DocumentNode; export const WorkspaceFunctionsQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceFunctionsQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateFunctionsPageHeader_Query"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"automateFunctions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsFunctionsCard_AutomateFunction"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateAutomationCreateDialog_AutomateFunction"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateFunctionCreateDialogTemplateStep_AutomateFunctionTemplate"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionTemplate"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"url"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsFunctionsCard_AutomateFunction"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"isFeatured"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"repo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"owner"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateAutomationCreateDialogFunctionParametersStep_AutomateFunction"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"releases"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"inputSchema"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateFunctionsPageHeader_Query"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Query"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"automateInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasAutomateGithubApp"}},{"kind":"Field","name":{"kind":"Name","value":"availableGithubOrgs"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"serverInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"automate"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"availableFunctionTemplates"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateFunctionCreateDialogTemplateStep_AutomateFunctionTemplate"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateAutomationCreateDialog_AutomateFunction"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunction"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsFunctionsCard_AutomateFunction"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateAutomationCreateDialogFunctionParametersStep_AutomateFunction"}}]}}]} as unknown as DocumentNode; export const WorkspaceInviteDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceInvite"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"token"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"options"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"WorkspaceInviteLookupOptions"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceInvite"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"token"},"value":{"kind":"Variable","name":{"kind":"Name","value":"token"}}},{"kind":"Argument","name":{"kind":"Name","value":"options"},"value":{"kind":"Variable","name":{"kind":"Name","value":"options"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInviteBanner_PendingWorkspaceCollaborator"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInviteBlock_PendingWorkspaceCollaborator"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceSlug"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInviteBanner_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedBy"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInviteBlock_PendingWorkspaceCollaborator"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaborator"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceId"}},{"kind":"Field","name":{"kind":"Name","value":"workspaceName"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"UseWorkspaceInviteManager_PendingWorkspaceCollaborator"}}]}}]} as unknown as DocumentNode; @@ -6966,7 +6963,7 @@ export const WorkspaceSsoByEmailDocument = {"kind":"Document","definitions":[{"k export const WorkspaceSsoCheckDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceSsoCheck"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"slug"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceBySlug"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"slug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"slug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceSsoStatus_Workspace"}}]}},{"kind":"Field","name":{"kind":"Name","value":"activeUser"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceSsoStatus_User"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSsoStatus_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"sso"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"provider"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"clientId"}},{"kind":"Field","name":{"kind":"Name","value":"issuerUrl"}}]}},{"kind":"Field","name":{"kind":"Name","value":"session"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"validUntil"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSsoStatus_User"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"User"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"expiredSsoSessions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}}]}}]} as unknown as DocumentNode; export const WorkspaceWizardDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceWizard"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspace"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceWizard_Workspace"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceWizard_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"creationState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"completed"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}}]} as unknown as DocumentNode; export const WorkspaceWizardRegionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"WorkspaceWizardRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceWizardStepRegion_ServerInfo"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"SettingsWorkspacesRegionsSelect_ServerRegionItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerRegionItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"key"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceWizardStepRegion_ServerInfo"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ServerInfo"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"multiRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"regions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"SettingsWorkspacesRegionsSelect_ServerRegionItem"}}]}}]}}]}}]} as unknown as DocumentNode; -export const OnWorkspaceUpdatedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"onWorkspaceUpdated"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaboratorsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceUpdated"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"workspaceSlug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjectList_Workspace"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceBase_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domain"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BillingAlert_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billingInterval"}},{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","alias":{"kind":"Name","value":"modelCount"},"name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardProject"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PendingFileUpload"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FileUpload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"modelName"}},{"kind":"Field","name":{"kind":"Name","value":"convertedStatus"}},{"kind":"Field","name":{"kind":"Name","value":"convertedMessage"}},{"kind":"Field","name":{"kind":"Name","value":"uploadDate"}},{"kind":"Field","name":{"kind":"Name","value":"convertedLastUpdate"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FunctionRunStatusForSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FunctionRunStatusForSummary"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"contextView"}},{"kind":"Field","name":{"kind":"Name","value":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","alias":{"kind":"Name","value":"versionCount"},"name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedVersions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions"}},{"kind":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedModels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceBase_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"BillingAlert_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"}}]}},{"kind":"Field","name":{"kind":"Name","value":"creationState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"completed"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]} as unknown as DocumentNode; +export const OnWorkspaceUpdatedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"onWorkspaceUpdated"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PendingWorkspaceCollaboratorsFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"workspaceUpdated"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"workspaceId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceId"}}},{"kind":"Argument","name":{"kind":"Name","value":"workspaceSlug"},"value":{"kind":"Variable","name":{"kind":"Name","value":"workspaceSlug"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjectList_Workspace"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceBase_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"LimitedUserAvatar"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"LimitedUser"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"invitedTeam"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"invitesFilter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"email"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"LimitedUserAvatar"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"adminWorkspacesJoinRequests"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceInvitedTeam_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domain"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"BillingAlert_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"billingInterval"}},{"kind":"Field","name":{"kind":"Name","value":"currentBillingCycleEnd"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"defaultRegion"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceHasCustomDataResidency_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsWorkspaceSelect_Workspace"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsMoveToWorkspaceDialog_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","alias":{"kind":"Name","value":"modelCount"},"name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"domainBasedMembershipProtectionEnabled"}},{"kind":"Field","name":{"kind":"Name","value":"domains"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"domain"}},{"kind":"Field","name":{"kind":"Name","value":"id"}}]}},{"kind":"Field","name":{"kind":"Name","value":"plan"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"seats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"guest"}},{"kind":"Field","name":{"kind":"Name","value":"plan"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsPageTeamDialogManagePermissions_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectsModelPageEmbed_Project"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardProject"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"visibility"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions_Project"}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"role"}},{"kind":"Field","name":{"kind":"Name","value":"team"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"user"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"avatar"}}]}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardProject"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"PendingFileUpload"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"FileUpload"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"projectId"}},{"kind":"Field","name":{"kind":"Name","value":"modelName"}},{"kind":"Field","name":{"kind":"Name","value":"convertedStatus"}},{"kind":"Field","name":{"kind":"Name","value":"convertedMessage"}},{"kind":"Field","name":{"kind":"Name","value":"uploadDate"}},{"kind":"Field","name":{"kind":"Name","value":"convertedLastUpdate"}},{"kind":"Field","name":{"kind":"Name","value":"fileType"}},{"kind":"Field","name":{"kind":"Name","value":"fileName"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageModelsActions"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"FunctionRunStatusForSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"FunctionRunStatusForSummary"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateFunctionRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"results"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"statusMessage"}},{"kind":"Field","name":{"kind":"Name","value":"contextView"}},{"kind":"Field","name":{"kind":"Name","value":"function"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automation"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"AutomateRun"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"functionRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRun"}}]}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomationsStatusOrderedRuns_AutomationRun"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"automationRuns"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialogRunsRows_AutomateRun"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"TriggeredAutomationsStatus"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"TriggeredAutomationsStatusSummary"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatus"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Model"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","alias":{"kind":"Name","value":"versionCount"},"name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"commentThreadCount"},"name":{"kind":"Name","value":"commentThreads"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"0"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedVersions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}},{"kind":"Field","name":{"kind":"Name","value":"previewUrl"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardRenameDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsCardDeleteDialog"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageModelsActions"}},{"kind":"Field","name":{"kind":"Name","value":"automationsStatus"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AutomateRunsTriggerStatus_TriggeredAutomationsStatus"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"ProjectDashboardItem"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Project"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItemNoModels"}},{"kind":"Field","name":{"kind":"Name","value":"models"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageLatestItemsModelItem"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"workspace"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"slug"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"logo"}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}},{"kind":"Field","name":{"kind":"Name","value":"pendingImportedModels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"4"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"PendingFileUpload"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectCollection"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectDashboardItem"}}]}},{"kind":"Field","name":{"kind":"Name","value":"cursor"}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WorkspaceProjectList_Workspace"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"Workspace"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceBase_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceTeam_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceSecurity_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"BillingAlert_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"MoveProjectsDialog_Workspace"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"InviteDialogWorkspace_Workspace"}},{"kind":"Field","name":{"kind":"Name","value":"projects"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WorkspaceProjectList_ProjectCollection"}}]}},{"kind":"Field","name":{"kind":"Name","value":"creationState"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"completed"}},{"kind":"Field","name":{"kind":"Name","value":"state"}}]}},{"kind":"Field","name":{"kind":"Name","value":"readOnly"}}]}}]} as unknown as DocumentNode; export const LegacyBranchRedirectMetadataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LegacyBranchRedirectMetadata"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"branchName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"modelByName"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"name"},"value":{"kind":"Variable","name":{"kind":"Name","value":"branchName"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]} as unknown as DocumentNode; export const LegacyViewerCommitRedirectMetadataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LegacyViewerCommitRedirectMetadata"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"commitId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"version"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"commitId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const LegacyViewerStreamRedirectMetadataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"LegacyViewerStreamRedirectMetadata"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"project"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"streamId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"versions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"1"}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalCount"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"model"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode; diff --git a/packages/frontend-2/lib/user/composables/emails.ts b/packages/frontend-2/lib/user/composables/emails.ts index 7e02bf7af..14f2a90a9 100644 --- a/packages/frontend-2/lib/user/composables/emails.ts +++ b/packages/frontend-2/lib/user/composables/emails.ts @@ -87,17 +87,23 @@ export function useUserEmails() { return false } - const deleteUserEmail = async (email: UserEmail, cancel = false) => { + const deleteUserEmail = async (options: { + email: UserEmail + hideToast?: boolean + }) => { + const { email, hideToast } = options const result = await deleteMutation({ input: { id: email.id } }).catch(convertThrowIntoFetchResult) if (result?.data) { - triggerNotification({ - type: ToastNotificationType.Success, - title: `${cancel ? 'Cancelled adding email' : 'Deleted email'}`, - description: email.email - }) + if (!hideToast) { + triggerNotification({ + type: ToastNotificationType.Success, + title: 'Deleted email', + description: email.email + }) + } mixpanel.track('Email Deleted') // If we're on the verify email page and there are no more unverified emails, redirect home diff --git a/packages/frontend-2/lib/workspaces/composables/joinRequests.ts b/packages/frontend-2/lib/workspaces/composables/joinRequests.ts index 5475b0e70..75296625d 100644 --- a/packages/frontend-2/lib/workspaces/composables/joinRequests.ts +++ b/packages/frontend-2/lib/workspaces/composables/joinRequests.ts @@ -3,9 +3,10 @@ import { approveWorkspaceJoinRequestMutation, denyWorkspaceJoinRequestMutation } from '~/lib/workspaces/graphql/mutations' -import type { - ApproveWorkspaceJoinRequestInput, - DenyWorkspaceJoinRequestInput +import { + type ApproveWorkspaceJoinRequestInput, + type DenyWorkspaceJoinRequestInput, + WorkspaceJoinRequestStatus } from '~~/lib/common/generated/gql/graphql' import { ToastNotificationType, useGlobalToast } from '~~/lib/common/composables/toast' import { @@ -30,22 +31,11 @@ export const useWorkspaceJoinRequest = () => { { input }, { update: (cache) => { - cache.evict({ - id: getCacheId('WorkspaceJoinRequest', requestId) - }) - modifyObjectField( cache, - getCacheId('Workspace', input.workspaceId), - 'adminWorkspacesJoinRequests', - ({ helpers: { createUpdatedValue } }) => { - return createUpdatedValue(({ update }) => { - update('totalCount', (totalCount) => totalCount - 1) - }) - }, - { - autoEvictFiltered: true - } + getCacheId('WorkspaceJoinRequest', requestId), + 'status', + () => WorkspaceJoinRequestStatus.Approved ) } } @@ -76,22 +66,11 @@ export const useWorkspaceJoinRequest = () => { { input }, { update: (cache) => { - cache.evict({ - id: getCacheId('WorkspaceJoinRequest', requestId) - }) - modifyObjectField( cache, - getCacheId('Workspace', input.workspaceId), - 'adminWorkspacesJoinRequests', - ({ helpers: { createUpdatedValue } }) => { - return createUpdatedValue(({ update }) => { - update('totalCount', (totalCount) => totalCount - 1) - }) - }, - { - autoEvictFiltered: true - } + getCacheId('WorkspaceJoinRequest', requestId), + 'status', + () => WorkspaceJoinRequestStatus.Denied ) } } diff --git a/packages/frontend-2/lib/workspaces/graphql/fragments.ts b/packages/frontend-2/lib/workspaces/graphql/fragments.ts index aab33e7f7..080fc9649 100644 --- a/packages/frontend-2/lib/workspaces/graphql/fragments.ts +++ b/packages/frontend-2/lib/workspaces/graphql/fragments.ts @@ -52,6 +52,10 @@ export const workspaceTeamFragment = graphql(` } adminWorkspacesJoinRequests { totalCount + items { + status + id + } } ...WorkspaceInvitedTeam_Workspace } diff --git a/packages/frontend-2/pages/settings/workspaces/[slug]/members.vue b/packages/frontend-2/pages/settings/workspaces/[slug]/members.vue index 064bd1197..7683c1a85 100644 --- a/packages/frontend-2/pages/settings/workspaces/[slug]/members.vue +++ b/packages/frontend-2/pages/settings/workspaces/[slug]/members.vue @@ -42,8 +42,7 @@ import { graphql } from '~/lib/common/generated/gql' import { settingsWorkspacesMembersQuery } from '~/lib/settings/graphql/queries' import type { LayoutPageTabItem } from '~~/lib/layout/helpers/components' import { useOnWorkspaceUpdated } from '~/lib/workspaces/composables/management' -import { WorkspaceJoinRequestStatus } from '~/lib/common/generated/gql/graphql' - +import { WorkspaceJoinRequestStatus } from '~~/lib/common/generated/gql/graphql' graphql(` fragment SettingsWorkspacesMembers_Workspace on Workspace { id @@ -76,10 +75,7 @@ const route = useRoute() const slug = computed(() => (route.params.slug as string) || '') const { result } = useQuery(settingsWorkspacesMembersQuery, () => ({ - slug: slug.value, - joinRequestsFilter: { - status: WorkspaceJoinRequestStatus.Pending - } + slug: slug.value })) const workspace = computed(() => result.value?.workspaceBySlug) @@ -96,7 +92,10 @@ const guestCount = computed( ) const invitedCount = computed(() => workspace.value?.invitedTeam?.length) const joinRequestCount = computed( - () => workspace.value?.adminWorkspacesJoinRequests?.totalCount + () => + workspace.value?.adminWorkspacesJoinRequests?.items.filter( + (item) => item.status === WorkspaceJoinRequestStatus.Pending + ).length ) const tabItems = computed(() => [ { title: 'Members', id: 'members', count: memberCount.value }, diff --git a/packages/frontend-2/pages/verify-email.vue b/packages/frontend-2/pages/verify-email.vue index f8c6d63b3..03389cd03 100644 --- a/packages/frontend-2/pages/verify-email.vue +++ b/packages/frontend-2/pages/verify-email.vue @@ -73,7 +73,7 @@ From 6daccd921f8817f475f8cf81ff11de41673ab361 Mon Sep 17 00:00:00 2001 From: Mike Date: Wed, 26 Feb 2025 10:25:26 +0100 Subject: [PATCH 36/56] Fix: Disable workspace menu items (#4068) --- .../components/settings/Sidebar.vue | 28 ++++++--------- .../lib/common/generated/gql/graphql.ts | 34 +++++++++++++++++++ .../layout/sidebar/menu/group/Item.vue | 1 + 3 files changed, 45 insertions(+), 18 deletions(-) diff --git a/packages/frontend-2/components/settings/Sidebar.vue b/packages/frontend-2/components/settings/Sidebar.vue index 4852b0497..522396d5a 100644 --- a/packages/frontend-2/components/settings/Sidebar.vue +++ b/packages/frontend-2/components/settings/Sidebar.vue @@ -93,7 +93,13 @@ - - - - - @@ -132,11 +131,10 @@ diff --git a/packages/frontend-2/components/automate/viewer/panel/FunctionRunRow.vue b/packages/frontend-2/components/automate/viewer/panel/FunctionRunRow.vue index ace26f50f..774e02c23 100644 --- a/packages/frontend-2/components/automate/viewer/panel/FunctionRunRow.vue +++ b/packages/frontend-2/components/automate/viewer/panel/FunctionRunRow.vue @@ -156,11 +156,13 @@ const hasValidContextView = computed(() => { }) const statusMessage = computed(() => { - const isFinished = ![ - AutomateRunStatus.Initializing, - AutomateRunStatus.Running, - AutomateRunStatus.Pending - ].includes(props.functionRun.status) + const isFinished = !( + [ + AutomateRunStatus.Initializing, + AutomateRunStatus.Running, + AutomateRunStatus.Pending + ] as string[] + ).includes(props.functionRun.status) return isFinished ? props.functionRun.statusMessage ?? 'No status message' diff --git a/packages/frontend-2/components/settings/workspaces/billing/PricingTable/Plan.vue b/packages/frontend-2/components/settings/workspaces/billing/PricingTable/Plan.vue index 933906ae0..3b283b453 100644 --- a/packages/frontend-2/components/settings/workspaces/billing/PricingTable/Plan.vue +++ b/packages/frontend-2/components/settings/workspaces/billing/PricingTable/Plan.vue @@ -5,7 +5,7 @@

Workspace - {{ plan.name }} + {{ plan }}

{{ badgeText }} @@ -15,8 +15,8 @@ £{{ yearlyIntervalSelected - ? plan.cost.yearly[Roles.Workspace.Member] - : plan.cost.monthly[Roles.Workspace.Member] + ? planPrices.yearly[Roles.Workspace.Member] + : planPrices.monthly[Roles.Workspace.Member] }} per seat/month @@ -40,7 +40,7 @@
  • - {{ feature.name }} + {{ featureMetadata.displayName }}
@@ -89,7 +92,7 @@ diff --git a/packages/frontend-2/pages/settings/workspaces/[slug]/members/guests.vue b/packages/frontend-2/pages/settings/workspaces/[slug]/members/guests.vue new file mode 100644 index 000000000..35d2d293f --- /dev/null +++ b/packages/frontend-2/pages/settings/workspaces/[slug]/members/guests.vue @@ -0,0 +1,17 @@ + + + diff --git a/packages/frontend-2/pages/settings/workspaces/[slug]/members/index.vue b/packages/frontend-2/pages/settings/workspaces/[slug]/members/index.vue new file mode 100644 index 000000000..7148a4e60 --- /dev/null +++ b/packages/frontend-2/pages/settings/workspaces/[slug]/members/index.vue @@ -0,0 +1,15 @@ + + + diff --git a/packages/frontend-2/pages/settings/workspaces/[slug]/members/invites.vue b/packages/frontend-2/pages/settings/workspaces/[slug]/members/invites.vue new file mode 100644 index 000000000..6db4496cc --- /dev/null +++ b/packages/frontend-2/pages/settings/workspaces/[slug]/members/invites.vue @@ -0,0 +1,20 @@ + + + diff --git a/packages/frontend-2/pages/settings/workspaces/[slug]/members/requests.vue b/packages/frontend-2/pages/settings/workspaces/[slug]/members/requests.vue new file mode 100644 index 000000000..56d086cd3 --- /dev/null +++ b/packages/frontend-2/pages/settings/workspaces/[slug]/members/requests.vue @@ -0,0 +1,20 @@ + + + diff --git a/packages/server/modules/workspaces/services/workspaceJoinRequestEmails/received.ts b/packages/server/modules/workspaces/services/workspaceJoinRequestEmails/received.ts index ad7026cf6..6ab9188b1 100644 --- a/packages/server/modules/workspaces/services/workspaceJoinRequestEmails/received.ts +++ b/packages/server/modules/workspaces/services/workspaceJoinRequestEmails/received.ts @@ -52,14 +52,14 @@ ${requester.name} is requesting to join your workspace ${workspace.name}. const buildEmailTemplateParams = (args: WorkspaceJoinRequestReceivedEmailArgs) => { const url = new URL( - `settings/workspaces/${args.workspace.slug}/members`, + `settings/workspaces/${args.workspace.slug}/members/requests`, getFrontendOrigin() ).toString() return { mjml: buildMjmlBody(args), text: buildTextBody(args), cta: { - title: 'Manage Members', + title: 'Review request', url } } From d5bd7ca34a0fd4508b07f650f35256be2ed6713d Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Thu, 27 Feb 2025 13:22:45 +0200 Subject: [PATCH 52/56] fix(fe2): broken GQLgen defs --- .../components/invite/dialog/CancelInvite.vue | 3 +++ .../lib/common/generated/gql/gql.ts | 19 +++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/frontend-2/components/invite/dialog/CancelInvite.vue b/packages/frontend-2/components/invite/dialog/CancelInvite.vue index e4da08545..d9d29a17d 100644 --- a/packages/frontend-2/components/invite/dialog/CancelInvite.vue +++ b/packages/frontend-2/components/invite/dialog/CancelInvite.vue @@ -38,4 +38,7 @@ const dialogButtons = computed((): LayoutDialogButton[] => [ } } ]) + +// testing deployments in prod, ignore this +markUsed('a') diff --git a/packages/frontend-2/lib/common/generated/gql/gql.ts b/packages/frontend-2/lib/common/generated/gql/gql.ts index 00678076d..80e821080 100644 --- a/packages/frontend-2/lib/common/generated/gql/gql.ts +++ b/packages/frontend-2/lib/common/generated/gql/gql.ts @@ -122,10 +122,10 @@ type Documents = { "\n fragment SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n }\n projectRoles {\n role\n project {\n id\n name\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersGuestsTable_WorkspaceCollaboratorFragmentDoc, "\n fragment SettingsWorkspacesMembersGuestsTable_Workspace on Workspace {\n id\n ...SettingsWorkspacesMembersTableHeader_Workspace\n ...SettingsSharedDeleteUserDialog_Workspace\n ...SettingsWorkspacesMembersChangeRoleDialog_Workspace\n team {\n items {\n id\n ...SettingsWorkspacesMembersGuestsTable_WorkspaceCollaborator\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersGuestsTable_WorkspaceFragmentDoc, "\n fragment SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator on PendingWorkspaceCollaborator {\n id\n inviteId\n role\n title\n updatedAt\n user {\n id\n ...LimitedUserAvatar\n }\n invitedBy {\n id\n ...LimitedUserAvatar\n }\n }\n": typeof types.SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaboratorFragmentDoc, - "\n fragment SettingsWorkspacesMembersInvitesTable_Workspace on Workspace {\n id\n ...SettingsWorkspacesMembersTableHeader_Workspace\n invitedTeam(filter: $invitesFilter) {\n ...SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator\n }\n }\n": typeof types.SettingsWorkspacesMembersInvitesTable_WorkspaceFragmentDoc, - "\n fragment SettingsWorkspacesMembersRequestsTable_Workspace on Workspace {\n ...SettingsWorkspacesMembersTableHeader_Workspace\n id\n adminWorkspacesJoinRequests(filter: $joinRequestsFilter) {\n totalCount\n items {\n ...WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest\n id\n createdAt\n status\n user {\n id\n avatar\n name\n }\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersRequestsTable_WorkspaceFragmentDoc, - "\n fragment SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n workspaceDomainPolicyCompliant\n }\n }\n": typeof types.SettingsWorkspacesMembersMembersTable_WorkspaceCollaboratorFragmentDoc, - "\n fragment SettingsWorkspacesMembersMembersTable_Workspace on Workspace {\n id\n name\n ...SettingsSharedDeleteUserDialog_Workspace\n ...SettingsWorkspacesMembersTableHeader_Workspace\n ...SettingsWorkspacesMembersChangeRoleDialog_Workspace\n team {\n items {\n id\n ...SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersMembersTable_WorkspaceFragmentDoc, + "\n fragment SettingsWorkspacesMembersInvitesTable_Workspace on Workspace {\n id\n ...SettingsWorkspacesMembersTableHeader_Workspace\n invitedTeam {\n ...SettingsWorkspacesMembersInvitesTable_PendingWorkspaceCollaborator\n }\n }\n": typeof types.SettingsWorkspacesMembersInvitesTable_WorkspaceFragmentDoc, + "\n fragment SettingsWorkspacesMembersRequestsTable_Workspace on Workspace {\n ...SettingsWorkspacesMembersTableHeader_Workspace\n id\n adminWorkspacesJoinRequests {\n totalCount\n items {\n ...WorkspaceJoinRequestApproveDialog_WorkspaceJoinRequest\n id\n createdAt\n status\n user {\n id\n avatar\n name\n }\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersRequestsTable_WorkspaceFragmentDoc, + "\n fragment SettingsWorkspacesMembersTable_WorkspaceCollaborator on WorkspaceCollaborator {\n id\n role\n user {\n id\n avatar\n name\n company\n workspaceDomainPolicyCompliant\n }\n }\n": typeof types.SettingsWorkspacesMembersTable_WorkspaceCollaboratorFragmentDoc, + "\n fragment SettingsWorkspacesMembersTable_Workspace on Workspace {\n id\n name\n ...SettingsSharedDeleteUserDialog_Workspace\n ...SettingsWorkspacesMembersTableHeader_Workspace\n ...SettingsWorkspacesMembersChangeRoleDialog_Workspace\n team {\n items {\n id\n ...SettingsWorkspacesMembersTable_WorkspaceCollaborator\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersTable_WorkspaceFragmentDoc, "\n fragment SettingsWorkspacesMembersTableHeader_Workspace on Workspace {\n id\n role\n ...InviteDialogWorkspace_Workspace\n }\n": typeof types.SettingsWorkspacesMembersTableHeader_WorkspaceFragmentDoc, "\n fragment SettingsWorkspacesRegionsSelect_ServerRegionItem on ServerRegionItem {\n id\n key\n name\n description\n }\n": typeof types.SettingsWorkspacesRegionsSelect_ServerRegionItemFragmentDoc, "\n fragment SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomain on WorkspaceDomain {\n id\n domain\n }\n": typeof types.SettingsWorkspacesSecurityDomainRemoveDialog_WorkspaceDomainFragmentDoc, @@ -307,9 +307,12 @@ type Documents = { "\n query SettingsWorkspaceBilling($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n ...SettingsWorkspacesBilling_Workspace\n }\n }\n": typeof types.SettingsWorkspaceBillingDocument, "\n query SettingsWorkspaceBillingCustomerPortal($workspaceId: String!) {\n workspace(id: $workspaceId) {\n customerPortalUrl\n }\n }\n": typeof types.SettingsWorkspaceBillingCustomerPortalDocument, "\n query SettingsWorkspaceRegions($slug: String!) {\n workspaceBySlug(slug: $slug) {\n id\n ...SettingsWorkspacesRegions_Workspace\n }\n serverInfo {\n ...SettingsWorkspacesRegions_ServerInfo\n }\n }\n": typeof types.SettingsWorkspaceRegionsDocument, - "\n query SettingsWorkspacesMembers(\n $slug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n $joinRequestsFilter: AdminWorkspaceJoinRequestFilter\n ) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembers_Workspace\n ...SettingsWorkspacesMembersMembersTable_Workspace\n ...SettingsWorkspacesMembersGuestsTable_Workspace\n ...SettingsWorkspacesMembersInvitesTable_Workspace\n ...SettingsWorkspacesMembersRequestsTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersDocument, - "\n query SettingsWorkspacesMembersSearch($slug: String!, $filter: WorkspaceTeamFilter) {\n workspaceBySlug(slug: $slug) {\n id\n team(filter: $filter) {\n items {\n id\n ...SettingsWorkspacesMembersMembersTable_WorkspaceCollaborator\n }\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersSearchDocument, - "\n query SettingsWorkspacesJoinRequestsSearch(\n $slug: String!\n $joinRequestsFilter: AdminWorkspaceJoinRequestFilter\n ) {\n workspaceBySlug(slug: $slug) {\n id\n ...SettingsWorkspacesMembersRequestsTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesJoinRequestsSearchDocument, + "\n query SettingsWorkspacesMembers($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembers_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersDocument, + "\n query SettingsWorkspacesMembersTable($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersTableDocument, + "\n query SettingsWorkspacesMembersGuests($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersGuestsTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersGuestsDocument, + "\n query SettingsWorkspacesMembersInvites($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersInvitesTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersInvitesDocument, + "\n query SettingsWorkspacesMembersRequests($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersRequestsTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesMembersRequestsDocument, + "\n query SettingsWorkspacesMembersSearch($slug: String!, $filter: WorkspaceTeamFilter) {\n workspaceBySlug(slug: $slug) {\n id\n team(filter: $filter) {\n items {\n id\n ...SettingsWorkspacesMembersTable_WorkspaceCollaborator\n }\n }\n }\n }\n": typeof types.SettingsWorkspacesMembersSearchDocument, "\n query SettingsWorkspacesInvitesSearch(\n $slug: String!\n $invitesFilter: PendingWorkspaceCollaboratorsFilter\n ) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesMembersInvitesTable_Workspace\n }\n }\n": typeof types.SettingsWorkspacesInvitesSearchDocument, "\n query SettingsWorkspacesProjects(\n $slug: String!\n $limit: Int!\n $cursor: String\n $filter: WorkspaceProjectsFilter\n ) {\n workspaceBySlug(slug: $slug) {\n id\n slug\n readOnly\n projects(limit: $limit, cursor: $cursor, filter: $filter) {\n cursor\n ...SettingsWorkspacesProjects_ProjectCollection\n }\n }\n }\n": typeof types.SettingsWorkspacesProjectsDocument, "\n query SettingsWorkspaceSecurity($slug: String!) {\n workspaceBySlug(slug: $slug) {\n ...SettingsWorkspacesSecurity_Workspace\n }\n activeUser {\n ...SettingsWorkspacesSecurity_User\n }\n }\n": typeof types.SettingsWorkspaceSecurityDocument, @@ -391,7 +394,7 @@ type Documents = { "\n query SettingsServerRegions {\n serverInfo {\n multiRegion {\n regions {\n id\n ...SettingsServerRegionsTable_ServerRegionItem\n }\n availableKeys\n }\n }\n }\n": typeof types.SettingsServerRegionsDocument, "\n fragment SettingsWorkspacesBilling_Workspace on Workspace {\n ...BillingAlert_Workspace\n id\n role\n plan {\n name\n status\n createdAt\n paymentMethod\n }\n subscription {\n billingInterval\n currentBillingCycleEnd\n seats {\n guest\n plan\n }\n }\n team {\n items {\n id\n role\n }\n }\n }\n": typeof types.SettingsWorkspacesBilling_WorkspaceFragmentDoc, "\n fragment SettingsWorkspacesGeneral_Workspace on Workspace {\n ...SettingsWorkspacesGeneralEditAvatar_Workspace\n ...SettingsWorkspaceGeneralDeleteDialog_Workspace\n ...SettingsWorkspacesGeneralEditSlugDialog_Workspace\n id\n name\n slug\n description\n logo\n role\n defaultProjectRole\n plan {\n status\n name\n }\n }\n": typeof types.SettingsWorkspacesGeneral_WorkspaceFragmentDoc, - "\n fragment SettingsWorkspacesMembers_Workspace on Workspace {\n id\n role\n team {\n items {\n id\n }\n }\n invitedTeam(filter: $invitesFilter) {\n user {\n id\n }\n }\n adminWorkspacesJoinRequests(filter: $joinRequestsFilter) {\n totalCount\n }\n }\n": typeof types.SettingsWorkspacesMembers_WorkspaceFragmentDoc, + "\n fragment SettingsWorkspacesMembers_Workspace on Workspace {\n id\n role\n team {\n items {\n id\n role\n }\n }\n invitedTeam {\n user {\n id\n }\n }\n adminWorkspacesJoinRequests {\n items {\n id\n status\n }\n totalCount\n }\n }\n": typeof types.SettingsWorkspacesMembers_WorkspaceFragmentDoc, "\n fragment SettingsWorkspacesProjects_ProjectCollection on ProjectCollection {\n totalCount\n items {\n ...SettingsSharedProjects_Project\n }\n }\n": typeof types.SettingsWorkspacesProjects_ProjectCollectionFragmentDoc, "\n fragment SettingsWorkspacesRegions_Workspace on Workspace {\n id\n role\n defaultRegion {\n id\n ...SettingsWorkspacesRegionsSelect_ServerRegionItem\n }\n hasAccessToMultiRegion: hasAccessToFeature(\n featureName: workspaceDataRegionSpecificity\n )\n hasProjects: projects(limit: 0) {\n totalCount\n }\n }\n": typeof types.SettingsWorkspacesRegions_WorkspaceFragmentDoc, "\n fragment SettingsWorkspacesRegions_ServerInfo on ServerInfo {\n multiRegion {\n regions {\n id\n ...SettingsWorkspacesRegionsSelect_ServerRegionItem\n }\n }\n }\n": typeof types.SettingsWorkspacesRegions_ServerInfoFragmentDoc, From 8c69f22e2775bfb04dc95ade742bd96b67d5d8b7 Mon Sep 17 00:00:00 2001 From: andrewwallacespeckle <139135120+andrewwallacespeckle@users.noreply.github.com> Date: Thu, 27 Feb 2025 11:29:00 +0000 Subject: [PATCH 53/56] feat: Update user tags in Mailchimp when user completes onboarding questions (#4000) * Add new updateMailchimpMemberTags function and use it in FE * Pass tag data from FE * Plan > Use case * move onboarding types to shared package * Check newsletter consent * Changes from PR * GQL --- .../components/onboarding/questions/Form.vue | 12 ++--- .../onboarding/questions/PlanSelect.vue | 3 +- .../onboarding/questions/RoleSelect.vue | 3 +- .../onboarding/questions/SourceSelect.vue | 3 +- .../lib/auth/composables/onboarding.ts | 19 +++++-- .../frontend-2/lib/auth/graphql/mutations.ts | 4 +- .../frontend-2/lib/auth/helpers/onboarding.ts | 36 +------------ .../lib/common/generated/gql/gql.ts | 6 +-- .../lib/common/generated/gql/graphql.ts | 19 +++++-- .../server/assets/core/typedefs/user.graphql | 8 ++- .../server/modules/auth/services/mailchimp.ts | 54 ++++++++++++++++++- .../modules/core/graph/generated/graphql.ts | 15 +++++- .../modules/core/graph/resolvers/users.ts | 31 ++++++++++- .../graph/generated/graphql.ts | 11 ++++ .../server/test/graphql/generated/graphql.ts | 11 ++++ packages/shared/src/index.ts | 1 + .../shared/src/onboarding/helpers/index.ts | 35 ++++++++++++ packages/shared/src/onboarding/index.ts | 1 + 18 files changed, 212 insertions(+), 60 deletions(-) create mode 100644 packages/shared/src/onboarding/helpers/index.ts create mode 100644 packages/shared/src/onboarding/index.ts diff --git a/packages/frontend-2/components/onboarding/questions/Form.vue b/packages/frontend-2/components/onboarding/questions/Form.vue index a2320ad59..0b181a8ca 100644 --- a/packages/frontend-2/components/onboarding/questions/Form.vue +++ b/packages/frontend-2/components/onboarding/questions/Form.vue @@ -26,11 +26,7 @@ diff --git a/packages/frontend-2/components/onboarding/questions/PlanSelect.vue b/packages/frontend-2/components/onboarding/questions/PlanSelect.vue index da21642ba..1de7f0b99 100644 --- a/packages/frontend-2/components/onboarding/questions/PlanSelect.vue +++ b/packages/frontend-2/components/onboarding/questions/PlanSelect.vue @@ -30,7 +30,8 @@