Add GraphQL API for upcoming scheduled PEP runs

What does this MR do and why?

Add a new upcomingSchedules field to PipelineExecutionSchedulePolicy GraphQL type that exposes upcoming scheduled runs for pipeline execution schedule policies.

This allows users to see when their scheduled policies will next trigger pipelines across projects, providing visibility into scheduled pipeline execution before building UI.

Changes

  • Add PipelineExecutionProjectScheduleType GraphQL type
  • Add UpcomingPolicySchedulesResolver with BatchLoader for N+1 prevention
  • Add PipelineExecutionProjectSchedulePolicy for authorization
  • Add preload_policy_and_project_route scope to avoid N+1 queries
  • Add upcomingSchedules field to PipelineExecutionSchedulePolicyType

The API returns schedule data including next_run_at, cron expression, timezone, time window, and snooze status. Authorization follows existing patterns - users need push_code permission on the policy management project.

Changelog: added EE: true

References

Screenshots or screen recordings

Example GraphQL query:

query {
  project(fullPath: "root/test-upcoming-schedules") {
    pipelineExecutionSchedulePolicies {
      nodes {
        name
        upcomingSchedules(first: 10) {
          nodes {
            id
            project {
              fullPath
            }
            nextRunAt
            cron
            cronTimezone
            timeWindowSeconds
            snoozedUntil
          }
        }
      }
    }
  }
}

Example response:

{
  "data": {
    "project": {
      "pipelineExecutionSchedulePolicies": {
        "nodes": [
          {
            "name": "test-schedule-policy",
            "upcomingSchedules": {
              "nodes": [
                {
                  "id": "gid://gitlab/Security::PipelineExecutionProjectSchedule/27",
                  "project": {
                    "fullPath": "root/test-upcoming-schedules"
                  },
                  "nextRunAt": "2026-05-29T09:01:56Z",
                  "cron": "0 8 * * *",
                  "cronTimezone": "UTC",
                  "timeWindowSeconds": 14400,
                  "snoozedUntil": null
                }
              ]
            }
          }
        ]
      }
    }
  }
}

Database

Query

The resolver generates the following query:

SELECT "security_pipeline_execution_project_schedules".*
FROM "security_pipeline_execution_project_schedules"
WHERE "security_pipeline_execution_project_schedules"."security_policy_id" = 2351295
ORDER BY "security_pipeline_execution_project_schedules"."next_run_at" ASC,
         "security_pipeline_execution_project_schedules"."id" ASC

Query plan

Query plan on postgres.ai

Sort  (cost=6.35..6.36 rows=3 width=89) (actual time=3.271..3.272 rows=2 loops=1)
   Sort Key: security_pipeline_execution_project_schedules.next_run_at, security_pipeline_execution_project_schedules.id
   Sort Method: quicksort  Memory: 25kB
   Buffers: shared hit=9 read=4 dirtied=2
   WAL: records=2 fpi=2 bytes=16226
   ->  Index Scan using idx_pipeline_execution_schedules_security_policy_id_and_id on public.security_pipeline_execution_project_schedules  (cost=0.28..6.33 rows=3 width=89) (actual time=2.701..3.226 rows=2 loops=1)
         Index Cond: (security_pipeline_execution_project_schedules.security_policy_id = 2351295)
         Buffers: shared hit=3 read=4 dirtied=2
         WAL: records=2 fpi=2 bytes=16226

Existing indexes

The table already has appropriate indexes:

  • idx_pipeline_execution_schedules_security_policy_id_and_id on (security_policy_id, id) - used by this query
  • idx_security_pipeline_execution_project_schedules_next_run_at on (next_run_at, id)

No new indexes required.

How to set up and validate locally

  1. Create a new group.

  2. Create a project in the group to serve as the security policy project.

  3. Add a CI config file policy-ci.yml:

    test-job:
      script:
        - echo "Scheduled policy job running"
  4. Add a .gitlab/security-policies/policy.yml file (replace <group-path>/<policy-project-path> with the full path to your policy project):

    ---
    pipeline_execution_schedule_policy:
      - name: test-schedule-policy
        description: 'Test scheduled policy'
        enabled: true
        content:
          include:
            - project: '<group-path>/<policy-project-path>'
              file: policy-ci.yml
        schedules:
          - type: daily
            start_time: '10:00'
            time_window:
              value: 600
              distribution: random
  5. Enable Pipeline execution policies on the policy project:

    • Go to Settings > General > Visibility, project features, permissions
    • Enable Pipeline execution policies
    • Save changes
  6. Go to the group Secure > Policies and link the policy project via Edit policy project.

  7. Create another project in the group as the target project.

  8. Wait for the policy to sync (this automatically creates the schedule record).

  9. Query the GraphQL API at /-/graphql-explorer using the example query above (replace the project path with your target project's full path).

  10. Verify the response includes the schedule with nextRunAt, cron, cronTimezone, and other fields.

  11. Verify authorization by querying as a user without push_code access to the policy project - the upcomingSchedules should return empty nodes (the policy itself is visible but schedule details are filtered).

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Related to #600939 (closed)

Edited by Andy Schoenen

Merge request reports

Loading
Loading