diff --git a/ee/app/assets/javascripts/workspaces/admin_settings/components/get_organization_workspaces_cluster_agents_query.vue b/ee/app/assets/javascripts/workspaces/admin_settings/components/get_organization_workspaces_cluster_agents_query.vue new file mode 100644 index 0000000000000000000000000000000000000000..66a0097f55fb7f86187af35b1781d25ddda731ac --- /dev/null +++ b/ee/app/assets/javascripts/workspaces/admin_settings/components/get_organization_workspaces_cluster_agents_query.vue @@ -0,0 +1,128 @@ + diff --git a/ee/app/assets/javascripts/workspaces/admin_settings/constants.js b/ee/app/assets/javascripts/workspaces/admin_settings/constants.js index f9e3abc56e9c5e0f8375b89362bb277150f9f2cc..1d30a9c9940ec3e1d654bfa2f9c73d3043e14fdd 100644 --- a/ee/app/assets/javascripts/workspaces/admin_settings/constants.js +++ b/ee/app/assets/javascripts/workspaces/admin_settings/constants.js @@ -1,21 +1,11 @@ import { s__ } from '~/locale'; -const AVAILABILITY_OPTIONS = { +export const AVAILABILITY_OPTIONS = { AVAILABLE: 'available', BLOCKED: 'blocked', }; -export const CONNECTION_STATUS = { - CONNECTED: 'connected', - NOT_CONNECTED: 'not_connected', -}; - export const AVAILABILITY_TEXT = { [AVAILABILITY_OPTIONS.AVAILABLE]: s__('Workspaces|Available'), [AVAILABILITY_OPTIONS.BLOCKED]: s__('Workspaces|Blocked'), }; - -export const CONNECTION_STATUS_TEXT = { - [CONNECTION_STATUS.CONNECTED]: s__('Workspaces|Connected'), - [CONNECTION_STATUS.NOT_CONNECTED]: s__('Workspaces|Not connected'), -}; diff --git a/ee/app/assets/javascripts/workspaces/admin_settings/graphql/queries/organization_mapped_agents.query.graphql b/ee/app/assets/javascripts/workspaces/admin_settings/graphql/queries/organization_mapped_agents.query.graphql new file mode 100644 index 0000000000000000000000000000000000000000..942689e62df16da631679379cb6349091b434673 --- /dev/null +++ b/ee/app/assets/javascripts/workspaces/admin_settings/graphql/queries/organization_mapped_agents.query.graphql @@ -0,0 +1,10 @@ +query mappedAgents($organizationId: OrganizationsOrganizationID!) { + organization(id: $organizationId) { + id + mappedAgents: workspacesClusterAgents(filter: DIRECTLY_MAPPED) { + nodes { + id + } + } + } +} diff --git a/ee/app/assets/javascripts/workspaces/admin_settings/graphql/queries/organization_workspaces_cluster_agents.query.graphql b/ee/app/assets/javascripts/workspaces/admin_settings/graphql/queries/organization_workspaces_cluster_agents.query.graphql new file mode 100644 index 0000000000000000000000000000000000000000..833c3892009e5a9d9573e97459a64a788b5e6b8a --- /dev/null +++ b/ee/app/assets/javascripts/workspaces/admin_settings/graphql/queries/organization_workspaces_cluster_agents.query.graphql @@ -0,0 +1,44 @@ +query organizationWorkspacesClusterAgents( + $organizationId: OrganizationsOrganizationID! + $before: String + $after: String +) { + organization(id: $organizationId) { + id + organizationWorkspacesClusterAgents: workspacesClusterAgents( + filter: DIRECTLY_MAPPED + first: 5 + before: $before + after: $after + ) { + nodes { + id + name + webPath + project { + id + name + group { + id + name + } + } + workspacesAgentConfig { + id + enabled + } + connections { + nodes { + connectedAt + } + } + } + pageInfo { + hasNextPage + hasPreviousPage + startCursor + endCursor + } + } + } +} diff --git a/ee/app/assets/javascripts/workspaces/admin_settings/init_admin_settings_app.js b/ee/app/assets/javascripts/workspaces/admin_settings/init_admin_settings_app.js index b340f8711455d3852d4405aabc9e810c972a8a7c..af90a561b626dc218ef2f69e2f4c573c078822e5 100644 --- a/ee/app/assets/javascripts/workspaces/admin_settings/init_admin_settings_app.js +++ b/ee/app/assets/javascripts/workspaces/admin_settings/init_admin_settings_app.js @@ -1,10 +1,20 @@ import Vue from 'vue'; +import VueApollo from 'vue-apollo'; import { convertToGraphQLId } from '~/graphql_shared/utils'; import { TYPE_ORGANIZATION } from '~/graphql_shared/constants'; import { parseBoolean } from '~/lib/utils/common_utils'; +import createDefaultClient from '~/lib/graphql'; import WorkspacesAgentAvailabilityApp from './pages/app.vue'; +Vue.use(VueApollo); + +const createApolloProvider = () => { + const defaultClient = createDefaultClient(); + + return new VueApollo({ defaultClient }); +}; + const initWorkspacesAgentAvailabilityApp = () => { const el = document.getElementById('js-workspaces-agent-availability-settings'); @@ -14,6 +24,7 @@ const initWorkspacesAgentAvailabilityApp = () => { return new Vue({ el, + apolloProvider: createApolloProvider(), components: { WorkspacesAgentAvailabilityApp, }, diff --git a/ee/app/assets/javascripts/workspaces/admin_settings/pages/app.vue b/ee/app/assets/javascripts/workspaces/admin_settings/pages/app.vue index 0e083f8d9ce32c37803f08c15ebcfabd961a30d8..c0338217d1a10815b4bdb935c330cc109011735a 100644 --- a/ee/app/assets/javascripts/workspaces/admin_settings/pages/app.vue +++ b/ee/app/assets/javascripts/workspaces/admin_settings/pages/app.vue @@ -1,20 +1,34 @@ diff --git a/ee/spec/frontend/workspaces/admin_settings/components/get_organization_workspaces_cluster_agents_query_spec.js b/ee/spec/frontend/workspaces/admin_settings/components/get_organization_workspaces_cluster_agents_query_spec.js new file mode 100644 index 0000000000000000000000000000000000000000..660c8b2d0aad244a801cde0b9624a978b874dbd0 --- /dev/null +++ b/ee/spec/frontend/workspaces/admin_settings/components/get_organization_workspaces_cluster_agents_query_spec.js @@ -0,0 +1,275 @@ +import Vue, { nextTick } from 'vue'; +import VueApollo from 'vue-apollo'; +import { shallowMount } from '@vue/test-utils'; + +import { logError } from '~/lib/logger'; +import mappedOrganizationClusterAgentsQuery from 'ee/workspaces/admin_settings/graphql/queries/organization_mapped_agents.query.graphql'; +import organizationWorkspacesClusterAgentsQuery from 'ee/workspaces/admin_settings/graphql/queries/organization_workspaces_cluster_agents.query.graphql'; +import GetOrganizationWorkspacesClusterAgentsQuery from 'ee/workspaces/admin_settings/components/get_organization_workspaces_cluster_agents_query.vue'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; + +import { + ORGANIZATION_MAPPED_AGENTS_QUERY_RESULT, + ORGANIZATION_WORKSPACES_CLUSTER_AGENTS_QUERY_RESULT, +} from '../../mock_data'; + +Vue.use(VueApollo); +jest.mock('~/lib/logger'); + +const MOCK_ORG_ID = 'gid://gitlab/Organizations::Organization/1'; + +describe('workspaces/admin_settings/components/get_organization_workspaces_cluster_agents_query.vue', () => { + const defaultSlotSpy = jest.fn(); + const mappedOrganizationClusterAgentsQueryHandler = jest.fn(); + const organizationWorkspacesClusterAgentsQueryHandler = jest.fn(); + let wrapper; + + const buildWrapper = async ({ propsData = {} } = {}) => { + const apolloProvider = createMockApollo( + [ + [mappedOrganizationClusterAgentsQuery, mappedOrganizationClusterAgentsQueryHandler], + [organizationWorkspacesClusterAgentsQuery, organizationWorkspacesClusterAgentsQueryHandler], + ], + {}, + { typePolicies: { Query: { fields: { organization: { merge: false } } } } }, + ); + + wrapper = shallowMount(GetOrganizationWorkspacesClusterAgentsQuery, { + apolloProvider, + propsData: { + organizationId: '', + ...propsData, + }, + scopedSlots: { + default: defaultSlotSpy, + }, + }); + + await waitForPromises(); + await nextTick(); + }; + const buildWrapperWithOrg = () => buildWrapper({ propsData: { organizationId: MOCK_ORG_ID } }); + + const setupMappedOrganizationClusterAgentsQueryHandler = (responses) => { + mappedOrganizationClusterAgentsQueryHandler.mockResolvedValue(responses); + }; + const setupOrganizationWorkspacesClusterAgentsQueryHandler = (responses) => { + organizationWorkspacesClusterAgentsQueryHandler.mockResolvedValue(responses); + }; + + beforeEach(() => { + logError.mockReset(); + defaultSlotSpy.mockReset(); + mappedOrganizationClusterAgentsQueryHandler.mockReset(); + organizationWorkspacesClusterAgentsQueryHandler.mockReset(); + }); + + describe('organizationMappedAgents query', () => { + it('does not execute query when organizationId is not provided', async () => { + await buildWrapper(); + + expect(mappedOrganizationClusterAgentsQueryHandler).not.toHaveBeenCalled(); + }); + + it('executes query when organizationId is provided', async () => { + setupMappedOrganizationClusterAgentsQueryHandler(ORGANIZATION_MAPPED_AGENTS_QUERY_RESULT); + + await buildWrapperWithOrg(); + + expect(mappedOrganizationClusterAgentsQueryHandler).toHaveBeenCalledWith({ + organizationId: MOCK_ORG_ID, + }); + }); + + it('returns error data to scoped slot on query error', async () => { + const mockError = new Error('Some error'); + mappedOrganizationClusterAgentsQueryHandler.mockRejectedValueOnce(mockError); + + await buildWrapperWithOrg(); + + const scopedSlotCall = defaultSlotSpy.mock.lastCall[0]; + expect(scopedSlotCall).toMatchObject({ + loading: false, + pagination: null, + error: mockError, + agents: [], + }); + }); + }); + + describe('organizationWorkspacesClusterAgents query', () => { + it('does not execute query when organizationId is not provided', async () => { + await buildWrapper(); + + expect(organizationWorkspacesClusterAgentsQueryHandler).not.toHaveBeenCalled(); + }); + + it('does not execute query when organizationId is provided but organizationMappedAgentsQuery is not successful', async () => { + mappedOrganizationClusterAgentsQueryHandler.mockRejectedValueOnce(new Error()); + await buildWrapperWithOrg(); + + expect(organizationWorkspacesClusterAgentsQueryHandler).not.toHaveBeenCalled(); + }); + + it('executes when organizationId is provided and organizationMappedAgentsQuery is successful', async () => { + setupMappedOrganizationClusterAgentsQueryHandler(ORGANIZATION_MAPPED_AGENTS_QUERY_RESULT); + setupOrganizationWorkspacesClusterAgentsQueryHandler( + ORGANIZATION_WORKSPACES_CLUSTER_AGENTS_QUERY_RESULT, + ); + await buildWrapperWithOrg(); + + expect(organizationWorkspacesClusterAgentsQueryHandler).toHaveBeenCalledWith({ + organizationId: MOCK_ORG_ID, + }); + }); + + describe('scoped slot', () => { + it('triggers error event when query is not successful', async () => { + setupMappedOrganizationClusterAgentsQueryHandler(ORGANIZATION_MAPPED_AGENTS_QUERY_RESULT); + const mockError = new Error('Some error'); + organizationWorkspacesClusterAgentsQueryHandler.mockRejectedValueOnce(mockError); + + await buildWrapperWithOrg(); + + const scopedSlotCall = defaultSlotSpy.mock.lastCall[0]; + + expect(scopedSlotCall).toMatchObject({ + loading: false, + pagination: null, + error: mockError, + agents: [], + }); + }); + + describe('on query success', () => { + beforeEach(async () => { + setupMappedOrganizationClusterAgentsQueryHandler(ORGANIZATION_MAPPED_AGENTS_QUERY_RESULT); + setupOrganizationWorkspacesClusterAgentsQueryHandler( + ORGANIZATION_WORKSPACES_CLUSTER_AGENTS_QUERY_RESULT, + ); + await buildWrapperWithOrg(); + }); + + it('renders correct data to scoped slot', () => { + const scopedSlotCall = defaultSlotSpy.mock.lastCall[0]; + const expectedPaginationData = { + show: true, + hasPreviousPage: false, + hasNextPage: true, + nextPage: wrapper.vm.nextPage, + prevPage: wrapper.vm.prevPage, + }; + + const expectedAgentsResult = [ + { + // gid://gitlab/Clusters::Agent/14 + availability: 'available', + group: 'Gitlab Org', + isConnected: false, + name: 'midnightowlgarden', + project: 'gitlab-agent-configurations', + url: 'http://test.host/gitlab-org/gitlab-agent-configurations/-/cluster_agents/midnightowlgarden', + workspacesEnabled: true, + }, + // gid://gitlab/Clusters::Agent/13 + { + availability: 'blocked', + group: 'Gitlab Org', + isConnected: false, + name: 'coastalechovalley', + project: 'gitlab-agent-configurations', + url: 'http://test.host/gitlab-org/gitlab-agent-configurations/-/cluster_agents/coastalechovalley', + workspacesEnabled: true, + }, + // gid://gitlab/Clusters::Agent/12 + { + availability: 'available', + group: 'Gitlab Org', + isConnected: false, + name: 'wandingbreezetale', + project: 'gitlab-agent-configurations', + url: 'http://test.host/gitlab-org/gitlab-agent-configurations/-/cluster_agents/wandingbreezetale', + workspacesEnabled: false, + }, + // gid://gitlab/Clusters::Agent/11 + { + availability: 'blocked', + group: 'Gitlab Org', + isConnected: false, + name: 'crimsonmapleshadow', + project: 'gitlab-agent-configurations', + url: 'http://test.host/gitlab-org/gitlab-agent-configurations/-/cluster_agents/crimsonmapleshadow', + workspacesEnabled: false, + }, + // gid://gitlab/Clusters::Agent/10 + { + availability: 'available', + group: 'Gitlab Org', + isConnected: true, + name: 'meadowsageharbor', + project: 'gitlab-agent-configurations', + url: 'http://test.host/gitlab-org/gitlab-agent-configurations/-/cluster_agents/meadowsageharbor', + workspacesEnabled: true, + }, + // gid://gitlab/Clusters::Agent/16 + { + availability: 'blocked', + group: 'Gitlab Org', + isConnected: true, + name: 'silvermoonharbor', + project: 'gitlab-agent-configurations', + url: 'http://test.host/gitlab-org/gitlab-agent-configurations/-/cluster_agents/silvermoonharbor', + workspacesEnabled: true, + }, + // gid://gitlab/Clusters::Agent/17 + { + availability: 'available', + group: 'Gitlab Org', + isConnected: true, + name: 'silvermoonharbor', + project: 'gitlab-agent-configurations', + url: 'http://test.host/gitlab-org/gitlab-agent-configurations/-/cluster_agents/silvermoonharbor', + workspacesEnabled: false, + }, + // gid://gitlab/Clusters::Agent/18 + { + availability: 'blocked', + group: 'Gitlab Org', + isConnected: true, + name: 'oceanbreezecliff', + project: 'gitlab-agent-configurations', + url: 'http://test.host/gitlab-org/gitlab-agent-configurations/-/cluster_agents/oceanbreezecliff', + workspacesEnabled: false, + }, + ]; + + expect(scopedSlotCall).toMatchObject({ + loading: false, + pagination: expectedPaginationData, + error: null, + agents: expectedAgentsResult, + }); + }); + + it.each` + methodName | expectedVariables + ${'nextPage'} | ${{ organizationId: MOCK_ORG_ID, before: null, after: 'eyJpZCI6IjEwIn0' }} + ${'prevPage'} | ${{ organizationId: MOCK_ORG_ID, before: 'eyJpZCI6IjE0In0', after: null }} + `( + 'refetches query with correct variables when $methodName is called', + async ({ methodName, expectedVariables }) => { + const scopedSlotCall = defaultSlotSpy.mock.lastCall[0]; + + await scopedSlotCall.pagination[methodName](); + + expect(organizationWorkspacesClusterAgentsQueryHandler).toHaveBeenCalledTimes(2); + expect(organizationWorkspacesClusterAgentsQueryHandler).toHaveBeenLastCalledWith( + expectedVariables, + ); + }, + ); + }); + }); + }); +}); diff --git a/ee/spec/frontend/workspaces/admin_settings/pages/app_spec.js b/ee/spec/frontend/workspaces/admin_settings/pages/app_spec.js index aed43d2677fda154e70556f76827000cc2a8cad1..71b0446d65297504906f6ded9da9c9537ac93e3a 100644 --- a/ee/spec/frontend/workspaces/admin_settings/pages/app_spec.js +++ b/ee/spec/frontend/workspaces/admin_settings/pages/app_spec.js @@ -1,44 +1,248 @@ import { nextTick } from 'vue'; -import { GlTableLite, GlLink } from '@gitlab/ui'; +import { + GlTableLite, + GlBadge, + GlLink, + GlAlert, + GlKeysetPagination, + GlSkeletonLoader, +} from '@gitlab/ui'; import { mountExtended } from 'helpers/vue_test_utils_helper'; import WorkspacesAgentAvailabilityApp from 'ee_component/workspaces/admin_settings/pages/app.vue'; import AvailabilityPopover from 'ee_component/workspaces/admin_settings/components/availability_popover.vue'; +import GetOrganizationWorkspacesClusterAgentsQuery from 'ee_component/workspaces/admin_settings/components/get_organization_workspaces_cluster_agents_query.vue'; +import { stubComponent } from 'helpers/stub_component'; const MOCK_ORG_ID = 'gid://gitlab/Organizations::Organization/1'; +const createMockAgents = (customAgent = {}) => { + return [ + { + availability: 'available', + group: 'Gitlab Org', + isConnected: false, + name: 'midnightowlgarden', + project: 'gitlab-agent-configurations', + url: 'http://test.host/gitlab-org/gitlab-agent-configurations/-/cluster_agents/midnightowlgarden', + workspacesEnabled: true, + ...customAgent, + }, + ]; +}; + +const MOCK_PAGINATION_DATA = { + show: true, + hasNextPage: true, + hasPreviousPage: false, + nextPage: jest.fn(), + prevPage: jest.fn(), +}; + describe('workspaces/admin_settings/pages/app.vue', () => { let wrapper; - const buildWrapper = () => { + const buildWrapper = (organizationWorkspacesClusterAgentsQueryState = {}) => { wrapper = mountExtended(WorkspacesAgentAvailabilityApp, { provide: { organizationId: MOCK_ORG_ID, - defaultExpanded: true, + }, + stubs: { + GetOrganizationWorkspacesClusterAgentsQuery: stubComponent( + GetOrganizationWorkspacesClusterAgentsQuery, + { + render() { + return this.$scopedSlots.default?.({ + loading: false, + agents: createMockAgents(), + error: null, + pagination: MOCK_PAGINATION_DATA, + ...organizationWorkspacesClusterAgentsQueryState, + }); + }, + }, + ), }, }); }; + + const findGetOrganizationWorkspacesClusterAgentsQuery = () => + wrapper.findComponent(GetOrganizationWorkspacesClusterAgentsQuery); const findAgentsTable = () => wrapper.findComponent(GlTableLite); const findAvailabilityPopover = () => wrapper.findComponent(AvailabilityPopover); + const findBadge = () => wrapper.findComponent(GlBadge); + const findAlert = () => wrapper.findComponent(GlAlert); + const findPagination = () => wrapper.findComponent(GlKeysetPagination); + const findLoadingState = () => wrapper.findComponent(GlSkeletonLoader); describe('default', () => { beforeEach(async () => { - buildWrapper(); + buildWrapper({ + loading: true, + }); + await nextTick(); + }); + + it('renders GetOrganizationWorkspacesClusterAgentsQuery component and passes organizationId', () => { + expect(findGetOrganizationWorkspacesClusterAgentsQuery().props('organizationId')).toBe( + MOCK_ORG_ID, + ); + }); + + it('does not render agents table', () => { + expect(findAgentsTable().exists()).toBe(false); + }); + + it('renders loading state', () => { + expect(findLoadingState().exists()).toBe(true); + }); + }); + + describe('when GetOrganizationWorkspacesClusterAgentsQuery component emits result event', () => { + it('renders empty state if no agents are returned', async () => { + buildWrapper({ + agents: [], + }); + await nextTick(); + + const emptyStateComponent = wrapper.findByTestId('agent-availability-empty-state'); + + expect(findAgentsTable().exists()).toBe(false); + expect(emptyStateComponent.exists()).toBe(true); + }); + + describe('when an error is returned', () => { + it('renders alert component', async () => { + buildWrapper({ + agents: [], + error: new Error('Some error'), + }); + + await nextTick(); + + const alertComponent = findAlert(); + expect(alertComponent.exists()).toBe(true); + expect(alertComponent.props('variant')).toBe('danger'); + expect(alertComponent.text()).toBe('Could not load agents. Refresh the page to try again.'); + }); + }); + + describe('when agents are returned', () => { + const mockResult = createMockAgents(); + beforeEach(async () => { + buildWrapper({ + agents: mockResult, + }); + await nextTick(); + }); + + it('renders agents table', () => { + expect(findAgentsTable().exists()).toBe(true); + }); + + it('renders popover in availability header column', () => { + expect(findAvailabilityPopover().exists()).toBe(true); + }); + + it('renders agent name with link to the agent page', () => { + const nameElement = wrapper.findComponent(GlLink); + + expect(nameElement.exists()).toBe(true); + expect(nameElement.attributes('href')).toBe(mockResult[0].url); + }); }); + }); + + describe('status badges', () => { + describe('connection status badge', () => { + it('renders correct badge variant based for connected', async () => { + buildWrapper({ + agents: createMockAgents({ isConnected: true }), + }); + + await nextTick(); + + const badge = findBadge(); + expect(badge.props('variant')).toBe('success'); + expect(badge.text()).toBe('Connected'); + }); + + it('renders correct badge variant based for not connected', async () => { + buildWrapper({ + agents: createMockAgents({ isConnected: false }), + }); + + await nextTick(); - it('renders agents table', () => { - expect(findAgentsTable().exists()).toBe(true); + const badge = findBadge(); + expect(badge.props('variant')).toBe('neutral'); + expect(badge.text()).toBe('Not connected'); + }); }); + }); - it('renders popover in availability header column', () => { - expect(findAvailabilityPopover().exists()).toBe(true); + describe('pagination', () => { + describe('when show is false', () => { + it('does not render component', async () => { + buildWrapper({ + pagination: { + show: false, + hasPreviousPage: false, + hasNextPage: false, + }, + }); + await nextTick(); + expect(findPagination().exists()).toBe(false); + }); }); - it('renders agent name with link to the agent page', () => { - const nameElement = wrapper.findComponent(GlLink); - expect(nameElement.exists()).toBe(true); - // TODO: update once we implement query: https://gitlab.com/gitlab-org/gitlab/-/issues/513370 - expect(nameElement.attributes('href')).toBe('#'); + describe('when show is true', () => { + const nextPageSpy = jest.fn(); + const prevPageSpy = jest.fn(); + + beforeEach(async () => { + nextPageSpy.mockClear(); + prevPageSpy.mockClear(); + + buildWrapper({ + pagination: { + ...MOCK_PAGINATION_DATA, + nextPage: nextPageSpy, + prevPage: prevPageSpy, + }, + }); + await nextTick(); + }); + + it('renders component if show is true', () => { + const paginationComponent = findPagination(); + + expect(paginationComponent.exists()).toBe(true); + expect(paginationComponent.props('hasNextPage')).toBe(MOCK_PAGINATION_DATA.hasNextPage); + expect(paginationComponent.props('hasPreviousPage')).toBe( + MOCK_PAGINATION_DATA.hasPreviousPage, + ); + }); + + it('calls correct method when next event is emitted', async () => { + const paginationComponent = findPagination(); + + paginationComponent.vm.$emit('next'); + await nextTick(); + + expect(nextPageSpy).toHaveBeenCalledTimes(1); + expect(prevPageSpy).not.toHaveBeenCalled(); + }); + + it('calls correct method when prev event is emitted', async () => { + const paginationComponent = findPagination(); + + paginationComponent.vm.$emit('prev'); + await nextTick(); + + expect(prevPageSpy).toHaveBeenCalledTimes(1); + expect(nextPageSpy).not.toHaveBeenCalled(); + }); }); }); }); diff --git a/ee/spec/frontend/workspaces/mock_data/index.js b/ee/spec/frontend/workspaces/mock_data/index.js index a50bf5f647e153bb22dd0b8bbb9b532ce62502fa..129db9a6c2b2b5033c74930a1042cd19f6bebbdc 100644 --- a/ee/spec/frontend/workspaces/mock_data/index.js +++ b/ee/spec/frontend/workspaces/mock_data/index.js @@ -660,3 +660,250 @@ export const DELETE_CLUSTER_AGENT_MAPPING_MUTATION_WITH_ERROR_RESULT = { }; export const NAMESPACE_ID = 'gid://gitlab/Group/81'; + +// these values correspond to ORGANIZATION_WORKSPACES_CLUSTER_AGENTS_QUERY_RESULT +export const ORGANIZATION_MAPPED_AGENTS_QUERY_RESULT = { + data: { + organization: { + id: 'gid://gitlab/Organizations::Organization/1', + mappedAgents: { + nodes: [ + { + id: 'gid://gitlab/Clusters::Agent/14', + }, + { + id: 'gid://gitlab/Clusters::Agent/12', + }, + { + id: 'gid://gitlab/Clusters::Agent/10', + }, + { + id: 'gid://gitlab/Clusters::Agent/17', + }, + ], + }, + }, + }, +}; + +export const ORGANIZATION_WORKSPACES_CLUSTER_AGENTS_QUERY_RESULT = { + data: { + organization: { + id: 'gid://gitlab/Organizations::Organization/1', + organizationWorkspacesClusterAgents: { + nodes: [ + /** + * Connection status: Not connected + * Workspaces agent config: enabled + * Availability: available + */ + { + id: 'gid://gitlab/Clusters::Agent/14', + name: 'midnightowlgarden', + webPath: '/gitlab-org/gitlab-agent-configurations/-/cluster_agents/midnightowlgarden', + project: { + id: 'gid://gitlab/Project/19', + name: 'gitlab-agent-configurations', + group: { + id: 'gid://gitlab/Group/24', + name: 'Gitlab Org', + }, + }, + workspacesAgentConfig: { + id: '', + enabled: true, + }, + connections: { + nodes: [], + }, + }, + /** + * Connection status: Not connected + * Workspaces agent config: enabled + * Availability: blocked + */ + { + id: 'gid://gitlab/Clusters::Agent/13', + name: 'coastalechovalley', + webPath: '/gitlab-org/gitlab-agent-configurations/-/cluster_agents/coastalechovalley', + project: { + id: 'gid://gitlab/Project/19', + name: 'gitlab-agent-configurations', + group: { + id: 'gid://gitlab/Group/24', + name: 'Gitlab Org', + }, + }, + workspacesAgentConfig: { + id: '', + enabled: true, + }, + connections: { + nodes: [], + }, + }, + /** + * Connection status: Not connected + * Workspaces agent config: not enabled + * Availability: available + */ + { + id: 'gid://gitlab/Clusters::Agent/12', + name: 'wandingbreezetale', + webPath: '/gitlab-org/gitlab-agent-configurations/-/cluster_agents/wandingbreezetale', + project: { + id: 'gid://gitlab/Project/19', + name: 'gitlab-agent-configurations', + group: { + id: 'gid://gitlab/Group/24', + name: 'Gitlab Org', + }, + }, + workspacesAgentConfig: null, + connections: { + nodes: [], + }, + }, + /** + * Connection status: Not connected + * Workspaces agent config: not enabled + * Availability: blocked + */ + { + id: 'gid://gitlab/Clusters::Agent/11', + name: 'crimsonmapleshadow', + webPath: '/gitlab-org/gitlab-agent-configurations/-/cluster_agents/crimsonmapleshadow', + project: { + id: 'gid://gitlab/Project/19', + name: 'gitlab-agent-configurations', + group: { + id: 'gid://gitlab/Group/24', + name: 'Gitlab Org', + }, + }, + workspacesAgentConfig: null, + connections: { + nodes: [], + }, + }, + /** + * Connection status: Connected + * Workspaces agent config: enabled + * Availability: available + */ + { + id: 'gid://gitlab/Clusters::Agent/10', + name: 'meadowsageharbor', + webPath: '/gitlab-org/gitlab-agent-configurations/-/cluster_agents/meadowsageharbor', + project: { + id: 'gid://gitlab/Project/19', + name: 'gitlab-agent-configurations', + group: { + id: 'gid://gitlab/Group/24', + name: 'Gitlab Org', + }, + }, + workspacesAgentConfig: { + id: '', + enabled: true, + }, + connections: { + nodes: [ + { + connectedAt: '2023-04-29T18:24:34Z', + }, + ], + }, + }, + /** + * Connection status: Connected + * Workspaces agent config: enabled + * Availability: blocked + */ + { + id: 'gid://gitlab/Clusters::Agent/16', + name: 'silvermoonharbor', + webPath: '/gitlab-org/gitlab-agent-configurations/-/cluster_agents/silvermoonharbor', + project: { + id: 'gid://gitlab/Project/19', + name: 'gitlab-agent-configurations', + group: { + id: 'gid://gitlab/Group/24', + name: 'Gitlab Org', + }, + }, + workspacesAgentConfig: { + id: '', + enabled: true, + }, + connections: { + nodes: [ + { + connectedAt: '2023-04-29T18:24:34Z', + }, + ], + }, + }, + /** + * Connection status: Connected + * Workspaces agent config: not enabled + * Availability: available + */ + { + id: 'gid://gitlab/Clusters::Agent/17', + name: 'silvermoonharbor', + webPath: '/gitlab-org/gitlab-agent-configurations/-/cluster_agents/silvermoonharbor', + project: { + id: 'gid://gitlab/Project/19', + name: 'gitlab-agent-configurations', + group: { + id: 'gid://gitlab/Group/24', + name: 'Gitlab Org', + }, + }, + workspacesAgentConfig: null, + connections: { + nodes: [ + { + connectedAt: '2023-04-29T18:24:34Z', + }, + ], + }, + }, + /** + * Connection status: Connected + * Workspaces agent config: not enabled + * Availability: blocked + */ + { + id: 'gid://gitlab/Clusters::Agent/18', + name: 'oceanbreezecliff', + webPath: '/gitlab-org/gitlab-agent-configurations/-/cluster_agents/oceanbreezecliff', + project: { + id: 'gid://gitlab/Project/19', + name: 'gitlab-agent-configurations', + group: { + id: 'gid://gitlab/Group/24', + name: 'Gitlab Org', + }, + }, + workspacesAgentConfig: null, + connections: { + nodes: [ + { + connectedAt: '2023-04-29T18:24:34Z', + }, + ], + }, + }, + ], + pageInfo: { + hasNextPage: true, + hasPreviousPage: false, + startCursor: 'eyJpZCI6IjE0In0', + endCursor: 'eyJpZCI6IjEwIn0', + }, + }, + }, + }, +}; diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 7f862f91ff8e853baf7307ea0c2c74c649353483..315e1dffd2a5cedb553da1d579fc4449ed63394b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -70475,6 +70475,9 @@ msgstr "" msgid "Workspaces|Connected" msgstr "" +msgid "Workspaces|Could not load agents. Refresh the page to try again." +msgstr "" + msgid "Workspaces|Could not load available agents. Refresh the page to try again." msgstr ""