Commit 13e00158 authored by Kerri Miller's avatar Kerri Miller

Merge branch '267147-terraform-state-versions-graphql' into 'master'

Add latest version field to Terraform state GraphQL type

See merge request gitlab-org/gitlab!45848
parents cf292505 5b3152a0
......@@ -27,6 +27,11 @@ module Types
null: true,
description: 'Timestamp the Terraform state was locked'
field :latest_version, Types::Terraform::StateVersionType,
complexity: 3,
null: true,
description: 'The latest version of the Terraform state'
field :created_at, Types::TimeType,
null: false,
description: 'Timestamp the Terraform state was created'
......
# frozen_string_literal: true
module Types
module Terraform
class StateVersionType < BaseObject
graphql_name 'TerraformStateVersion'
authorize :read_terraform_state
field :id, GraphQL::ID_TYPE,
null: false,
description: 'ID of the Terraform state version'
field :created_by_user, Types::UserType,
null: true,
authorize: :read_user,
description: 'The user that created this version',
resolve: -> (version, _, _) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, version.created_by_user_id).find }
field :created_at, Types::TimeType,
null: false,
description: 'Timestamp the version was created'
field :updated_at, Types::TimeType,
null: false,
description: 'Timestamp the version was updated'
end
end
end
......@@ -17,8 +17,15 @@ module Terraform
belongs_to :project
belongs_to :locked_by_user, class_name: 'User'
has_many :versions, class_name: 'Terraform::StateVersion', foreign_key: :terraform_state_id
has_one :latest_version, -> { ordered_by_version_desc }, class_name: 'Terraform::StateVersion', foreign_key: :terraform_state_id
has_many :versions,
class_name: 'Terraform::StateVersion',
foreign_key: :terraform_state_id,
inverse_of: :terraform_state
has_one :latest_version, -> { ordered_by_version_desc },
class_name: 'Terraform::StateVersion',
foreign_key: :terraform_state_id,
inverse_of: :terraform_state
scope :versioning_not_enabled, -> { where(versioning_enabled: false) }
scope :ordered_by_name, -> { order(:name) }
......
# frozen_string_literal: true
module Terraform
class StateVersionPolicy < BasePolicy
alias_method :terraform_state_version, :subject
delegate { terraform_state_version.terraform_state }
end
end
---
title: Add latest version field to Terraform state GraphQL type
merge_request: 45848
author:
type: added
......@@ -19334,6 +19334,11 @@ type TerraformState {
"""
id: ID!
"""
The latest version of the Terraform state
"""
latestVersion: TerraformStateVersion
"""
Timestamp the Terraform state was locked
"""
......@@ -19490,6 +19495,28 @@ type TerraformStateUnlockPayload {
errors: [String!]!
}
type TerraformStateVersion {
"""
Timestamp the version was created
"""
createdAt: Time!
"""
The user that created this version
"""
createdByUser: User
"""
ID of the Terraform state version
"""
id: ID!
"""
Timestamp the version was updated
"""
updatedAt: Time!
}
"""
Represents the Geo sync and verification state of a terraform state version
"""
......
......@@ -56034,6 +56034,20 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "latestVersion",
"description": "The latest version of the Terraform state",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "TerraformStateVersion",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "lockedAt",
"description": "Timestamp the Terraform state was locked",
......@@ -56510,6 +56524,87 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "TerraformStateVersion",
"description": null,
"fields": [
{
"name": "createdAt",
"description": "Timestamp the version was created",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Time",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "createdByUser",
"description": "The user that created this version",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "User",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": "ID of the Terraform state version",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "updatedAt",
"description": "Timestamp the version was updated",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "Time",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "TerraformStateVersionRegistry",
......@@ -2683,6 +2683,7 @@ Completion status of tasks.
| ----- | ---- | ----------- |
| `createdAt` | Time! | Timestamp the Terraform state was created |
| `id` | ID! | ID of the Terraform state |
| `latestVersion` | TerraformStateVersion | The latest version of the Terraform state |
| `lockedAt` | Time | Timestamp the Terraform state was locked |
| `lockedByUser` | User | The user currently holding a lock on the Terraform state |
| `name` | String! | Name of the Terraform state |
......@@ -2715,6 +2716,15 @@ Autogenerated return type of TerraformStateUnlock.
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
### TerraformStateVersion
| Field | Type | Description |
| ----- | ---- | ----------- |
| `createdAt` | Time! | Timestamp the version was created |
| `createdByUser` | User | The user that created this version |
| `id` | ID! | ID of the Terraform state version |
| `updatedAt` | Time! | Timestamp the version was updated |
### TerraformStateVersionRegistry
Represents the Geo sync and verification state of a terraform state version.
......
......@@ -19,7 +19,7 @@ FactoryBot.define do
trait :with_version do
after(:create) do |state|
create(:terraform_state_version, :with_file, terraform_state: state)
create(:terraform_state_version, terraform_state: state)
end
end
......
......@@ -7,7 +7,7 @@ RSpec.describe GitlabSchema.types['TerraformState'] do
it { expect(described_class).to require_graphql_authorizations(:read_terraform_state) }
describe 'fields' do
let(:fields) { %i[id name locked_by_user locked_at created_at updated_at] }
let(:fields) { %i[id name locked_by_user locked_at latest_version created_at updated_at] }
it { expect(described_class).to have_graphql_fields(fields) }
......@@ -17,5 +17,8 @@ RSpec.describe GitlabSchema.types['TerraformState'] do
it { expect(described_class.fields['lockedAt'].type).not_to be_non_null }
it { expect(described_class.fields['createdAt'].type).to be_non_null }
it { expect(described_class.fields['updatedAt'].type).to be_non_null }
it { expect(described_class.fields['latestVersion'].type).not_to be_non_null }
it { expect(described_class.fields['latestVersion'].complexity).to eq(3) }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['TerraformStateVersion'] do
it { expect(described_class.graphql_name).to eq('TerraformStateVersion') }
it { expect(described_class).to require_graphql_authorizations(:read_terraform_state) }
describe 'fields' do
let(:fields) { %i[id created_by_user created_at updated_at] }
it { expect(described_class).to have_graphql_fields(fields) }
it { expect(described_class.fields['id'].type).to be_non_null }
it { expect(described_class.fields['createdByUser'].type).not_to be_non_null }
it { expect(described_class.fields['createdAt'].type).to be_non_null }
it { expect(described_class.fields['updatedAt'].type).to be_non_null }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Terraform::StateVersionPolicy do
let_it_be(:project) { create(:project) }
let_it_be(:terraform_state) { create(:terraform_state, :with_version, project: project)}
subject { described_class.new(user, terraform_state.latest_version) }
describe 'rules' do
context 'no access' do
let(:user) { create(:user) }
it { is_expected.to be_disallowed(:read_terraform_state) }
it { is_expected.to be_disallowed(:admin_terraform_state) }
end
context 'developer' do
let(:user) { create(:user, developer_projects: [project]) }
it { is_expected.to be_allowed(:read_terraform_state) }
it { is_expected.to be_disallowed(:admin_terraform_state) }
end
context 'maintainer' do
let(:user) { create(:user, maintainer_projects: [project]) }
it { is_expected.to be_allowed(:read_terraform_state) }
it { is_expected.to be_allowed(:admin_terraform_state) }
end
end
end
......@@ -6,7 +6,8 @@ RSpec.describe 'query terraform states' do
include GraphqlHelpers
let_it_be(:project) { create(:project) }
let_it_be(:terraform_state) { create(:terraform_state, :locked, project: project) }
let_it_be(:terraform_state) { create(:terraform_state, :with_version, :locked, project: project) }
let_it_be(:latest_version) { terraform_state.latest_version }
let(:query) do
graphql_query_for(:project, { fullPath: project.full_path },
......@@ -20,6 +21,16 @@ RSpec.describe 'query terraform states' do
createdAt
updatedAt
latestVersion {
id
createdAt
updatedAt
createdByUser {
id
}
}
lockedByUser {
id
}
......@@ -37,13 +48,19 @@ RSpec.describe 'query terraform states' do
it 'returns terraform state data', :aggregate_failures do
state = data.dig('nodes', 0)
version = state['latestVersion']
expect(state['id']).to eq(terraform_state.to_global_id.to_s)
expect(state['name']).to eq(terraform_state.name)
expect(state['lockedAt']).to eq(terraform_state.locked_at.strftime('%Y-%m-%dT%H:%M:%SZ'))
expect(state['createdAt']).to eq(terraform_state.created_at.strftime('%Y-%m-%dT%H:%M:%SZ'))
expect(state['updatedAt']).to eq(terraform_state.updated_at.strftime('%Y-%m-%dT%H:%M:%SZ'))
expect(state['lockedAt']).to eq(terraform_state.locked_at.iso8601)
expect(state['createdAt']).to eq(terraform_state.created_at.iso8601)
expect(state['updatedAt']).to eq(terraform_state.updated_at.iso8601)
expect(state.dig('lockedByUser', 'id')).to eq(terraform_state.locked_by_user.to_global_id.to_s)
expect(version['id']).to eq(latest_version.to_global_id.to_s)
expect(version['createdAt']).to eq(terraform_state.created_at.iso8601)
expect(version['updatedAt']).to eq(terraform_state.updated_at.iso8601)
expect(version.dig('createdByUser', 'id')).to eq(latest_version.created_by_user.to_global_id.to_s)
end
it 'returns count of terraform states' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment