feat(fe2): automations status subscriptions (#1793)

* fe subscriptions working

* minor adjustment
This commit is contained in:
Kristaps Fabians Geikins
2023-09-18 15:03:42 +03:00
committed by GitHub
parent 79d293e178
commit eb441d7f4f
13 changed files with 272 additions and 26 deletions
@@ -0,0 +1,51 @@
import { ApolloCache } from '@apollo/client/core'
import { OnModelVersionCardAutomationsStatusUpdatedSubscription } from '~~/lib/common/generated/gql/graphql'
import { Get } from 'type-fest'
import { onModelVersionCardAutomationsStatusUpdated } from '~~/lib/automations/graphql/subscriptions'
import { useApolloClient, useSubscription } from '@vue/apollo-composable'
import { useLock } from '~~/lib/common/composables/singleton'
/**
* Track project model/version automations status updates and makes cache updates accordingly.
* Optionally you can provide an extra handler to be called when an event is received.
*/
export const useModelVersionCardAutomationsStatusUpdateTracking = (
projectId: MaybeRef<string>,
handler?: (
data: NonNullable<
Get<
OnModelVersionCardAutomationsStatusUpdatedSubscription,
'projectAutomationsStatusUpdated'
>
>,
cache: ApolloCache<unknown>
) => void
) => {
const { onResult } = useSubscription(
onModelVersionCardAutomationsStatusUpdated,
() => ({
projectId: unref(projectId)
})
)
const { hasLock } = useLock(
computed(
() => `useModelVersionCardAutomationsStatusUpdateTracking-${unref(projectId)}`
)
)
const apollo = useApolloClient().client
onResult((result) => {
if (!result.data?.projectAutomationsStatusUpdated || !hasLock.value) return
// Just by virtue of receiving this event the cache should be updated
// In case we need to do any global stuff, feel free to do it below:
})
onResult((result) => {
if (!result.data?.projectAutomationsStatusUpdated) return
const event = result.data.projectAutomationsStatusUpdated
handler?.(event, apollo.cache)
})
}
@@ -0,0 +1,11 @@
import { graphql } from '~~/lib/common/generated/gql'
export const onModelVersionCardAutomationsStatusUpdated = graphql(`
subscription OnModelVersionCardAutomationsStatusUpdated($projectId: String!) {
projectAutomationsStatusUpdated(projectId: $projectId) {
status {
...ModelCardAutomationStatus_AutomationsStatus
}
}
}
`)
@@ -70,6 +70,7 @@ const documents = {
"\n mutation FinishOnboarding {\n activeUserMutations {\n finishOnboarding\n }\n }\n": types.FinishOnboardingDocument,
"\n query AuthServerInfo {\n serverInfo {\n ...AuthStategiesServerInfoFragment\n ...ServerTermsOfServicePrivacyPolicyFragment\n ...AuthRegisterPanelServerInfo\n }\n }\n": types.AuthServerInfoDocument,
"\n query AuthorizableAppMetadata($id: String!) {\n app(id: $id) {\n id\n name\n description\n trustByDefault\n redirectUrl\n scopes {\n name\n description\n }\n author {\n name\n id\n avatar\n }\n }\n }\n": types.AuthorizableAppMetadataDocument,
"\n subscription OnModelVersionCardAutomationsStatusUpdated($projectId: String!) {\n projectAutomationsStatusUpdated(projectId: $projectId) {\n status {\n ...ModelCardAutomationStatus_AutomationsStatus\n }\n }\n }\n": types.OnModelVersionCardAutomationsStatusUpdatedDocument,
"\n query MentionsUserSearch($query: String!, $emailOnly: Boolean = false) {\n userSearch(\n query: $query\n limit: 5\n cursor: null\n archived: false\n emailOnly: $emailOnly\n ) {\n items {\n id\n name\n company\n }\n }\n }\n": types.MentionsUserSearchDocument,
"\n query UserSearch($query: String!, $limit: Int, $cursor: String, $archived: Boolean) {\n userSearch(query: $query, limit: $limit, cursor: $cursor, archived: $archived) {\n cursor\n items {\n id\n name\n bio\n company\n avatar\n verified\n role\n }\n }\n }\n": types.UserSearchDocument,
"\n query ServerInfoBlobSizeLimit {\n serverInfo {\n blobSizeLimitBytes\n }\n }\n": types.ServerInfoBlobSizeLimitDocument,
@@ -396,6 +397,10 @@ export function graphql(source: "\n query AuthServerInfo {\n serverInfo {\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 query AuthorizableAppMetadata($id: String!) {\n app(id: $id) {\n id\n name\n description\n trustByDefault\n redirectUrl\n scopes {\n name\n description\n }\n author {\n name\n id\n avatar\n }\n }\n }\n"): (typeof documents)["\n query AuthorizableAppMetadata($id: String!) {\n app(id: $id) {\n id\n name\n description\n trustByDefault\n redirectUrl\n scopes {\n name\n description\n }\n author {\n name\n id\n avatar\n }\n }\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 subscription OnModelVersionCardAutomationsStatusUpdated($projectId: String!) {\n projectAutomationsStatusUpdated(projectId: $projectId) {\n status {\n ...ModelCardAutomationStatus_AutomationsStatus\n }\n }\n }\n"): (typeof documents)["\n subscription OnModelVersionCardAutomationsStatusUpdated($projectId: String!) {\n projectAutomationsStatusUpdated(projectId: $projectId) {\n status {\n ...ModelCardAutomationStatus_AutomationsStatus\n }\n }\n }\n"];
/**
* The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
@@ -1463,6 +1463,14 @@ export type ProjectWebhooksArgs = {
id?: InputMaybe<Scalars['String']>;
};
export type ProjectAutomationsStatusUpdatedMessage = {
__typename?: 'ProjectAutomationsStatusUpdatedMessage';
model: Model;
project: Project;
status: AutomationsStatus;
version: Version;
};
export type ProjectCollaborator = {
__typename?: 'ProjectCollaborator';
role: Scalars['String'];
@@ -2329,6 +2337,7 @@ export type Subscription = {
commitDeleted?: Maybe<Scalars['JSONObject']>;
/** Subscribe to commit updated event. */
commitUpdated?: Maybe<Scalars['JSONObject']>;
projectAutomationsStatusUpdated: ProjectAutomationsStatusUpdatedMessage;
/**
* Subscribe to updates to resource comments/threads. Optionally specify resource ID string to only receive
* updates regarding comments for those resources.
@@ -2416,6 +2425,11 @@ export type SubscriptionCommitUpdatedArgs = {
};
export type SubscriptionProjectAutomationsStatusUpdatedArgs = {
projectId: Scalars['String'];
};
export type SubscriptionProjectCommentsUpdatedArgs = {
target: ViewerUpdateTrackingTarget;
};
@@ -2965,6 +2979,13 @@ export type AuthorizableAppMetadataQueryVariables = Exact<{
export type AuthorizableAppMetadataQuery = { __typename?: 'Query', app?: { __typename?: 'ServerApp', id: string, name: string, description?: string | null, trustByDefault?: boolean | null, redirectUrl: string, scopes: Array<{ __typename?: 'Scope', name: string, description: string }>, author?: { __typename?: 'AppAuthor', name?: string | null, id?: string | null, avatar?: string | null } | null } | null };
export type OnModelVersionCardAutomationsStatusUpdatedSubscriptionVariables = Exact<{
projectId: Scalars['String'];
}>;
export type OnModelVersionCardAutomationsStatusUpdatedSubscription = { __typename?: 'Subscription', projectAutomationsStatusUpdated: { __typename?: 'ProjectAutomationsStatusUpdatedMessage', status: { __typename?: 'AutomationsStatus', id: string, status: AutomationRunStatus, statusMessage?: string | null, automationRuns: Array<{ __typename?: 'AutomationRun', id: string, automationId: string, createdAt: string, status: AutomationRunStatus, functionRuns: Array<{ __typename?: 'AutomationFunctionRun', id: string, functionId: string, elapsed: number, status: AutomationRunStatus, statusMessage?: string | null, contextView?: string | null, resultVersions: Array<{ __typename?: 'Version', id: string }> }> }> } } };
export type MentionsUserSearchQueryVariables = Exact<{
query: Scalars['String'];
emailOnly?: InputMaybe<Scalars['Boolean']>;
@@ -3586,6 +3607,7 @@ export const CreateOnboardingProjectDocument = {"kind":"Document","definitions":
export const FinishOnboardingDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"FinishOnboarding"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"activeUserMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"finishOnboarding"}}]}}]}}]} as unknown as DocumentNode<FinishOnboardingMutation, FinishOnboardingMutationVariables>;
export const AuthServerInfoDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AuthServerInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"AuthStategiesServerInfoFragment"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ServerTermsOfServicePrivacyPolicyFragment"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"AuthRegisterPanelServerInfo"}}]}}]}},...AuthStategiesServerInfoFragmentFragmentDoc.definitions,...ServerTermsOfServicePrivacyPolicyFragmentFragmentDoc.definitions,...AuthRegisterPanelServerInfoFragmentDoc.definitions]} as unknown as DocumentNode<AuthServerInfoQuery, AuthServerInfoQueryVariables>;
export const AuthorizableAppMetadataDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"AuthorizableAppMetadata"},"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":"app"},"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":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"trustByDefault"}},{"kind":"Field","name":{"kind":"Name","value":"redirectUrl"}},{"kind":"Field","name":{"kind":"Name","value":"scopes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"description"}}]}},{"kind":"Field","name":{"kind":"Name","value":"author"},"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"}}]}}]}}]}}]} as unknown as DocumentNode<AuthorizableAppMetadataQuery, AuthorizableAppMetadataQueryVariables>;
export const OnModelVersionCardAutomationsStatusUpdatedDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"subscription","name":{"kind":"Name","value":"OnModelVersionCardAutomationsStatusUpdated"},"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":"projectAutomationsStatusUpdated"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"projectId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"status"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"ModelCardAutomationStatus_AutomationsStatus"}}]}}]}}]}},...ModelCardAutomationStatus_AutomationsStatusFragmentDoc.definitions]} as unknown as DocumentNode<OnModelVersionCardAutomationsStatusUpdatedSubscription, OnModelVersionCardAutomationsStatusUpdatedSubscriptionVariables>;
export const MentionsUserSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"MentionsUserSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"query"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"emailOnly"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}},"defaultValue":{"kind":"BooleanValue","value":false}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userSearch"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"query"},"value":{"kind":"Variable","name":{"kind":"Name","value":"query"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"IntValue","value":"5"}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"NullValue"}},{"kind":"Argument","name":{"kind":"Name","value":"archived"},"value":{"kind":"BooleanValue","value":false}},{"kind":"Argument","name":{"kind":"Name","value":"emailOnly"},"value":{"kind":"Variable","name":{"kind":"Name","value":"emailOnly"}}}],"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":"name"}},{"kind":"Field","name":{"kind":"Name","value":"company"}}]}}]}}]}}]} as unknown as DocumentNode<MentionsUserSearchQuery, MentionsUserSearchQueryVariables>;
export const UserSearchDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"UserSearch"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"query"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"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":"archived"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Boolean"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"userSearch"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"query"},"value":{"kind":"Variable","name":{"kind":"Name","value":"query"}}},{"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":"archived"},"value":{"kind":"Variable","name":{"kind":"Name","value":"archived"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"items"},"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"}},{"kind":"Field","name":{"kind":"Name","value":"role"}}]}}]}}]}}]} as unknown as DocumentNode<UserSearchQuery, UserSearchQueryVariables>;
export const ServerInfoBlobSizeLimitDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ServerInfoBlobSizeLimit"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"serverInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"blobSizeLimitBytes"}}]}}]}}]} as unknown as DocumentNode<ServerInfoBlobSizeLimitQuery, ServerInfoBlobSizeLimitQueryVariables>;
@@ -1,4 +1,5 @@
import { MaybeRef } from '@vueuse/core'
import { useModelVersionCardAutomationsStatusUpdateTracking } from '~~/lib/automations/composables/automationsStatus'
import { useSynchronizedCookie } from '~~/lib/common/composables/reactiveCookie'
import { GridListToggleValue } from '~~/lib/layout/helpers/components'
import {
@@ -60,4 +61,8 @@ export function useGeneralProjectPageUpdateTracking(
// Pending model & version update tracking
useProjectPendingVersionUpdateTracking(projectId)
useProjectPendingModelUpdateTracking(projectId)
// AUTOMATIONS:
// AutomationsStatus update
useModelVersionCardAutomationsStatusUpdateTracking(projectId)
}
@@ -117,3 +117,16 @@ type AutomationMutations {
extend type Mutation {
automationMutations: AutomationMutations!
}
type ProjectAutomationsStatusUpdatedMessage {
status: AutomationsStatus!
version: Version!
model: Model!
project: Project!
}
extend type Subscription {
projectAutomationsStatusUpdated(
projectId: String!
): ProjectAutomationsStatusUpdatedMessage!
}
@@ -5,6 +5,12 @@ import {
} from '@/modules/automations/services/management'
import { formatResults } from '@/modules/automations/services/results'
import { Resolvers } from '@/modules/core/graph/generated/graphql'
import { getStream } from '@/modules/core/repositories/streams'
import {
ProjectSubscriptions,
filteredSubscribe
} from '@/modules/shared/utils/subscriptions'
import { ForbiddenError } from 'apollo-server-express'
export = {
Model: {
@@ -67,5 +73,28 @@ export = {
await upsertModelAutomationRunResult({ userId, input: args.input })
return true
}
},
Subscription: {
projectAutomationsStatusUpdated: {
subscribe: filteredSubscribe(
ProjectSubscriptions.ProjectAutomationStatusUpdated,
async (payload, variables, context) => {
if (payload.projectId !== variables.projectId) return false
const stream = await getStream({
streamId: variables.projectId,
userId: context.userId
})
if (
!stream ||
(!(stream.isDiscoverable || stream.isPublic) && !stream.role)
) {
throw new ForbiddenError('You are not authorized.')
}
return true
}
)
}
}
} as Resolvers
@@ -30,10 +30,14 @@ import { AutomationFunctionRunGraphQLReturn } from '@/modules/automations/helper
import { AutomationRunSchema } from '@/modules/automations/helpers/inputTypes'
import { StreamNotFoundError } from '@/modules/core/errors/stream'
import { BranchNotFoundError } from '@/modules/core/errors/branch'
import { getCommits } from '@/modules/core/repositories/commits'
import {
getCommits,
getCommit,
getCommitBranch
} from '@/modules/core/repositories/commits'
import { AutomationNotFoundError } from '@/modules/automations/errors/automations'
import { getCommitById } from '@/modules/core/services/commits'
import { CommitNotFoundError } from '@/modules/core/errors/commit'
import { ProjectSubscriptions, publish } from '@/modules/shared/utils/subscriptions'
type AutomationRunWithFunctionRunsRecord = AutomationRunRecord & {
functionRuns: AutomationFunctionRunRecord[]
@@ -76,21 +80,23 @@ export async function upsertModelAutomationRunResult({
const automation = await getAutomation(input.automationId)
if (!automation) throw new AutomationNotFoundError()
// authz the current user on the automation
const stream = await getStream({
userId: userId || undefined,
streamId: automation.projectId
})
const [stream, version, model] = await Promise.all([
getStream({
userId: userId || undefined,
streamId: automation.projectId
}),
getCommit(validatedInput.versionId, {
streamId: automation.projectId
}),
getCommitBranch(validatedInput.versionId)
])
// this is never going to happen, cause the automation has an FK to the streamId
if (!stream) throw new StreamNotFoundError('Project not found')
if (stream.role !== Roles.Stream.Owner)
throw new ForbiddenError('Only project owners are allowed')
const version = await getCommitById({
streamId: automation.projectId,
id: validatedInput.versionId
})
if (!version) throw new CommitNotFoundError()
if (!model) throw new BranchNotFoundError()
// store the result of the run, if it already exists, patch it
const maybeAutomationRun = await getAutomationRun(input.automationRunId)
@@ -153,16 +159,23 @@ export async function upsertModelAutomationRunResult({
)
await insertAutomationFunctionRunResultVersion(validVersionsRecords)
// 4. publish an event for new automation run creation
// 5. publish an event for new run result update
// the last two events should be separated.
// automate should publish the new run linked to the model / version, with function run results as pending
// the running function should only update the function run result
// this is, so that FE subscriptions can be added properly.
// when a new run is triggered, the frontend should react to that
// also for the result of independent function run results.
// we're now shortcutting this until the one automation one function barrier is there.
// publish(automationRunStatusUpdate, { foo: 'bar' })
// Emit subscription
const newStatus = await getAutomationsStatus({
modelId: version.branchId,
projectId: stream.id,
versionId: version.id
})
if (newStatus) {
await publish(ProjectSubscriptions.ProjectAutomationStatusUpdated, {
projectId: stream.id,
projectAutomationsStatusUpdated: {
status: newStatus,
version,
project: stream,
model
}
})
}
}
const anyFunctionRunsHaveStatus = (
@@ -1476,6 +1476,14 @@ export type ProjectWebhooksArgs = {
id?: InputMaybe<Scalars['String']>;
};
export type ProjectAutomationsStatusUpdatedMessage = {
__typename?: 'ProjectAutomationsStatusUpdatedMessage';
model: Model;
project: Project;
status: AutomationsStatus;
version: Version;
};
export type ProjectCollaborator = {
__typename?: 'ProjectCollaborator';
role: Scalars['String'];
@@ -2342,6 +2350,7 @@ export type Subscription = {
commitDeleted?: Maybe<Scalars['JSONObject']>;
/** Subscribe to commit updated event. */
commitUpdated?: Maybe<Scalars['JSONObject']>;
projectAutomationsStatusUpdated: ProjectAutomationsStatusUpdatedMessage;
/**
* Subscribe to updates to resource comments/threads. Optionally specify resource ID string to only receive
* updates regarding comments for those resources.
@@ -2429,6 +2438,11 @@ export type SubscriptionCommitUpdatedArgs = {
};
export type SubscriptionProjectAutomationsStatusUpdatedArgs = {
projectId: Scalars['String'];
};
export type SubscriptionProjectCommentsUpdatedArgs = {
target: ViewerUpdateTrackingTarget;
};
@@ -2990,6 +3004,7 @@ export type ResolversTypes = {
PasswordStrengthCheckResults: ResolverTypeWrapper<PasswordStrengthCheckResults>;
PendingStreamCollaborator: ResolverTypeWrapper<PendingStreamCollaboratorGraphQLReturn>;
Project: ResolverTypeWrapper<ProjectGraphQLReturn>;
ProjectAutomationsStatusUpdatedMessage: ResolverTypeWrapper<Omit<ProjectAutomationsStatusUpdatedMessage, 'model' | 'project' | 'status' | 'version'> & { model: ResolversTypes['Model'], project: ResolversTypes['Project'], status: ResolversTypes['AutomationsStatus'], version: ResolversTypes['Version'] }>;
ProjectCollaborator: ResolverTypeWrapper<Omit<ProjectCollaborator, 'user'> & { user: ResolversTypes['LimitedUser'] }>;
ProjectCollection: ResolverTypeWrapper<Omit<ProjectCollection, 'items'> & { items: Array<ResolversTypes['Project']> }>;
ProjectCommentCollection: ResolverTypeWrapper<Omit<ProjectCommentCollection, 'items'> & { items: Array<ResolversTypes['Comment']> }>;
@@ -3160,6 +3175,7 @@ export type ResolversParentTypes = {
PasswordStrengthCheckResults: PasswordStrengthCheckResults;
PendingStreamCollaborator: PendingStreamCollaboratorGraphQLReturn;
Project: ProjectGraphQLReturn;
ProjectAutomationsStatusUpdatedMessage: Omit<ProjectAutomationsStatusUpdatedMessage, 'model' | 'project' | 'status' | 'version'> & { model: ResolversParentTypes['Model'], project: ResolversParentTypes['Project'], status: ResolversParentTypes['AutomationsStatus'], version: ResolversParentTypes['Version'] };
ProjectCollaborator: Omit<ProjectCollaborator, 'user'> & { user: ResolversParentTypes['LimitedUser'] };
ProjectCollection: Omit<ProjectCollection, 'items'> & { items: Array<ResolversParentTypes['Project']> };
ProjectCommentCollection: Omit<ProjectCommentCollection, 'items'> & { items: Array<ResolversParentTypes['Comment']> };
@@ -3778,6 +3794,14 @@ export type ProjectResolvers<ContextType = GraphQLContext, ParentType extends Re
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type ProjectAutomationsStatusUpdatedMessageResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ProjectAutomationsStatusUpdatedMessage'] = ResolversParentTypes['ProjectAutomationsStatusUpdatedMessage']> = {
model?: Resolver<ResolversTypes['Model'], ParentType, ContextType>;
project?: Resolver<ResolversTypes['Project'], ParentType, ContextType>;
status?: Resolver<ResolversTypes['AutomationsStatus'], ParentType, ContextType>;
version?: Resolver<ResolversTypes['Version'], ParentType, ContextType>;
__isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>;
};
export type ProjectCollaboratorResolvers<ContextType = GraphQLContext, ParentType extends ResolversParentTypes['ProjectCollaborator'] = ResolversParentTypes['ProjectCollaborator']> = {
role?: Resolver<ResolversTypes['String'], ParentType, ContextType>;
user?: Resolver<ResolversTypes['LimitedUser'], ParentType, ContextType>;
@@ -4070,6 +4094,7 @@ export type SubscriptionResolvers<ContextType = GraphQLContext, ParentType exten
commitCreated?: SubscriptionResolver<Maybe<ResolversTypes['JSONObject']>, "commitCreated", ParentType, ContextType, RequireFields<SubscriptionCommitCreatedArgs, 'streamId'>>;
commitDeleted?: SubscriptionResolver<Maybe<ResolversTypes['JSONObject']>, "commitDeleted", ParentType, ContextType, RequireFields<SubscriptionCommitDeletedArgs, 'streamId'>>;
commitUpdated?: SubscriptionResolver<Maybe<ResolversTypes['JSONObject']>, "commitUpdated", ParentType, ContextType, RequireFields<SubscriptionCommitUpdatedArgs, 'streamId'>>;
projectAutomationsStatusUpdated?: SubscriptionResolver<ResolversTypes['ProjectAutomationsStatusUpdatedMessage'], "projectAutomationsStatusUpdated", ParentType, ContextType, RequireFields<SubscriptionProjectAutomationsStatusUpdatedArgs, 'projectId'>>;
projectCommentsUpdated?: SubscriptionResolver<ResolversTypes['ProjectCommentsUpdatedMessage'], "projectCommentsUpdated", ParentType, ContextType, RequireFields<SubscriptionProjectCommentsUpdatedArgs, 'target'>>;
projectModelsUpdated?: SubscriptionResolver<ResolversTypes['ProjectModelsUpdatedMessage'], "projectModelsUpdated", ParentType, ContextType, RequireFields<SubscriptionProjectModelsUpdatedArgs, 'id'>>;
projectPendingModelsUpdated?: SubscriptionResolver<ResolversTypes['ProjectPendingModelsUpdatedMessage'], "projectPendingModelsUpdated", ParentType, ContextType, RequireFields<SubscriptionProjectPendingModelsUpdatedArgs, 'id'>>;
@@ -4267,6 +4292,7 @@ export type Resolvers<ContextType = GraphQLContext> = {
PasswordStrengthCheckResults?: PasswordStrengthCheckResultsResolvers<ContextType>;
PendingStreamCollaborator?: PendingStreamCollaboratorResolvers<ContextType>;
Project?: ProjectResolvers<ContextType>;
ProjectAutomationsStatusUpdatedMessage?: ProjectAutomationsStatusUpdatedMessageResolvers<ContextType>;
ProjectCollaborator?: ProjectCollaboratorResolvers<ContextType>;
ProjectCollection?: ProjectCollectionResolvers<ContextType>;
ProjectCommentCollection?: ProjectCommentCollectionResolvers<ContextType>;
@@ -38,7 +38,12 @@ export type CommitWithStreamBranchMetadata = CommitRecord & {
/**
* Get commits with their stream and branch IDs
*/
export async function getCommits(commitIds: string[]) {
export async function getCommits(
commitIds: string[],
options?: Partial<{ streamId: string }>
) {
const { streamId } = options || {}
const q = Commits.knex()
.select<CommitWithStreamBranchMetadata[]>(CommitWithStreamBranchMetadataFields)
.whereIn(Commits.col.id, commitIds)
@@ -46,6 +51,10 @@ export async function getCommits(commitIds: string[]) {
.leftJoin(BranchCommits.name, BranchCommits.col.commitId, Commits.col.id)
.innerJoin(Branches.name, Branches.col.id, BranchCommits.col.branchId)
if (streamId) {
q.andWhere(StreamCommits.col.streamId, streamId)
}
const rows = await q
// in case the join tables have multiple values for each commit
@@ -55,8 +64,11 @@ export async function getCommits(commitIds: string[]) {
return uniqueRows
}
export async function getCommit(commitId: string) {
const [commit] = await getCommits([commitId])
export async function getCommit(
commitId: string,
options?: Partial<{ streamId: string }>
) {
const [commit] = await getCommits([commitId], options)
return commit as Optional<typeof commit>
}
@@ -1466,6 +1466,14 @@ export type ProjectWebhooksArgs = {
id?: InputMaybe<Scalars['String']>;
};
export type ProjectAutomationsStatusUpdatedMessage = {
__typename?: 'ProjectAutomationsStatusUpdatedMessage';
model: Model;
project: Project;
status: AutomationsStatus;
version: Version;
};
export type ProjectCollaborator = {
__typename?: 'ProjectCollaborator';
role: Scalars['String'];
@@ -2332,6 +2340,7 @@ export type Subscription = {
commitDeleted?: Maybe<Scalars['JSONObject']>;
/** Subscribe to commit updated event. */
commitUpdated?: Maybe<Scalars['JSONObject']>;
projectAutomationsStatusUpdated: ProjectAutomationsStatusUpdatedMessage;
/**
* Subscribe to updates to resource comments/threads. Optionally specify resource ID string to only receive
* updates regarding comments for those resources.
@@ -2419,6 +2428,11 @@ export type SubscriptionCommitUpdatedArgs = {
};
export type SubscriptionProjectAutomationsStatusUpdatedArgs = {
projectId: Scalars['String'];
};
export type SubscriptionProjectCommentsUpdatedArgs = {
target: ViewerUpdateTrackingTarget;
};
@@ -5,6 +5,9 @@ import Redis from 'ioredis'
import { withFilter } from 'graphql-subscriptions'
import { GraphQLContext } from '@/modules/shared/helpers/typeHelper'
import {
AutomationRun,
AutomationsStatus,
ProjectAutomationsStatusUpdatedMessage,
ProjectCommentsUpdatedMessage,
ProjectModelsUpdatedMessage,
ProjectPendingModelsUpdatedMessage,
@@ -12,6 +15,7 @@ import {
ProjectUpdatedMessage,
ProjectVersionsPreviewGeneratedMessage,
ProjectVersionsUpdatedMessage,
SubscriptionProjectAutomationsStatusUpdatedArgs,
SubscriptionProjectCommentsUpdatedArgs,
SubscriptionProjectModelsUpdatedArgs,
SubscriptionProjectPendingModelsUpdatedArgs,
@@ -33,6 +37,7 @@ import {
} from '@/modules/core/helpers/graphTypes'
import { CommentGraphQLReturn } from '@/modules/comments/helpers/graphTypes'
import { FileUploadGraphQLReturn } from '@/modules/fileuploads/helpers/types'
import { AutomationFunctionRunGraphQLReturn } from '@/modules/automations/helpers/graphTypes'
/**
* GraphQL Subscription PubSub instance
@@ -82,7 +87,8 @@ export enum ProjectSubscriptions {
ProjectModelsUpdated = 'PROJECT_MODELS_UPDATED',
ProjectVersionsUpdated = 'PROJECT_VERSIONS_UPDATED',
ProjectVersionsPreviewGenerated = 'PROJECT_VERSIONS_PREVIEW_GENERATED',
ProjectCommentsUpdated = 'PROJECT_COMMENTS_UPDATED'
ProjectCommentsUpdated = 'PROJECT_COMMENTS_UPDATED',
ProjectAutomationStatusUpdated = 'PROJECT_AUTOMATION_STATUS_UPDATED'
}
export enum ViewerSubscriptions {
@@ -184,6 +190,31 @@ type SubscriptionTypeMap = {
}
variables: SubscriptionProjectPendingVersionsUpdatedArgs
}
[ProjectSubscriptions.ProjectAutomationStatusUpdated]: {
payload: {
projectAutomationsStatusUpdated: Merge<
ProjectAutomationsStatusUpdatedMessage,
{
version: VersionGraphQLReturn
model: ModelGraphQLReturn
project: ProjectGraphQLReturn
status: Merge<
AutomationsStatus,
{
automationRuns: Array<
Merge<
AutomationRun,
{ functionRuns: AutomationFunctionRunGraphQLReturn[] }
>
>
}
>
}
>
projectId: string
}
variables: SubscriptionProjectAutomationsStatusUpdatedArgs
}
} & { [k in SubscriptionEvent]: { payload: unknown; variables: unknown } }
type SubscriptionEvent =
@@ -1466,6 +1466,14 @@ export type ProjectWebhooksArgs = {
id?: InputMaybe<Scalars['String']>;
};
export type ProjectAutomationsStatusUpdatedMessage = {
__typename?: 'ProjectAutomationsStatusUpdatedMessage';
model: Model;
project: Project;
status: AutomationsStatus;
version: Version;
};
export type ProjectCollaborator = {
__typename?: 'ProjectCollaborator';
role: Scalars['String'];
@@ -2332,6 +2340,7 @@ export type Subscription = {
commitDeleted?: Maybe<Scalars['JSONObject']>;
/** Subscribe to commit updated event. */
commitUpdated?: Maybe<Scalars['JSONObject']>;
projectAutomationsStatusUpdated: ProjectAutomationsStatusUpdatedMessage;
/**
* Subscribe to updates to resource comments/threads. Optionally specify resource ID string to only receive
* updates regarding comments for those resources.
@@ -2419,6 +2428,11 @@ export type SubscriptionCommitUpdatedArgs = {
};
export type SubscriptionProjectAutomationsStatusUpdatedArgs = {
projectId: Scalars['String'];
};
export type SubscriptionProjectCommentsUpdatedArgs = {
target: ViewerUpdateTrackingTarget;
};