Commit bb5ec122 authored by Alex Kalderimis's avatar Alex Kalderimis

Change type of CiJob.needs

This changes the type of `CiJob.needs` from `JobType.connection_type`
to `BuildNeedType.connection_type`. This is because `Build#needs` does
not return a relation of builds, but one of `Ci::BuildNeed`.

This change is a backwards compatible bug-fix.
Any query that works now will continue to work.
parent 28a2f781
# frozen_string_literal: true
module Types
module Ci
# rubocop: disable Graphql/AuthorizeTypes
# This type is only accessible from CiJob
class BuildNeedType < BaseObject
graphql_name 'CiBuildNeed'
field :name, GraphQL::STRING_TYPE, null: true,
description: 'Name of the job we need to complete.'
end
end
end
......@@ -10,8 +10,8 @@ module Types
description: 'Pipeline the job belongs to'
field :name, GraphQL::STRING_TYPE, null: true,
description: 'Name of the job'
field :needs, JobType.connection_type, null: true,
description: 'Builds that must complete before the jobs run'
field :needs, BuildNeedType.connection_type, null: true,
description: 'References to builds that must complete before the jobs run'
field :detailed_status, Types::Ci::DetailedStatusType, null: true,
description: 'Detailed status of the job'
field :scheduled_at, Types::TimeType, null: true,
......
......@@ -9,6 +9,7 @@ module Types
field :name, GraphQL::STRING_TYPE, null: true,
description: 'Name of the stage'
field :groups, Ci::GroupType.connection_type, null: true,
extras: [:lookahead],
description: 'Group of jobs for the stage'
field :detailed_status, Types::Ci::DetailedStatusType, null: true,
description: 'Detailed status of the stage'
......@@ -18,24 +19,42 @@ module Types
end
# Issues one query per pipeline
def groups
BatchLoader::GraphQL.for([object.pipeline, object]).batch(default_value: []) do |keys, loader|
def groups(lookahead:)
needs_selected = %i[nodes jobs nodes]
.reduce(lookahead) { |q, f| q.selection(f) }
.selects?(:needs)
key = [object.pipeline, object, needs_selected]
BatchLoader::GraphQL.for(key).batch(default_value: []) do |keys, loader|
by_pipeline = keys.group_by(&:first)
include_needs = keys.any? { |k| k[2] }
by_pipeline.each do |pl, key_group|
project = pl.project
stages = key_group.map(&:second).uniq
indexed = stages.index_by(&:id)
results = pl.latest_statuses.where(stage_id: stages.map(&:id)) # rubocop: disable CodeReuse/ActiveRecord
results.group_by(&:stage_id).each do |stage_id, statuses|
jobs_for_pipeline(pl, stages.map(&:id), include_needs).each do |stage_id, statuses|
stage = indexed[stage_id]
groups = ::Ci::Group.fabricate(project, stage, statuses)
loader.call([pl, stage], groups)
# we don't know (and do not care) whether this set of jobs was
# loaded with needs preloaded as part of the key.
[true, false].each { |b| loader.call([pl, stage, b], groups) }
end
end
end
end
private
# rubocop: disable CodeReuse/ActiveRecord
def jobs_for_pipeline(pipeline, stage_ids, include_needs)
results = pipeline.latest_statuses.where(stage_id: stage_ids)
results = results.preload(:needs) if include_needs
results.group_by(&:stage_id)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end
---
title: Change type of CiJob.needs
merge_request: 50192
author:
type: fixed
......@@ -2288,6 +2288,48 @@ type BurnupChartDailyTotals {
scopeWeight: Int!
}
type CiBuildNeed {
"""
Name of the job we need to complete.
"""
name: String
}
"""
The connection type for CiBuildNeed.
"""
type CiBuildNeedConnection {
"""
A list of edges.
"""
edges: [CiBuildNeedEdge]
"""
A list of nodes.
"""
nodes: [CiBuildNeed]
"""
Information to aid in pagination.
"""
pageInfo: PageInfo!
}
"""
An edge in a connection.
"""
type CiBuildNeedEdge {
"""
A cursor for use in pagination.
"""
cursor: String!
"""
The item at the end of the edge.
"""
node: CiBuildNeed
}
type CiConfig {
"""
Linting errors
......@@ -2717,7 +2759,7 @@ type CiJob {
name: String
"""
Builds that must complete before the jobs run
References to builds that must complete before the jobs run
"""
needs(
"""
......@@ -2739,7 +2781,7 @@ type CiJob {
Returns the last _n_ elements from the list.
"""
last: Int
): CiJobConnection
): CiBuildNeedConnection
"""
Pipeline the job belongs to
......
......@@ -6101,6 +6101,145 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "CiBuildNeed",
"description": null,
"fields": [
{
"name": "name",
"description": "Name of the job we need to complete.",
"args": [
],
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "CiBuildNeedConnection",
"description": "The connection type for CiBuildNeed.",
"fields": [
{
"name": "edges",
"description": "A list of edges.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "CiBuildNeedEdge",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "nodes",
"description": "A list of nodes.",
"args": [
],
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "OBJECT",
"name": "CiBuildNeed",
"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": "CiBuildNeedEdge",
"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": "CiBuildNeed",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "CiConfig",
......@@ -7305,7 +7444,7 @@
},
{
"name": "needs",
"description": "Builds that must complete before the jobs run",
"description": "References to builds that must complete before the jobs run",
"args": [
{
"name": "after",
......@@ -7350,7 +7489,7 @@
],
"type": {
"kind": "OBJECT",
"name": "CiJobConnection",
"name": "CiBuildNeedConnection",
"ofType": null
},
"isDeprecated": false,
......@@ -375,6 +375,12 @@ Represents the total number of issues and their weights for a particular day.
| `scopeCount` | Int! | Number of issues as of this day |
| `scopeWeight` | Int! | Total weight of issues as of this day |
### CiBuildNeed
| Field | Type | Description |
| ----- | ---- | ----------- |
| `name` | String | Name of the job we need to complete. |
### CiConfig
| Field | Type | Description |
......@@ -430,7 +436,7 @@ Represents the total number of issues and their weights for a particular day.
| `artifacts` | CiJobArtifactConnection | Artifacts generated by the job |
| `detailedStatus` | DetailedStatus | Detailed status of the job |
| `name` | String | Name of the job |
| `needs` | CiJobConnection | Builds that must complete before the jobs run |
| `needs` | CiBuildNeedConnection | References to builds that must complete before the jobs run |
| `pipeline` | Pipeline | Pipeline the job belongs to |
| `scheduledAt` | Time | Schedule for the build |
......
......@@ -15,7 +15,7 @@ RSpec.describe 'Query.project.pipeline' do
let(:pipeline) do
pipeline = create(:ci_pipeline, project: project, user: user)
stage = create(:ci_stage_entity, project: project, pipeline: pipeline, name: 'first')
create(:commit_status, stage_id: stage.id, pipeline: pipeline, name: 'my test job')
create(:ci_build, stage_id: stage.id, pipeline: pipeline, name: 'my test job')
pipeline
end
......@@ -42,6 +42,9 @@ RSpec.describe 'Query.project.pipeline' do
jobs {
nodes {
name
needs {
nodes { #{all_graphql_fields_for('CiBuildNeed')} }
}
pipeline {
id
}
......@@ -53,6 +56,27 @@ RSpec.describe 'Query.project.pipeline' do
FIELDS
end
context 'when there are build needs' do
before do
pipeline.statuses.each do |build|
create_list(:ci_build_need, 2, build: build)
end
end
it 'reports the build needs' do
post_graphql(query, current_user: user)
expect(jobs_graphql_data).to contain_exactly a_hash_including(
'needs' => a_hash_including(
'nodes' => contain_exactly(
a_hash_including('name' => String),
a_hash_including('name' => String)
)
)
)
end
end
it 'returns the jobs of a pipeline stage' do
post_graphql(query, current_user: user)
......
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