Add new query API to expose workspace variables in GraphQL
Issue: Expose workspace_variables in graphQL API (#473138 - closed)
What does this MR do and why?
Why this is needed?
On workspace creation a user can specify variables(key/value) that get injected into a workspace. We store these in the workspace_variables table along with some static variables that are created by default on workspace creation.
We want to expose the workspace_variables table data through a graphQL API for the following reasons:
- Making the data available to third-party integrations or user automations/scripting/reporting
- Facilitating QA/E2E testing
- Enable frontend to access this data for any future UI features for workspace variables.
This is a similar use case to CiProjectVariable
What it does
With this MR we are introducing a query for workspaceVariables and the following changes
-
Migration to add a new field
user_providedand backfill. This is needed because currently we don't have a way to distinguish between static variables that get injected by default and variables that the user specified. We already have a list of all static type variables, so based on this we can determine which variables were added by users and set the flag for those records. database -
Adds a new
Type.WorkspaceVariableand field to theType.Workspacewhich returns a list of all user provided variables associated with the workspace. User providedworkspace_variablesare preloaded as the association:user_provided_workspace_variablesin the workspaces resolver if the field is selected in the query. GraphQL -
Introduces a new
WorkspaceVariableTypeEnumthat can be used to in bothType.WorkspaceVariableInputandType.WorkspaceVariablefor determining the type of the workspace_variable. GraphQL -
Adds a new authorization policy for
Type.WorkspaceVariablewhich delegates authorization to the workspace. Any user with theread_workspaceability is able to queryworkspaceVariables -
Introduces
resolve_with_lookaheadin the workspaces resolvers to preloadworkspace_variables. Refactors common code for workspaces resolvers into a base resolver that all workspaces resolvers inherit from. Category:Workspaces
MR acceptance checklist
Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Screenshots or screen recordings
Screenshots are required for UI changes, and strongly recommended for all other merge requests.
This MR does not contain any UI changes
GraphQL
| Query.workspaces.workspaceVariables | Query.currentUser.workspaces.workspaceVariables | Query.workspace.workspaceVariables |
|---|---|---|
![]() |
![]() |
![]() |
The above queries don't suffer from the N+1 problem, running Query.currentUser.workspaces.workspaceVariables results in only 2 queries:
Database Review
We are doing the backfill operation in a regular migration because the application code relies on the user_provided field to be present.
Total records for workspace_variables => 48949 on production as of 01.08.2025 out of which only 70 need to be updated
How to set up and validate locally
Numbered steps to set up and validate the change are strongly suggested.
-
Setup workspaces locally following this guide
-
Run the migration
bin/rails db:migrate -
Navigate to
http://gdk.test:3000/-/remote_development/workspaces/and create a new workspace with some variables (Add variable button) -
In the rails console validate authorization policy for workspace_variables
workspace_variable = RemoteDevelopment::WorkspaceVariable.where(workspace_id: workspace.id).last #User is an admin admin_user = User.where(admin: true).first policy = RemoteDevelopment::WorkspaceVariablePolicy.new(admin_user, workspace_variable) policy.debug(:read_workspace_variable) ## Output @enabled=true, @prevented=false #User is the workspace owner policy = RemoteDevelopment::WorkspaceVariablePolicy.new(workspace_variable.workspace.user, workspace_variable) policy.debug(:read_workspace_variable) ## Output @enabled=true, @prevented=false #Unauthorized User unauthorized_user = User.second # any non-admin user that is not associated with the workspace policy = RemoteDevelopment::WorkspaceVariablePolicy.new(unauthorized_user, workspace_variable) policy.debug(:read_workspace_variable) ## Output @enabled=false, @prevented=true -
Go to the GraphQL Explorer, http://gdk.test:3000/-/graphql-explorer, and run the following query
query { currentUser { workspaces { nodes { name workspaceVariables { nodes { id key value variableType createdAt updatedAt } } } } } }
Troubleshooting
The value field on the workspace_variables is an encrypted field. If during testing the API returns an error response related to attr_encrypted gem. Check and validate whether the value field on the RemoteDevelopment::WorkspaceVariable can be decrypted by using the current secrets locally.
Ref: Integrity Check
bundle exec rake gitlab:doctor:secrets RAILS_ENV=production




