Commit d8c72787 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch '330308-dast-scheduler-fetch-query' into 'master'

Add DAST Profile Schedule to Profile Type Query

See merge request gitlab-org/gitlab!68050
parents 808858d2 ab86fa4b
...@@ -8565,6 +8565,7 @@ Represents a DAST Profile. ...@@ -8565,6 +8565,7 @@ Represents a DAST Profile.
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="dastprofilebranch"></a>`branch` | [`DastProfileBranch`](#dastprofilebranch) | The associated branch. | | <a id="dastprofilebranch"></a>`branch` | [`DastProfileBranch`](#dastprofilebranch) | The associated branch. |
| <a id="dastprofiledastprofileschedule"></a>`dastProfileSchedule` | [`DastProfileSchedule`](#dastprofileschedule) | Associated profile schedule. Will always return `null` if `dast_on_demand_scans_scheduler` feature flag is disabled. |
| <a id="dastprofiledastscannerprofile"></a>`dastScannerProfile` | [`DastScannerProfile`](#dastscannerprofile) | The associated scanner profile. | | <a id="dastprofiledastscannerprofile"></a>`dastScannerProfile` | [`DastScannerProfile`](#dastscannerprofile) | The associated scanner profile. |
| <a id="dastprofiledastsiteprofile"></a>`dastSiteProfile` | [`DastSiteProfile`](#dastsiteprofile) | The associated site profile. | | <a id="dastprofiledastsiteprofile"></a>`dastSiteProfile` | [`DastSiteProfile`](#dastsiteprofile) | The associated site profile. |
| <a id="dastprofiledescription"></a>`description` | [`String`](#string) | The description of the scan. | | <a id="dastprofiledescription"></a>`description` | [`String`](#string) | The description of the scan. |
...@@ -8583,6 +8584,32 @@ Represents a DAST Profile Branch. ...@@ -8583,6 +8584,32 @@ Represents a DAST Profile Branch.
| <a id="dastprofilebranchexists"></a>`exists` | [`Boolean`](#boolean) | Indicates whether or not the branch exists. | | <a id="dastprofilebranchexists"></a>`exists` | [`Boolean`](#boolean) | Indicates whether or not the branch exists. |
| <a id="dastprofilebranchname"></a>`name` | [`String`](#string) | The name of the branch. | | <a id="dastprofilebranchname"></a>`name` | [`String`](#string) | The name of the branch. |
### `DastProfileCadence`
Represents DAST Profile Cadence.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="dastprofilecadenceduration"></a>`duration` | [`Int`](#int) | Duration of the DAST profile cadence. |
| <a id="dastprofilecadenceunit"></a>`unit` | [`DastProfileCadenceUnit`](#dastprofilecadenceunit) | Unit for the duration of DAST profile cadence. |
### `DastProfileSchedule`
Represents a DAST profile schedule.
#### Fields
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="dastprofilescheduleactive"></a>`active` | [`Boolean`](#boolean) | Status of the DAST profile schedule. |
| <a id="dastprofileschedulecadence"></a>`cadence` | [`DastProfileCadence`](#dastprofilecadence) | Cadence of the DAST profile schedule. |
| <a id="dastprofilescheduleid"></a>`id` | [`DastProfileScheduleID!`](#dastprofilescheduleid) | ID of the DAST profile schedule. |
| <a id="dastprofileschedulenextrunat"></a>`nextRunAt` | [`Time`](#time) | Next run time of the DAST profile schedule in the given timezone. |
| <a id="dastprofileschedulestartsat"></a>`startsAt` | [`Time`](#time) | Start time of the DAST profile schedule in the given timezone. |
| <a id="dastprofilescheduletimezone"></a>`timezone` | [`String`](#string) | Time zone of the start time of the DAST profile schedule. |
### `DastScannerProfile` ### `DastScannerProfile`
Represents a DAST scanner profile. Represents a DAST scanner profile.
...@@ -16443,6 +16470,12 @@ A `DastProfileID` is a global ID. It is encoded as a string. ...@@ -16443,6 +16470,12 @@ A `DastProfileID` is a global ID. It is encoded as a string.
An example `DastProfileID` is: `"gid://gitlab/Dast::Profile/1"`. An example `DastProfileID` is: `"gid://gitlab/Dast::Profile/1"`.
### `DastProfileScheduleID`
A `DastProfileScheduleID` is a global ID. It is encoded as a string.
An example `DastProfileScheduleID` is: `"gid://gitlab/Dast::ProfileSchedule/1"`.
### `DastScannerProfileID` ### `DastScannerProfileID`
A `DastScannerProfileID` is a global ID. It is encoded as a string. A `DastScannerProfileID` is a global ID. It is encoded as a string.
......
...@@ -25,7 +25,8 @@ module Resolvers ...@@ -25,7 +25,8 @@ module Resolvers
def preloads def preloads
{ {
dast_site_profile: [{ dast_site_profile: [:dast_site, :secret_variables] }], dast_site_profile: [{ dast_site_profile: [:dast_site, :secret_variables] }],
dast_scanner_profile: [:dast_scanner_profile] dast_scanner_profile: [:dast_scanner_profile],
dast_profile_schedule: [:dast_profile_schedule]
} }
end end
......
# frozen_string_literal: true
# Disabling this cop as the auth check is happening in ProfileScheduleType.
# ProfileCadenceType is a dependent entity on ProfileScheduleType and does not exist without it.
# rubocop:disable Graphql/AuthorizeTypes
module Types
module Dast
class ProfileCadenceType < BaseObject
graphql_name 'DastProfileCadence'
description 'Represents DAST Profile Cadence.'
field :unit, ::Types::Dast::ProfileCadenceUnitEnum,
null: true,
description: 'Unit for the duration of DAST profile cadence.'
field :duration, GraphQL::Types::Int,
null: true,
description: 'Duration of the DAST profile cadence.'
end
end
end
# frozen_string_literal: true
module Types
module Dast
class ProfileScheduleType < BaseObject
graphql_name 'DastProfileSchedule'
description 'Represents a DAST profile schedule.'
authorize :read_on_demand_scans
field :id, ::Types::GlobalIDType[::Dast::ProfileSchedule], null: false,
description: 'ID of the DAST profile schedule.'
field :active, GraphQL::Types::Boolean, null: true,
description: 'Status of the DAST profile schedule.'
field :starts_at, Types::TimeType, null: true,
description: 'Start time of the DAST profile schedule in the given timezone.'
field :timezone, GraphQL::Types::String, null: true,
description: 'Time zone of the start time of the DAST profile schedule.'
field :cadence, Types::Dast::ProfileCadenceType, null: true,
description: 'Cadence of the DAST profile schedule.'
field :next_run_at, Types::TimeType, null: true,
description: 'Next run time of the DAST profile schedule in the given timezone.'
def starts_at
return unless object.starts_at && object.timezone
object.starts_at.in_time_zone(object.timezone)
end
def next_run_at
return unless object.next_run_at && object.timezone
object.next_run_at.in_time_zone(object.timezone)
end
end
end
end
...@@ -23,6 +23,10 @@ module Types ...@@ -23,6 +23,10 @@ module Types
field :dast_scanner_profile, DastScannerProfileType, null: true, field :dast_scanner_profile, DastScannerProfileType, null: true,
description: 'The associated scanner profile.' description: 'The associated scanner profile.'
field :dast_profile_schedule, ::Types::Dast::ProfileScheduleType, null: true,
description: 'Associated profile schedule. Will always return `null` ' \
'if `dast_on_demand_scans_scheduler` feature flag is disabled.'
field :branch, Dast::ProfileBranchType, null: true, field :branch, Dast::ProfileBranchType, null: true,
description: 'The associated branch.', description: 'The associated branch.',
calls_gitaly: true calls_gitaly: true
...@@ -33,6 +37,12 @@ module Types ...@@ -33,6 +37,12 @@ module Types
def edit_path def edit_path
Gitlab::Routing.url_helpers.edit_project_on_demand_scan_path(object.project, object) Gitlab::Routing.url_helpers.edit_project_on_demand_scan_path(object.project, object)
end end
def dast_profile_schedule
return unless Feature.enabled?(:dast_on_demand_scans_scheduler, object.project, default_enabled: :yaml)
object.dast_profile_schedule
end
end end
end end
end end
# frozen_string_literal: true
module Dast
class ProfileSchedulePolicy < BasePolicy
delegate { @subject.project }
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['DastProfileCadence'] do
include GraphqlHelpers
let_it_be(:fields) { %i[unit duration] }
specify { expect(described_class.graphql_name).to eq('DastProfileCadence') }
it { expect(described_class).to have_graphql_fields(fields) }
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe GitlabSchema.types['DastProfileSchedule'] do
include GraphqlHelpers
let_it_be(:fields) { %i[id active startsAt timezone nextRunAt cadence] }
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user, developer_projects: [project]) }
let_it_be(:object) { create(:dast_profile_schedule, project: project, owner: user) }
specify { expect(described_class.graphql_name).to eq('DastProfileSchedule') }
it { expect(described_class).to have_graphql_fields(fields) }
before do
stub_licensed_features(security_on_demand_scans: true)
end
describe 'startsAt field' do
it 'converts the startsAt to the timezone' do
expect(resolve_field(:starts_at, object, current_user: user)).to eq(object.starts_at.in_time_zone(object.timezone))
end
end
describe 'nextRunAt field' do
it 'converts the nextRunAt to the timezone' do
expect(resolve_field(:next_run_at, object, current_user: user)).to eq(object.next_run_at.in_time_zone(object.timezone))
end
end
end
...@@ -8,7 +8,7 @@ RSpec.describe GitlabSchema.types['DastProfile'] do ...@@ -8,7 +8,7 @@ RSpec.describe GitlabSchema.types['DastProfile'] do
let_it_be(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
let_it_be(:object) { create(:dast_profile, project: project) } let_it_be(:object) { create(:dast_profile, project: project) }
let_it_be(:user) { create(:user, developer_projects: [project]) } let_it_be(:user) { create(:user, developer_projects: [project]) }
let_it_be(:fields) { %i[id name description dastSiteProfile dastScannerProfile branch editPath] } let_it_be(:fields) { %i[id name description dastSiteProfile dastScannerProfile dastProfileSchedule branch editPath] }
specify { expect(described_class.graphql_name).to eq('DastProfile') } specify { expect(described_class.graphql_name).to eq('DastProfile') }
specify { expect(described_class).to require_graphql_authorizations(:read_on_demand_scans) } specify { expect(described_class).to require_graphql_authorizations(:read_on_demand_scans) }
...@@ -35,4 +35,22 @@ RSpec.describe GitlabSchema.types['DastProfile'] do ...@@ -35,4 +35,22 @@ RSpec.describe GitlabSchema.types['DastProfile'] do
expect(resolve_field(:edit_path, object, current_user: user)).to eq(expected_result) expect(resolve_field(:edit_path, object, current_user: user)).to eq(expected_result)
end end
end end
describe 'dastProfileSchedule field' do
context 'when the feature flag is enabled' do
it 'correctly resolves the field' do
expect(resolve_field(:dast_profile_schedule, object, current_user: user)).to eq(object.dast_profile_schedule)
end
end
context 'when the feature flag is not enabled' do
before do
stub_feature_flags(dast_on_demand_scans_scheduler: false)
end
it 'is nil' do
expect(resolve_field(:dast_profile_schedule, object, current_user: user)).to be_nil
end
end
end
end end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Dast::ProfileSchedulePolicy do
it_behaves_like 'a dast on-demand scan policy' do
let_it_be(:record) { create(:dast_profile_schedule, project: project) }
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