diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index fbba2f2474b097c2780b5ad4df7ef85edad5d985..62101b4f3a6e442605a0481289c18464199bb2b6 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -1093,11 +1093,11 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | `actualStates` | [`[String!]`](#string) | Filter workspaces by actual states. | -| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent ids. | -| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace ids. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | +| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent GlobalIDs. | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | | `includeActualStates` **{warning-solid}** | [`[String!]`](#string) | **Deprecated** in 16.7. Use actual_states instead. | -| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project ids. | -| `userIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by user ids. | +| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project GlobalIDs. | +| `userIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by user GlobalIDs. | ## `Mutation` type @@ -8620,7 +8620,7 @@ Input type: `WorkspaceCreateInput` | Name | Type | Description | | ---- | ---- | ----------- | | `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. | -| `clusterAgentId` | [`ClustersAgentID!`](#clustersagentid) | ID of the cluster agent the created workspace will be associated with. | +| `clusterAgentId` | [`ClustersAgentID!`](#clustersagentid) | GlobalID of the cluster agent the created workspace will be associated with. | | `desiredState` | [`String!`](#string) | Desired state of the created workspace. | | `devfilePath` | [`String!`](#string) | Project repo git path containing the devfile used to configure the workspace. | | `devfileRef` | [`String!`](#string) | Project repo git ref containing the devfile used to configure the workspace. | @@ -14243,10 +14243,10 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | `actualStates` | [`[String!]`](#string) | Filter workspaces by actual states. | -| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent ids. | -| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace ids. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | +| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent GlobalIDs. | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | | `includeActualStates` **{warning-solid}** | [`[String!]`](#string) | **Deprecated** in 16.7. Use actual_states instead. | -| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project ids. | +| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project GlobalIDs. | ### `AgentConfiguration` @@ -14923,10 +14923,10 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | `actualStates` | [`[String!]`](#string) | Filter workspaces by actual states. | -| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent ids. | -| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace ids. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | +| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent GlobalIDs. | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | | `includeActualStates` **{warning-solid}** | [`[String!]`](#string) | **Deprecated** in 16.7. Use actual_states instead. | -| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project ids. | +| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project GlobalIDs. | ### `AwardEmoji` @@ -16001,6 +16001,30 @@ GitLab CI/CD configuration template. | `vulnerabilityImages` | [`VulnerabilityContainerImageConnection`](#vulnerabilitycontainerimageconnection) | Container images reported on the agent vulnerabilities. (see [Connections](#connections)) | | `webPath` | [`String`](#string) | Web path of the cluster agent. | +#### Fields with arguments + +##### `ClusterAgent.workspaces` + +Workspaces associated with the agent. + +WARNING: +**Introduced** in 16.7. +This feature is an Experiment. It can be changed or removed at any time. + +Returns [`WorkspaceConnection`](#workspaceconnection). + +This field returns a [connection](#connections). It accepts the +four standard [pagination arguments](#connection-pagination-arguments): +`before: String`, `after: String`, `first: Int`, and `last: Int`. + +###### Arguments + +| Name | Type | Description | +| ---- | ---- | ----------- | +| `actualStates` | [`[String!]`](#string) | Filter workspaces by actual states. | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | +| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project GlobalID. | + ### `ClusterAgentActivityEvent` #### Fields @@ -16936,10 +16960,10 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | `actualStates` | [`[String!]`](#string) | Filter workspaces by actual states. | -| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent ids. | -| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace ids. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | +| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent GlobalIDs. | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | | `includeActualStates` **{warning-solid}** | [`[String!]`](#string) | **Deprecated** in 16.7. Use actual_states instead. | -| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project ids. | +| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project GlobalIDs. | ### `CustomEmoji` @@ -21804,10 +21828,10 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | `actualStates` | [`[String!]`](#string) | Filter workspaces by actual states. | -| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent ids. | -| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace ids. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | +| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent GlobalIDs. | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | | `includeActualStates` **{warning-solid}** | [`[String!]`](#string) | **Deprecated** in 16.7. Use actual_states instead. | -| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project ids. | +| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project GlobalIDs. | ### `MergeRequestAuthor` @@ -22092,10 +22116,10 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | `actualStates` | [`[String!]`](#string) | Filter workspaces by actual states. | -| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent ids. | -| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace ids. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | +| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent GlobalIDs. | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | | `includeActualStates` **{warning-solid}** | [`[String!]`](#string) | **Deprecated** in 16.7. Use actual_states instead. | -| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project ids. | +| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project GlobalIDs. | ### `MergeRequestDiff` @@ -22443,10 +22467,10 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | `actualStates` | [`[String!]`](#string) | Filter workspaces by actual states. | -| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent ids. | -| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace ids. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | +| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent GlobalIDs. | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | | `includeActualStates` **{warning-solid}** | [`[String!]`](#string) | **Deprecated** in 16.7. Use actual_states instead. | -| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project ids. | +| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project GlobalIDs. | ### `MergeRequestPermissions` @@ -22767,10 +22791,10 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | `actualStates` | [`[String!]`](#string) | Filter workspaces by actual states. | -| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent ids. | -| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace ids. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | +| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent GlobalIDs. | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | | `includeActualStates` **{warning-solid}** | [`[String!]`](#string) | **Deprecated** in 16.7. Use actual_states instead. | -| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project ids. | +| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project GlobalIDs. | ### `Metadata` @@ -27833,10 +27857,10 @@ four standard [pagination arguments](#connection-pagination-arguments): | Name | Type | Description | | ---- | ---- | ----------- | | `actualStates` | [`[String!]`](#string) | Filter workspaces by actual states. | -| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent ids. | -| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace ids. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | +| `agentIds` | [`[ClustersAgentID!]`](#clustersagentid) | Filter workspaces by agent GlobalIDs. | +| `ids` | [`[RemoteDevelopmentWorkspaceID!]`](#remotedevelopmentworkspaceid) | Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`. | | `includeActualStates` **{warning-solid}** | [`[String!]`](#string) | **Deprecated** in 16.7. Use actual_states instead. | -| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project ids. | +| `projectIds` | [`[ProjectID!]`](#projectid) | Filter workspaces by project GlobalIDs. | ### `UserMergeRequestInteraction` diff --git a/ee/app/graphql/ee/types/clusters/agent_type.rb b/ee/app/graphql/ee/types/clusters/agent_type.rb index b7edda890cd8be041eade288abd75ccc1d452b01..c125b2e12a5492cf400a37e44dc4369ba1cdf85e 100644 --- a/ee/app/graphql/ee/types/clusters/agent_type.rb +++ b/ee/app/graphql/ee/types/clusters/agent_type.rb @@ -12,6 +12,13 @@ module AgentType null: true, description: 'Container images reported on the agent vulnerabilities.', resolver: ::Resolvers::Vulnerabilities::ContainerImagesResolver + + field :workspaces, + ::Types::RemoteDevelopment::WorkspaceType.connection_type, + null: true, + resolver: ::Resolvers::RemoteDevelopment::WorkspacesForAgentResolver, + description: 'Workspaces associated with the agent.', + alpha: { milestone: '16.7' } end end end diff --git a/ee/app/graphql/mutations/remote_development/workspaces/create.rb b/ee/app/graphql/mutations/remote_development/workspaces/create.rb index 11b01d9d1a6e31b0fdc7d1caa41362ae283d8c91..5e8a4f9bc51e9f798d05396de64ad8489f2f4ecb 100644 --- a/ee/app/graphql/mutations/remote_development/workspaces/create.rb +++ b/ee/app/graphql/mutations/remote_development/workspaces/create.rb @@ -18,7 +18,7 @@ class Create < BaseMutation argument :cluster_agent_id, ::Types::GlobalIDType[::Clusters::Agent], required: true, - description: 'ID of the cluster agent the created workspace will be associated with.' + description: 'GlobalID of the cluster agent the created workspace will be associated with.' # TODO: https://gitlab.com/gitlab-org/gitlab/-/issues/409772 - Make this a type:enum argument :desired_state, diff --git a/ee/app/graphql/resolvers/remote_development/workspaces_for_agent_resolver.rb b/ee/app/graphql/resolvers/remote_development/workspaces_for_agent_resolver.rb new file mode 100644 index 0000000000000000000000000000000000000000..880535807c02d9d399ba1806b2600b57a32f886e --- /dev/null +++ b/ee/app/graphql/resolvers/remote_development/workspaces_for_agent_resolver.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Resolvers + module RemoteDevelopment + class WorkspacesForAgentResolver < ::Resolvers::BaseResolver + include ResolvesIds + include Gitlab::Graphql::Authorize::AuthorizeResource + + type Types::RemoteDevelopment::WorkspaceType.connection_type, null: true + authorize :admin_cluster + authorizes_object! + + argument :ids, [::Types::GlobalIDType[::RemoteDevelopment::Workspace]], + required: false, + description: + 'Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`.' + + argument :project_ids, [::Types::GlobalIDType[Project]], + required: false, + description: 'Filter workspaces by project GlobalID.' + + argument :actual_states, [GraphQL::Types::String], + required: false, + description: 'Filter workspaces by actual states.' + + alias_method :agent, :object + + def resolve(**args) + unless License.feature_available?(:remote_development) + raise_resource_not_available_error! "'remote_development' licensed feature is not available" + end + + ::RemoteDevelopment::WorkspacesFinder.execute( + current_user: current_user, + agent_ids: [agent.id], + ids: resolve_ids(args[:ids]).map(&:to_i), + project_ids: resolve_ids(args[:project_ids]).map(&:to_i), + actual_states: args[:actual_states] || [] + ) + end + end + end +end diff --git a/ee/app/graphql/resolvers/remote_development/workspaces_for_current_user_resolver.rb b/ee/app/graphql/resolvers/remote_development/workspaces_for_current_user_resolver.rb index e29213394a9adb9c8e7262f74e21a70dd41967ce..4ca802928a50d01b996c66f1b5a531356c866209 100644 --- a/ee/app/graphql/resolvers/remote_development/workspaces_for_current_user_resolver.rb +++ b/ee/app/graphql/resolvers/remote_development/workspaces_for_current_user_resolver.rb @@ -11,15 +11,15 @@ class WorkspacesForCurrentUserResolver < ::Resolvers::BaseResolver argument :ids, [::Types::GlobalIDType[::RemoteDevelopment::Workspace]], required: false, description: - 'Filter workspaces by workspace ids. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`.' + 'Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`.' argument :project_ids, [::Types::GlobalIDType[Project]], required: false, - description: 'Filter workspaces by project ids.' + description: 'Filter workspaces by project GlobalIDs.' argument :agent_ids, [::Types::GlobalIDType[::Clusters::Agent]], required: false, - description: 'Filter workspaces by agent ids.' + description: 'Filter workspaces by agent GlobalIDs.' argument :include_actual_states, [GraphQL::Types::String], required: false, diff --git a/ee/app/graphql/resolvers/remote_development/workspaces_for_query_root_resolver.rb b/ee/app/graphql/resolvers/remote_development/workspaces_for_query_root_resolver.rb index 287e7eb951cb4be7115721898c4659fc67873d44..b96aea7190a5c12d8e66b5373b9944314238fc3f 100644 --- a/ee/app/graphql/resolvers/remote_development/workspaces_for_query_root_resolver.rb +++ b/ee/app/graphql/resolvers/remote_development/workspaces_for_query_root_resolver.rb @@ -16,19 +16,19 @@ class WorkspacesForQueryRootResolver < ::Resolvers::BaseResolver argument :ids, [::Types::GlobalIDType[::RemoteDevelopment::Workspace]], required: false, description: - 'Filter workspaces by workspace ids. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`.' + 'Filter workspaces by workspace GlobalIDs. For example, `["gid://gitlab/RemoteDevelopment::Workspace/1"]`.' argument :user_ids, [::Types::GlobalIDType[Project]], required: false, - description: 'Filter workspaces by user ids.' + description: 'Filter workspaces by user GlobalIDs.' argument :project_ids, [::Types::GlobalIDType[Project]], required: false, - description: 'Filter workspaces by project ids.' + description: 'Filter workspaces by project GlobalIDs.' argument :agent_ids, [::Types::GlobalIDType[::Clusters::Agent]], required: false, - description: 'Filter workspaces by agent ids.' + description: 'Filter workspaces by agent GlobalIDs.' argument :include_actual_states, [GraphQL::Types::String], required: false, diff --git a/ee/spec/requests/api/graphql/remote_development/README.md b/ee/spec/requests/api/graphql/remote_development/README.md index 4173d78688731f01f8082894857f63d58c3ccfd3..4e1a75e638a78f014efff5af69f42ba6fcf9e253 100644 --- a/ee/spec/requests/api/graphql/remote_development/README.md +++ b/ee/spec/requests/api/graphql/remote_development/README.md @@ -3,6 +3,9 @@ under the root `Query` type. For example: +- `ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces` + contains specs which test the `Query.project.clusterAgent.workspaces` field in the GraphQL API schema, + as well as any other field which contains a `clusterAgent` field or collection. - `ee/spec/requests/api/graphql/remote_development/current_user/workspaces` contains specs which test the `Query.currentUser.workspaces` field in the GraphQL API schema. - `ee/spec/requests/api/graphql/remote_development/workspace` diff --git a/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/shared.rb b/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/shared.rb new file mode 100644 index 0000000000000000000000000000000000000000..dab7b3861ec1f33bdf3abee106082f298cba3a92 --- /dev/null +++ b/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/shared.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require_relative '../../shared' + +RSpec.shared_context 'for a Query.project.clusterAgent.workspaces query' do + include GraphqlHelpers + + let_it_be(:cluster_admin_user) { create(:user) } + let_it_be(:authorized_user) { cluster_admin_user } + + # NOTE: We use the workspace owner as the unauthorized user, because they should not have any access to the workspace + # via the Query.project.clusterAgent.workspaces query, even if they otherwise have full access to the workspace. + let_it_be(:unauthorized_user, reload: true) { workspace.user } + + let_it_be(:agent, reload: true) { workspace.agent } + let_it_be(:agent_project, reload: true) { agent.project } + + let(:fields) do + <<~QUERY + nodes { + #{all_graphql_fields_for('workspaces'.classify, max_depth: 1)} + } + QUERY + end + + let(:query) do + graphql_query_for( + :project, + { full_path: agent_project.full_path }, + query_graphql_field( + :cluster_agent, + { name: agent.name }, + query_graphql_field('workspaces', args, fields) + ) + ) + end + + before do + agent_project.add_maintainer(cluster_admin_user) # rubocop:disable RSpec/BeforeAllRoleAssignment -- this needs to be `before`, not `before_all` + agent.update!(created_by_user: cluster_admin_user) + workspace.reload # Ensure loaded workspace fixture's agent reflects updated created_by_user + end + + subject { graphql_data.dig('project', 'clusterAgent', 'workspaces', 'nodes') } +end diff --git a/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/with_actual_states_arg_spec.rb b/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/with_actual_states_arg_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..951fa340a5816e300efedf6d7e5c999b02d72fe4 --- /dev/null +++ b/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/with_actual_states_arg_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_relative './shared' + +RSpec.describe 'Query.project.clusterAgent.workspaces(actual_states: [GraphQL::Types::String])', feature_category: :remote_development do + include_context 'with actual_states argument' + include_context 'for a Query.project.clusterAgent.workspaces query' + include_context 'with non_matching_workspace associated with same agent' + + it_behaves_like 'multiple workspaces query' +end diff --git a/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/with_ids_arg_spec.rb b/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/with_ids_arg_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..f020d66447535f38da5e207c1609ca234ae96f0c --- /dev/null +++ b/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/with_ids_arg_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_relative './shared' + +RSpec.describe 'Query.project.clusterAgent.workspaces(ids: [RemoteDevelopmentWorkspaceID!])', feature_category: :remote_development do + include_context 'with ids argument' + include_context 'for a Query.project.clusterAgent.workspaces query' + + it_behaves_like 'multiple workspaces query' +end diff --git a/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/with_no_args_spec.rb b/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/with_no_args_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..8313850a538c71d2390a1d606466a061f3776e88 --- /dev/null +++ b/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/with_no_args_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_relative './shared' + +RSpec.describe 'Query.project.clusterAgent.workspaces (with no arguments)', feature_category: :remote_development do + include_context 'with no arguments' + include_context 'for a Query.project.clusterAgent.workspaces query' + include_context 'with non_matching_workspace associated with other agent created' + + it_behaves_like 'multiple workspaces query' +end diff --git a/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/with_project_ids_arg_spec.rb b/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/with_project_ids_arg_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..73360bf853d169581f3b2c8a13fc753d11b48c65 --- /dev/null +++ b/ee/spec/requests/api/graphql/remote_development/cluster_agent/workspaces/with_project_ids_arg_spec.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_relative './shared' + +RSpec.describe 'Query.project.clusterAgent.workspaces(project_ids: [::Types::GlobalIDType[Project]!])', feature_category: :remote_development do + include_context 'with project_ids argument' + include_context 'for a Query.project.clusterAgent.workspaces query' + + it_behaves_like 'multiple workspaces query' +end diff --git a/ee/spec/requests/api/graphql/remote_development/current_user/workspaces/shared.rb b/ee/spec/requests/api/graphql/remote_development/current_user/workspaces/shared.rb index adfae4cf56c57116cb0f1777fed6ab2415c44a3d..a57a35e4d8ae2f3e1df09107ebe0644c764263e3 100644 --- a/ee/spec/requests/api/graphql/remote_development/current_user/workspaces/shared.rb +++ b/ee/spec/requests/api/graphql/remote_development/current_user/workspaces/shared.rb @@ -5,7 +5,7 @@ RSpec.shared_context 'for a Query.currentUser.workspaces query' do include GraphqlHelpers - let_it_be(:authorized_user) { workspace.user } + let_it_be(:authorized_user, reload: true) { workspace.user } let_it_be(:unauthorized_user) { create(:user) } include_context "with authorized user as developer on workspace's project" diff --git a/ee/spec/requests/api/graphql/remote_development/current_user/workspaces/with_no_args_spec.rb b/ee/spec/requests/api/graphql/remote_development/current_user/workspaces/with_no_args_spec.rb index 0e906b3e1a1dda67cd12115b065ef5868b9d459e..a5124014b63997cea4f5e2c3f95b77277ece04a6 100644 --- a/ee/spec/requests/api/graphql/remote_development/current_user/workspaces/with_no_args_spec.rb +++ b/ee/spec/requests/api/graphql/remote_development/current_user/workspaces/with_no_args_spec.rb @@ -8,7 +8,7 @@ include_context 'for a Query.currentUser.workspaces query' # create workspace owned by different user, to ensure it is not returned by the query - let_it_be(:non_matching_workspace) { create(:workspace, name: 'non-matching-workspace') } + let_it_be(:non_matching_workspace, reload: true) { create(:workspace, name: 'non-matching-workspace') } it_behaves_like 'multiple workspaces query' end diff --git a/ee/spec/requests/api/graphql/remote_development/shared.rb b/ee/spec/requests/api/graphql/remote_development/shared.rb index cc49269f7a6b32742ebdcfc504eae5b691bae118..f289773ca2a0dc7f5bddf66705645f5159920b39 100644 --- a/ee/spec/requests/api/graphql/remote_development/shared.rb +++ b/ee/spec/requests/api/graphql/remote_development/shared.rb @@ -7,7 +7,7 @@ RSpec.shared_context 'with no arguments' do include_context 'with unauthorized workspace created' - let_it_be(:workspace) { create(:workspace, name: 'matching-workspace') } + let_it_be(:workspace, reload: true) { create(:workspace, name: 'matching-workspace') } # NOTE: Specs including this context must define `non_matching_workspace` as follows: # let_it_be(:non_matching_workspace) { create(:workspace, name: 'non-matching-workspace', ...) } @@ -19,10 +19,12 @@ RSpec.shared_context 'with id arg' do include_context 'with unauthorized workspace created' - let_it_be(:workspace) { create(:workspace, name: 'matching-workspace') } + let_it_be(:workspace, reload: true) { create(:workspace, name: 'matching-workspace') } # create workspace with different ID but still owned by the same user, to ensure isn't returned by the query - let_it_be(:non_matching_workspace) { create(:workspace, user: workspace.user, name: 'non-matching-workspace') } + let_it_be(:non_matching_workspace, reload: true) do + create(:workspace, user: workspace.user, name: 'non-matching-workspace') + end let(:id) { workspace.to_global_id.to_s } let(:args) { { id: id } } @@ -31,10 +33,12 @@ RSpec.shared_context 'with ids argument' do include_context 'with unauthorized workspace created' - let_it_be(:workspace) { create(:workspace, name: 'matching-workspace') } + let_it_be(:workspace, reload: true) { create(:workspace, name: 'matching-workspace') } # create workspace with different ID but still owned by the same user, to ensure isn't returned by the query - let_it_be(:non_matching_workspace) { create(:workspace, user: workspace.user, name: 'non-matching-workspace') } + let_it_be(:non_matching_workspace, reload: true) do + create(:workspace, user: workspace.user, name: 'non-matching-workspace') + end let_it_be(:ids) { [workspace.to_global_id.to_s, unauthorized_workspace.to_global_id.to_s] } let_it_be(:args) { { ids: ids } } @@ -44,10 +48,10 @@ include_context 'with unauthorized workspace created' let_it_be(:project) { create(:project, :public) } - let_it_be(:workspace) { create(:workspace, project_id: project.id, name: 'matching-workspace') } + let_it_be(:workspace, reload: true) { create(:workspace, project_id: project.id, name: 'matching-workspace') } # create workspace with different project but still owned by the same user, to ensure isn't returned by the query - let_it_be(:non_matching_workspace) do + let_it_be(:non_matching_workspace, reload: true) do create(:workspace, user: workspace.user, name: 'non-matching-workspace') end @@ -59,28 +63,26 @@ include_context 'with unauthorized workspace created' let_it_be(:agent) { create(:ee_cluster_agent, :with_remote_development_agent_config) } - let_it_be(:workspace) { create(:workspace, agent: agent, name: 'matching-workspace') } + let_it_be(:workspace, reload: true) { create(:workspace, agent: agent, name: 'matching-workspace') } - # create workspace associated with different agent but owned by same user, to ensure isn't returned by the query - let_it_be(:other_agent) { create(:ee_cluster_agent, :with_remote_development_agent_config) } - let_it_be(:non_matching_workspace) do - create(:workspace, agent: other_agent, user: workspace.user, name: 'non-matching-workspace') - end + include_context 'with non_matching_workspace associated with other agent created' let(:agent_ids) { [agent.to_global_id.to_s, unauthorized_workspace.agent.to_global_id.to_s] } let(:args) { { agent_ids: agent_ids } } end RSpec.shared_context 'with actual_states argument' do - let_it_be(:matching_actual_state) { ::RemoteDevelopment::Workspaces::States::STOPPED } + let_it_be(:matching_actual_state) { ::RemoteDevelopment::Workspaces::States::CREATION_REQUESTED } include_context 'with unauthorized workspace created' - let_it_be(:workspace) { create(:workspace, actual_state: matching_actual_state, name: 'matching-workspace') } + let_it_be(:workspace, reload: true) do + create(:workspace, actual_state: matching_actual_state, name: 'matching-workspace') + end - # create workspace with non-matching actual state but owned by same user, to ensure it is not returned by the query + # create workspace with non-matching actual state, to ensure it is not returned by the query let_it_be(:non_matching_actual_state) { ::RemoteDevelopment::Workspaces::States::RUNNING } - let_it_be(:non_matching_workspace) do + let_it_be(:non_matching_workspace, reload: true) do create(:workspace, actual_state: non_matching_actual_state, user: workspace.user, name: 'non-matching-workspace') end @@ -192,6 +194,22 @@ let_it_be(:other_workspace) { create(:workspace, name: 'other-workspace') } end +RSpec.shared_context 'with non_matching_workspace associated with same agent' do + before do + # Ensure the non-matching workspace is also associated with the same agent + non_matching_workspace.update!(agent: agent) + non_matching_workspace.reload + end +end + +RSpec.shared_context 'with non_matching_workspace associated with other agent created' do + # create workspace associated with different agent but owned by same user, to ensure isn't returned by the query + let_it_be(:other_agent) { create(:ee_cluster_agent, :with_remote_development_agent_config) } + let_it_be(:non_matching_workspace, reload: true) do + create(:workspace, agent: other_agent, user: workspace.user, name: 'non-matching-workspace') + end +end + RSpec.shared_context 'with unauthorized workspace created' do # The authorized_user will not be authorized to see the `other-workspace`. We don't name it # `unauthorized-workspace`, because the admin is still authorized to see it. diff --git a/ee/spec/requests/api/graphql/remote_development/workspaces/shared.rb b/ee/spec/requests/api/graphql/remote_development/workspaces/shared.rb index 1d3451c88429a1be3a83cad029c1c4f53ac990f4..57500dca65b137204f5ec7e1ad26afc8c8d5b0f4 100644 --- a/ee/spec/requests/api/graphql/remote_development/workspaces/shared.rb +++ b/ee/spec/requests/api/graphql/remote_development/workspaces/shared.rb @@ -8,7 +8,7 @@ let_it_be(:authorized_user) { create(:admin) } # Only instance admins may use this query, all other users, even workspace owners, will get an empty result - let_it_be(:workspace_owner) { workspace.user } + let_it_be(:workspace_owner, reload: true) { workspace.user } let_it_be(:unauthorized_user) { workspace_owner } let(:fields) do