Commit 88f935cb authored by Kerri Miller's avatar Kerri Miller

Merge branch 'lm-update-w-connections-graphql' into 'master'

Add connection type to ci config fields

See merge request gitlab-org/gitlab!50160
parents f589d1a8 90558f87
......@@ -11,7 +11,7 @@ module Types
description: 'Linting errors'
field :merged_yaml, GraphQL::STRING_TYPE, null: true,
description: 'Merged CI config YAML'
field :stages, [Types::Ci::Config::StageType], null: true,
field :stages, Types::Ci::Config::StageType.connection_type, null: true,
description: 'Stages of the pipeline'
field :status, Types::Ci::Config::StatusEnum, null: true,
description: 'Status of linting, can be either valid or invalid'
......
......@@ -9,7 +9,7 @@ module Types
field :name, GraphQL::STRING_TYPE, null: true,
description: 'Name of the job group'
field :jobs, [Types::Ci::Config::JobType], null: true,
field :jobs, Types::Ci::Config::JobType.connection_type, null: true,
description: 'Jobs in group'
field :size, GraphQL::INT_TYPE, null: true,
description: 'Size of the job group'
......
......@@ -13,7 +13,7 @@ module Types
description: 'Name of the job group'
field :stage, GraphQL::STRING_TYPE, null: true,
description: 'Name of the job stage'
field :needs, [Types::Ci::Config::NeedType], null: true,
field :needs, Types::Ci::Config::NeedType.connection_type, null: true,
description: 'Builds that must complete before the jobs run'
end
end
......
......@@ -9,7 +9,7 @@ module Types
field :name, GraphQL::STRING_TYPE, null: true,
description: 'Name of the stage'
field :groups, [Types::Ci::Config::GroupType], null: true,
field :groups, Types::Ci::Config::GroupType.connection_type, null: true,
description: 'Groups of jobs for the stage'
end
end
......
---
title: Update ci config type for GraphQL to use connection_type
merge_request: 50160
author:
type: changed
......@@ -2291,7 +2291,27 @@ type CiConfig {
"""
Stages of the pipeline
"""
stages: [CiConfigStage!]
stages(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): CiConfigStageConnection
"""
Status of linting, can be either valid or invalid
......@@ -2303,7 +2323,27 @@ type CiConfigGroup {
"""
Jobs in group
"""
jobs: [CiConfigJob!]
jobs(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): CiConfigJobConnection
"""
Name of the job group
......@@ -2316,6 +2356,41 @@ type CiConfigGroup {
size: Int
}
"""
The connection type for CiConfigGroup.
"""
type CiConfigGroupConnection {
"""
A list of edges.
"""
edges: [CiConfigGroupEdge]
"""
A list of nodes.
"""
nodes: [CiConfigGroup]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type CiConfigGroupEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: CiConfigGroup
}
type CiConfigJob {
"""
Name of the job group
......@@ -2330,7 +2405,27 @@ type CiConfigJob {
"""
Builds that must complete before the jobs run
"""
needs: [CiConfigNeed!]
needs(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): CiConfigNeedConnection
"""
Name of the job stage
......@@ -2338,6 +2433,41 @@ type CiConfigJob {
stage: String
}
"""
The connection type for CiConfigJob.
"""
type CiConfigJobConnection {
"""
A list of edges.
"""
edges: [CiConfigJobEdge]
"""
A list of nodes.
"""
nodes: [CiConfigJob]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type CiConfigJobEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: CiConfigJob
}
type CiConfigNeed {
"""
Name of the need
......@@ -2345,11 +2475,66 @@ type CiConfigNeed {
name: String
}
"""
The connection type for CiConfigNeed.
"""
type CiConfigNeedConnection {
"""
A list of edges.
"""
edges: [CiConfigNeedEdge]
"""
A list of nodes.
"""
nodes: [CiConfigNeed]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type CiConfigNeedEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: CiConfigNeed
}
type CiConfigStage {
"""
Groups of jobs for the stage
"""
groups: [CiConfigGroup!]
groups(
"""
Returns the elements in the list that come after the specified cursor.
"""
after: String
"""
Returns the elements in the list that come before the specified cursor.
"""
before: String
"""
Returns the first _n_ elements from the list.
"""
first: Int
"""
Returns the last _n_ elements from the list.
"""
last: Int
): CiConfigGroupConnection
"""
Name of the stage
......@@ -2357,6 +2542,41 @@ type CiConfigStage {
name: String
}
"""
The connection type for CiConfigStage.
"""
type CiConfigStageConnection {
"""
A list of edges.
"""
edges: [CiConfigStageEdge]
"""
A list of nodes.
"""
nodes: [CiConfigStage]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type CiConfigStageEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: CiConfigStage
}
"""
Values for YAML processor result
"""
......
......@@ -6125,6 +6125,198 @@
{
"name": "stages",
"description": "Stages of the pipeline",
"args": [
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "CiConfigStageConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "status",
"description": "Status of linting, can be either valid or invalid",
"args": [
],
"type": {
"kind": "ENUM",
"name": "CiConfigStatus",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "CiConfigGroup",
"description": null,
"fields": [
{
"name": "jobs",
"description": "Jobs in group",
"args": [
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "CiConfigJobConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"description": "Name of the job group",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "size",
"description": "Size of the job group",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "CiConfigGroupConnection",
"description": "The connection type for CiConfigGroup.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "CiConfigGroupEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
......@@ -6132,28 +6324,248 @@
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "CiConfigGroup",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "CiConfigGroupEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "CiConfigStage",
"name": "CiConfigGroup",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "CiConfigJob",
"description": null,
"fields": [
{
"name": "groupName",
"description": "Name of the job group",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"description": "Name of the job",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "needs",
"description": "Builds that must complete before the jobs run",
"args": [
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "CiConfigNeedConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "stage",
"description": "Name of the job stage",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "CiConfigJobConnection",
"description": "The connection type for CiConfigJob.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "CiConfigJobEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "CiConfigJob",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "status",
"description": "Status of linting, can be either valid or invalid",
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "ENUM",
"name": "CiConfigStatus",
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
......@@ -6168,54 +6580,63 @@
},
{
"kind": "OBJECT",
"name": "CiConfigGroup",
"description": null,
"name": "CiConfigJobEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "jobs",
"description": "Jobs in group",
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "CiConfigJob",
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"description": "Name of the job group",
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"kind": "OBJECT",
"name": "CiConfigJob",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"name": "size",
"description": "Size of the job group",
"kind": "OBJECT",
"name": "CiConfigNeed",
"description": null,
"fields": [
{
"name": "name",
"description": "Name of the need",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "Int",
"name": "String",
"ofType": null
},
"isDeprecated": false,
......@@ -6231,69 +6652,104 @@
},
{
"kind": "OBJECT",
"name": "CiConfigJob",
"description": null,
"name": "CiConfigNeedConnection",
"description": "The connection type for CiConfigNeed.",
"fields": [
{
"name": "groupName",
"description": "Name of the job group",
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "CiConfigNeedEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"description": "Name of the job",
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "CiConfigNeed",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "needs",
"description": "Builds that must complete before the jobs run",
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "CiConfigNeed",
"name": "PageInfo",
"ofType": null
}
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"name": "stage",
"description": "Name of the job stage",
"kind": "OBJECT",
"name": "CiConfigNeedEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "CiConfigNeed",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
......@@ -6308,12 +6764,65 @@
},
{
"kind": "OBJECT",
"name": "CiConfigNeed",
"name": "CiConfigStage",
"description": null,
"fields": [
{
"name": "groups",
"description": "Groups of jobs for the stage",
"args": [
{
"name": "after",
"description": "Returns the elements in the list that come after the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "before",
"description": "Returns the elements in the list that come before the specified cursor.",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "first",
"description": "Returns the first _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
},
{
"name": "last",
"description": "Returns the last _n_ elements from the list.",
"type": {
"kind": "SCALAR",
"name": "Int",
"ofType": null
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "CiConfigGroupConnection",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "name",
"description": "Name of the need",
"description": "Name of the stage",
"args": [
],
......@@ -6335,12 +6844,12 @@
},
{
"kind": "OBJECT",
"name": "CiConfigStage",
"description": null,
"name": "CiConfigStageConnection",
"description": "The connection type for CiConfigStage.",
"fields": [
{
"name": "groups",
"description": "Groups of jobs for the stage",
"name": "edges",
"description": "A list of edges.",
"args": [
],
......@@ -6348,28 +6857,91 @@
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"kind": "OBJECT",
"name": "CiConfigStageEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "CiConfigGroup",
"name": "CiConfigStage",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "pageInfo",
"description": "Information to aid in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "PageInfo",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"name": "name",
"description": "Name of the stage",
"kind": "OBJECT",
"name": "CiConfigStageEdge",
"description": "An edge in a connection.",
"fields": [
{
"name": "cursor",
"description": "A cursor for use in pagination.",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "node",
"description": "The item at the end of the edge.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "CiConfigStage",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
......@@ -381,14 +381,14 @@ Represents the total number of issues and their weights for a particular day.
| ----- | ---- | ----------- |
| `errors` | String! => Array | Linting errors |
| `mergedYaml` | String | Merged CI config YAML |
| `stages` | CiConfigStage! => Array | Stages of the pipeline |
| `stages` | CiConfigStageConnection | Stages of the pipeline |
| `status` | CiConfigStatus | Status of linting, can be either valid or invalid |
### CiConfigGroup
| Field | Type | Description |
| ----- | ---- | ----------- |
| `jobs` | CiConfigJob! => Array | Jobs in group |
| `jobs` | CiConfigJobConnection | Jobs in group |
| `name` | String | Name of the job group |
| `size` | Int | Size of the job group |
......@@ -398,7 +398,7 @@ Represents the total number of issues and their weights for a particular day.
| ----- | ---- | ----------- |
| `groupName` | String | Name of the job group |
| `name` | String | Name of the job |
| `needs` | CiConfigNeed! => Array | Builds that must complete before the jobs run |
| `needs` | CiConfigNeedConnection | Builds that must complete before the jobs run |
| `stage` | String | Name of the job stage |
### CiConfigNeed
......@@ -411,7 +411,7 @@ Represents the total number of issues and their weights for a particular day.
| Field | Type | Description |
| ----- | ---- | ----------- |
| `groups` | CiConfigGroup! => Array | Groups of jobs for the stage |
| `groups` | CiConfigGroupConnection | Groups of jobs for the stage |
| `name` | String | Name of the stage |
### CiGroup
......
......@@ -20,15 +20,19 @@ RSpec.describe 'Query.ciConfig' do
status
errors
stages {
nodes {
name
groups {
nodes {
name
size
jobs {
nodes {
name
groupName
stage
needs {
nodes {
name
}
}
......@@ -36,6 +40,10 @@ RSpec.describe 'Query.ciConfig' do
}
}
}
}
}
}
}
)
end
......@@ -50,42 +58,60 @@ RSpec.describe 'Query.ciConfig' do
"status" => "VALID",
"errors" => [],
"stages" =>
{
"nodes" =>
[
{
"name" => "build",
"groups" =>
{
"nodes" =>
[
{
"name" => "rspec",
"size" => 2,
"jobs" =>
{
"nodes" =>
[
{ "name" => "rspec 0 1", "groupName" => "rspec", "stage" => "build", "needs" => [] },
{ "name" => "rspec 0 2", "groupName" => "rspec", "stage" => "build", "needs" => [] }
{ "name" => "rspec 0 1", "groupName" => "rspec", "stage" => "build", "needs" => { "nodes" => [] } },
{ "name" => "rspec 0 2", "groupName" => "rspec", "stage" => "build", "needs" => { "nodes" => [] } }
]
}
},
{
"name" => "spinach", "size" => 1, "jobs" =>
{
"nodes" =>
[
{ "name" => "spinach", "groupName" => "spinach", "stage" => "build", "needs" => [] }
{ "name" => "spinach", "groupName" => "spinach", "stage" => "build", "needs" => { "nodes" => [] } }
]
}
}
]
}
},
{
"name" => "test",
"groups" =>
{
"nodes" =>
[
{
"name" => "docker",
"size" => 1,
"jobs" => [
{ "name" => "docker", "groupName" => "docker", "stage" => "test", "needs" => [{ "name" => "spinach" }, { "name" => "rspec 0 1" }] }
"jobs" =>
{
"nodes" => [
{ "name" => "docker", "groupName" => "docker", "stage" => "test", "needs" => { "nodes" => [{ "name" => "spinach" }, { "name" => "rspec 0 1" }] } }
]
}
}
]
}
}
]
}
)
end
end
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