From b54fea0a7cd59b203a5c19f6dcfacae93903433c Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Tue, 4 Jun 2024 13:21:49 +0300 Subject: [PATCH] feat: various automate fe2 fixes (#2321) * minor cleanup * fix(web-982): hide Open View button if already on that view * fix(web-1000): showing updated notification when creating automation * fix(web-1014;web-1016): clearing model select when project changed + listing only valid projects --- .../automate/automation/CreateDialog.vue | 48 ++++++++++--------- .../create-dialog/AutomationDetailsStep.vue | 7 +++ .../automate/viewer/panel/FunctionRunRow.vue | 19 +++++--- .../frontend-2/lib/common/composables/url.ts | 35 +++++++------- .../lib/common/generated/gql/gql.ts | 4 +- .../lib/common/generated/gql/graphql.ts | 4 +- .../frontend-2/lib/common/helpers/route.ts | 41 +++++++++++++++- .../frontend-2/lib/core/configs/apollo.ts | 8 ++++ .../lib/projects/graphql/mutations.ts | 1 + .../automate/services/automationManagement.ts | 5 +- .../modules/automate/services/trigger.ts | 3 ++ 11 files changed, 124 insertions(+), 51 deletions(-) diff --git a/packages/frontend-2/components/automate/automation/CreateDialog.vue b/packages/frontend-2/components/automate/automation/CreateDialog.vue index 51e83573e..658ac63c3 100644 --- a/packages/frontend-2/components/automate/automation/CreateDialog.vue +++ b/packages/frontend-2/components/automate/automation/CreateDialog.vue @@ -57,14 +57,21 @@ v-model:has-errors="hasParameterErrors" :fn="selectedFunction" /> - + - @@ -223,7 +224,7 @@ const buttons = computed((): LayoutDialogButton[] => { }, onClick: () => { isTestAutomation.value = true - step.value = 2 + enumStep.value = AutomationCreateSteps.AutomationDetails } }, { @@ -463,13 +464,16 @@ const onDetailsSubmit = handleDetailsSubmit(async () => { } // Enable - await updateAutomation({ - projectId: project.id, - input: { - id: aId, - enabled: true - } - }) + await updateAutomation( + { + projectId: project.id, + input: { + id: aId, + enabled: true + } + }, + { hideSuccessToast: true } + ) step.value++ } finally { diff --git a/packages/frontend-2/components/automate/automation/create-dialog/AutomationDetailsStep.vue b/packages/frontend-2/components/automate/automation/create-dialog/AutomationDetailsStep.vue index a14e31787..2ade57ff7 100644 --- a/packages/frontend-2/components/automate/automation/create-dialog/AutomationDetailsStep.vue +++ b/packages/frontend-2/components/automate/automation/create-dialog/AutomationDetailsStep.vue @@ -12,6 +12,7 @@ :rules="projectRules" :allow-unset="false" validate-on-value-update + owned-only /> { + if (model.value && newVal && newVal.id !== oldVal?.id) { + model.value = undefined + } +}) diff --git a/packages/frontend-2/components/automate/viewer/panel/FunctionRunRow.vue b/packages/frontend-2/components/automate/viewer/panel/FunctionRunRow.vue index 493a87655..08c5cf5d9 100644 --- a/packages/frontend-2/components/automate/viewer/panel/FunctionRunRow.vue +++ b/packages/frontend-2/components/automate/viewer/panel/FunctionRunRow.vue @@ -71,16 +71,13 @@ -
+
Open view @@ -125,6 +122,7 @@ import { useInjectedViewerState } from '~~/lib/viewer/composables/setup' import { graphql } from '~/lib/common/generated/gql' import { useAutomationFunctionRunResults } from '~/lib/automate/composables/runs' import { useRunStatusMetadata } from '~/lib/automate/composables/runStatus' +import { doesRouteFitTarget } from '~/lib/common/helpers/route' graphql(` fragment AutomateViewerPanelFunctionRunRow_AutomateFunctionRun on AutomateFunctionRun { @@ -143,19 +141,19 @@ graphql(` } `) -const { projectId } = useInjectedViewerState() - const props = defineProps<{ functionRun: AutomateViewerPanelFunctionRunRow_AutomateFunctionRunFragment automationName: string }>() +const { projectId } = useInjectedViewerState() const results = useAutomationFunctionRunResults({ results: computed(() => props.functionRun.results) }) const { metadata: statusMetaData } = useRunStatusMetadata({ status: computed(() => props.functionRun.status) }) +const route = useRoute() const pageRunLimit = ref(5) const expanded = ref(false) @@ -163,4 +161,11 @@ const expanded = ref(false) const attachments = computed(() => (results.value?.values.blobIds || []).filter((b) => !!b) ) +const hasValidContextView = computed(() => { + const ctxView = props.functionRun.contextView + if (!ctxView?.length) return false + + const currentPath = route.fullPath + return !doesRouteFitTarget(ctxView, currentPath) +}) diff --git a/packages/frontend-2/lib/common/composables/url.ts b/packages/frontend-2/lib/common/composables/url.ts index db4139918..48c948545 100644 --- a/packages/frontend-2/lib/common/composables/url.ts +++ b/packages/frontend-2/lib/common/composables/url.ts @@ -13,6 +13,24 @@ export function serializeHashState( .join('&')}` } +export function deserializeHashState(hashString: string) { + if (hashString.length < 2 || !hashString.startsWith('#')) return {} + + const keyValuePairs = hashString.substring(1).split('&') + const result = reduce( + keyValuePairs, + (result, item) => { + const [key, value] = item.split('=') + if (key && value) { + result[key] = value + } + return result + }, + {} as Record> + ) + return result +} + /** * Read/writable state similar to one in the querystring, but one that uses anchor (#) data instead */ @@ -22,22 +40,7 @@ export function useRouteHashState() { const hashState = writableAsyncComputed({ get: () => { - const hash = route.hash - if (hash.length < 2 || !hash.startsWith('#')) return {} - - const keyValuePairs = hash.substring(1).split('&') - const result = reduce( - keyValuePairs, - (result, item) => { - const [key, value] = item.split('=') - if (key && value) { - result[key] = value - } - return result - }, - {} as Record> - ) - return result + return deserializeHashState(route.hash) }, set: async (newVal) => { const hashString = serializeHashState(newVal) diff --git a/packages/frontend-2/lib/common/generated/gql/gql.ts b/packages/frontend-2/lib/common/generated/gql/gql.ts index 72cfcc1d8..d1b1d2f3c 100644 --- a/packages/frontend-2/lib/common/generated/gql/gql.ts +++ b/packages/frontend-2/lib/common/generated/gql/gql.ts @@ -159,7 +159,7 @@ const documents = { "\n mutation UpdateAutomation($projectId: ID!, $input: ProjectAutomationUpdateInput!) {\n projectMutations {\n automationMutations(projectId: $projectId) {\n update(input: $input) {\n id\n name\n enabled\n }\n }\n }\n }\n": types.UpdateAutomationDocument, "\n mutation CreateAutomationRevision(\n $projectId: ID!\n $input: ProjectAutomationRevisionCreateInput!\n ) {\n projectMutations {\n automationMutations(projectId: $projectId) {\n createRevision(input: $input) {\n id\n }\n }\n }\n }\n": types.CreateAutomationRevisionDocument, "\n mutation TriggerAutomation($projectId: ID!, $automationId: ID!) {\n projectMutations {\n automationMutations(projectId: $projectId) {\n trigger(automationId: $automationId)\n }\n }\n }\n": types.TriggerAutomationDocument, - "\n mutation CreateTestAutomation(\n $projectId: ID!\n $input: ProjectTestAutomationCreateInput!\n ) {\n projectMutations {\n automationMutations(projectId: $projectId) {\n createTestAutomation(input: $input) {\n id\n }\n }\n }\n }\n": types.CreateTestAutomationDocument, + "\n mutation CreateTestAutomation(\n $projectId: ID!\n $input: ProjectTestAutomationCreateInput!\n ) {\n projectMutations {\n automationMutations(projectId: $projectId) {\n createTestAutomation(input: $input) {\n id\n ...ProjectPageAutomationsRow_Automation\n }\n }\n }\n }\n": types.CreateTestAutomationDocument, "\n query ProjectAccessCheck($id: String!) {\n project(id: $id) {\n id\n }\n }\n": types.ProjectAccessCheckDocument, "\n query ProjectRoleCheck($id: String!) {\n project(id: $id) {\n id\n role\n }\n }\n": types.ProjectRoleCheckDocument, "\n query ProjectsDashboardQuery($filter: UserProjectsFilter, $cursor: String) {\n activeUser {\n id\n projects(filter: $filter, limit: 6, cursor: $cursor) {\n cursor\n totalCount\n items {\n ...ProjectDashboardItem\n }\n }\n ...ProjectsInviteBanners\n }\n }\n": types.ProjectsDashboardQueryDocument, @@ -838,7 +838,7 @@ export function graphql(source: "\n mutation TriggerAutomation($projectId: ID!, /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "\n mutation CreateTestAutomation(\n $projectId: ID!\n $input: ProjectTestAutomationCreateInput!\n ) {\n projectMutations {\n automationMutations(projectId: $projectId) {\n createTestAutomation(input: $input) {\n id\n }\n }\n }\n }\n"): (typeof documents)["\n mutation CreateTestAutomation(\n $projectId: ID!\n $input: ProjectTestAutomationCreateInput!\n ) {\n projectMutations {\n automationMutations(projectId: $projectId) {\n createTestAutomation(input: $input) {\n id\n }\n }\n }\n }\n"]; +export function graphql(source: "\n mutation CreateTestAutomation(\n $projectId: ID!\n $input: ProjectTestAutomationCreateInput!\n ) {\n projectMutations {\n automationMutations(projectId: $projectId) {\n createTestAutomation(input: $input) {\n id\n ...ProjectPageAutomationsRow_Automation\n }\n }\n }\n }\n"): (typeof documents)["\n mutation CreateTestAutomation(\n $projectId: ID!\n $input: ProjectTestAutomationCreateInput!\n ) {\n projectMutations {\n automationMutations(projectId: $projectId) {\n createTestAutomation(input: $input) {\n id\n ...ProjectPageAutomationsRow_Automation\n }\n }\n }\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 182a29d83..9363352d5 100644 --- a/packages/frontend-2/lib/common/generated/gql/graphql.ts +++ b/packages/frontend-2/lib/common/generated/gql/graphql.ts @@ -4091,7 +4091,7 @@ export type CreateTestAutomationMutationVariables = Exact<{ }>; -export type CreateTestAutomationMutation = { __typename?: 'Mutation', projectMutations: { __typename?: 'ProjectMutations', automationMutations: { __typename?: 'ProjectAutomationMutations', createTestAutomation: { __typename?: 'Automation', id: string } } } }; +export type CreateTestAutomationMutation = { __typename?: 'Mutation', projectMutations: { __typename?: 'ProjectMutations', automationMutations: { __typename?: 'ProjectAutomationMutations', createTestAutomation: { __typename?: 'Automation', id: string, name: string, enabled: boolean, isTestAutomation: boolean, currentRevision?: { __typename?: 'AutomationRevision', id: string, triggerDefinitions: Array<{ __typename?: 'VersionCreatedTriggerDefinition', model: { __typename?: 'Model', id: string, name: string } }> } | null, runs: { __typename?: 'AutomateRunCollection', totalCount: number, items: Array<{ __typename?: 'AutomateRun', id: string, status: AutomateRunStatus, createdAt: string, updatedAt: string, functionRuns: Array<{ __typename?: 'AutomateFunctionRun', statusMessage?: string | null, id: string, status: AutomateRunStatus }>, trigger: { __typename?: 'VersionCreatedTrigger', version: { __typename?: 'Version', id: string }, model: { __typename?: 'Model', id: string } } }> } } } } }; export type ProjectAccessCheckQueryVariables = Exact<{ id: Scalars['String']; @@ -4758,7 +4758,7 @@ export const CreateAutomationDocument = {"kind":"Document","definitions":[{"kind export const UpdateAutomationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateAutomation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectAutomationUpdateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"automationMutations"},"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":"update"},"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":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const CreateAutomationRevisionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateAutomationRevision"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectAutomationRevisionCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"automationMutations"},"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":"createRevision"},"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":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const TriggerAutomationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"TriggerAutomation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"automationId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"automationMutations"},"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":"trigger"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"automationId"},"value":{"kind":"Variable","name":{"kind":"Name","value":"automationId"}}}]}]}}]}}]}}]} as unknown as DocumentNode; -export const CreateTestAutomationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateTestAutomation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectTestAutomationCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"automationMutations"},"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":"createTestAutomation"},"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":"id"}}]}}]}}]}}]}}]} as unknown as DocumentNode; +export const CreateTestAutomationDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"CreateTestAutomation"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"projectId"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"input"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ProjectTestAutomationCreateInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"projectMutations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"automationMutations"},"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":"createTestAutomation"},"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":"id"}},{"kind":"FragmentSpread","name":{"kind":"Name","value":"ProjectPageAutomationsRow_Automation"}}]}}]}}]}}]}},...ProjectPageAutomationsRow_AutomationFragmentDoc.definitions,...AutomationRunDetailsFragmentDoc.definitions,...FunctionRunStatusForSummaryFragmentDoc.definitions]} as unknown as DocumentNode; export const ProjectAccessCheckDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectAccessCheck"},"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":"project"},"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"}}]}}]}}]} as unknown as DocumentNode; export const ProjectRoleCheckDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectRoleCheck"},"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":"project"},"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":"role"}}]}}]}}]} as unknown as DocumentNode; export const ProjectsDashboardQueryDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ProjectsDashboardQuery"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"UserProjectsFilter"}}},{"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":"activeUser"},"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":"limit"},"value":{"kind":"IntValue","value":"6"}},{"kind":"Argument","name":{"kind":"Name","value":"cursor"},"value":{"kind":"Variable","name":{"kind":"Name","value":"cursor"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"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":"FragmentSpread","name":{"kind":"Name","value":"ProjectsInviteBanners"}}]}}]}},...ProjectDashboardItemFragmentDoc.definitions,...ProjectDashboardItemNoModelsFragmentDoc.definitions,...ProjectPageModelsCardProjectFragmentDoc.definitions,...ProjectPageModelsActions_ProjectFragmentDoc.definitions,...ProjectsModelPageEmbed_ProjectFragmentDoc.definitions,...ProjectsPageTeamDialogManagePermissions_ProjectFragmentDoc.definitions,...ProjectPageLatestItemsModelItemFragmentDoc.definitions,...PendingFileUploadFragmentDoc.definitions,...ProjectPageModelsCardRenameDialogFragmentDoc.definitions,...ProjectPageModelsCardDeleteDialogFragmentDoc.definitions,...ProjectPageModelsActionsFragmentDoc.definitions,...AutomateRunsTriggerStatus_TriggeredAutomationsStatusFragmentDoc.definitions,...TriggeredAutomationsStatusSummaryFragmentDoc.definitions,...FunctionRunStatusForSummaryFragmentDoc.definitions,...AutomateRunsTriggerStatusDialog_TriggeredAutomationsStatusFragmentDoc.definitions,...AutomateRunsTriggerStatusDialogRunsRows_AutomateRunFragmentDoc.definitions,...AutomateRunsTriggerStatusDialogFunctionRun_AutomateFunctionRunFragmentDoc.definitions,...AutomationsStatusOrderedRuns_AutomationRunFragmentDoc.definitions,...ProjectsInviteBannersFragmentDoc.definitions,...ProjectsInviteBannerFragmentDoc.definitions,...LimitedUserAvatarFragmentDoc.definitions]} as unknown as DocumentNode; diff --git a/packages/frontend-2/lib/common/helpers/route.ts b/packages/frontend-2/lib/common/helpers/route.ts index 419aede6c..53100f3ae 100644 --- a/packages/frontend-2/lib/common/helpers/route.ts +++ b/packages/frontend-2/lib/common/helpers/route.ts @@ -1,5 +1,5 @@ import type { LocationQueryRaw } from 'vue-router' -import { serializeHashState } from '~~/lib/common/composables/url' +import { deserializeHashState, serializeHashState } from '~~/lib/common/composables/url' import { ViewerHashStateKeys } from '~~/lib/viewer/composables/setup/urlHashState' export const profileRoute = '/profile' @@ -83,3 +83,42 @@ export const useNavigateToProject = () => { return router.push({ path: projectRoute(id), query }) } } + +/** + * Check that fullPathA fits fullPathB (not necessarily the inverse) + */ +export const doesRouteFitTarget = (fullPathA: string, fullPathB: string) => { + const fakeOrigin = 'https://test.com' + + let urlA: URL + let urlB: URL + + try { + urlA = new URL(fullPathA, fakeOrigin) + urlB = new URL(fullPathB, fakeOrigin) + } catch (e) { + useLogger().warn('Failed to parse URLs', e) + return false + } + + if (urlA.pathname !== urlB.pathname) { + return false + } + + const queryKeysA = urlA.searchParams.keys() + for (const key of queryKeysA) { + if (urlB.searchParams.get(key) !== urlA.searchParams.get(key)) { + return false + } + } + + const hashA = deserializeHashState(urlA.hash) + const hashB = deserializeHashState(urlB.hash) + for (const [key, value] of Object.entries(hashA)) { + if (hashB[key] !== value) { + return false + } + } + + return true +} diff --git a/packages/frontend-2/lib/core/configs/apollo.ts b/packages/frontend-2/lib/core/configs/apollo.ts index 5ce64d5ef..7e4b6ad3b 100644 --- a/packages/frontend-2/lib/core/configs/apollo.ts +++ b/packages/frontend-2/lib/core/configs/apollo.ts @@ -276,6 +276,14 @@ function createCache(): InMemoryCache { ) } } + }, + Automation: { + fields: { + runs: { + keyArgs: ['limit'], + merge: buildAbstractCollectionMergeFunction('AutomateRunCollection') + } + } } } }) diff --git a/packages/frontend-2/lib/projects/graphql/mutations.ts b/packages/frontend-2/lib/projects/graphql/mutations.ts index 216ebd072..5e26fa5d4 100644 --- a/packages/frontend-2/lib/projects/graphql/mutations.ts +++ b/packages/frontend-2/lib/projects/graphql/mutations.ts @@ -225,6 +225,7 @@ export const createTestAutomationMutation = graphql(` automationMutations(projectId: $projectId) { createTestAutomation(input: $input) { id + ...ProjectPageAutomationsRow_Automation } } } diff --git a/packages/server/modules/automate/services/automationManagement.ts b/packages/server/modules/automate/services/automationManagement.ts index f6dc35c0f..466e1560f 100644 --- a/packages/server/modules/automate/services/automationManagement.ts +++ b/packages/server/modules/automate/services/automationManagement.ts @@ -130,7 +130,10 @@ export type CreateTestAutomationDeps = { storeAutomationRevision: typeof storeAutomationRevision } -/** Create a test automation and its first revision in one request. */ +/** + * Create a test automation and its first revision in one request. + * TODO: Reduce code duplication w/ createAutomation + */ export const createTestAutomation = (deps: CreateTestAutomationDeps) => async (params: { diff --git a/packages/server/modules/automate/services/trigger.ts b/packages/server/modules/automate/services/trigger.ts index b08dd2dea..ab935169d 100644 --- a/packages/server/modules/automate/services/trigger.ts +++ b/packages/server/modules/automate/services/trigger.ts @@ -509,6 +509,9 @@ export type CreateTestAutomationRunDeps = { getFullAutomationRevisionMetadata: typeof getFullAutomationRevisionMetadata } & CreateAutomationRunDataDeps +/** + * TODO: Reduce duplication w/ other fns in this service + */ export const createTestAutomationRun = (deps: CreateTestAutomationRunDeps) => async (params: { projectId: string; automationId: string; userId: string }) => {