Skip to content

WIP: Expose a preview API for rotation params

Sarah Yasonik requested to merge sy-preview-rotation-api into master

What does this MR do?

Implements a mutation to generate a preview of the shifts for a rotation.

This is for #259172, which will allow users to visualize the rotation that they are creating/updating before saving it.

Changes in this MR:

  • Add new mutation for previewing shifts
  • Change nullable option for OncallRotation and OncallParticipant GraphQL types*
  • Refactor OncallRotation::Base mutation to better reflect shared functionality
  • Aligns fields required mutation arguments for destroying/editing rotations
  • Updates the OncallShifts::ReadService to accept an unsaved rotation & use that as an indicator not to attempt to pull historical shifts from the DB

The mutation is meant to be essentially identical to the OncallRotationCreate mutation, including nearly all the same validations.

Differences between creation mutation:

  • The rotation name will not be validated for uniqueness.
  • Participants are not individually validated. Some checks take place (permissions, duplicates, quantity), but data visualization options are not checked.
  • Rotation and participant ids will not be available, as the rotation has not been saved.
Sample GraphQL details for new mutation

Sample mutation:

mutation PreviewRotationEMEA($rotationPreviewEMEA: OncallRotationPreviewInput!) {
  oncallRotationPreview(input: $rotationPreviewEMEA) {
    oncallRotation {
      id
      name
      startsAt
      length
      lengthUnit
      activePeriod {
        endTime
        startTime
      }
      shifts(startTime: "2021-03-14 17:23:13 -0500" , endTime: "2021-04-08 17:23:13 -0500") {
        nodes {
          endsAt
          startsAt
          participant {
            id
            user {
              username
            }
          }
        }
      }
    }
    errors
  }
}
{
  "rotationPreviewEMEA": {
    "name": "EMEA",
    "projectPath": "h5bp/html5-boilerplate",
    "scheduleIid": "1",
    "startsAt": {
      "date": "2021-02-01",
      "time": "08:00"
    },
    "rotationLength": {
      "length": 12,
      "unit": "HOURS"
    },
    "participants": [
      { "username": "colton", "colorPalette": "BLUE", "colorWeight": "WEIGHT_200" }, 
      { "username": "pauletta_paucek", "colorPalette": "AQUA", "colorWeight": "WEIGHT_400" },
      { "username": "nubia.hartmann", "colorPalette": "GREEN", "colorWeight": "WEIGHT_700" },
      { "username": "juliane.labadie", "colorPalette": "ORANGE", "colorWeight": "WEIGHT_900" }
    ]
  }
}

Sample output:

{
  "data": {
    "oncallRotationPreview": {
      "oncallRotation": {
        "id": null,
        "name": "GitLab Preview 1564768962608780951",
        "startsAt": "2021-02-01T08:00:00Z",
        "length": 12,
        "lengthUnit": "HOURS",
        "activePeriod": {
          "endTime": null,
          "startTime": null
        },
        "shifts": {
          "nodes": [
            {
              "endsAt": "2021-03-15T08:00:00Z",
              "startsAt": "2021-03-14T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-03-15T20:00:00Z",
              "startsAt": "2021-03-15T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-03-16T08:00:00Z",
              "startsAt": "2021-03-15T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            },
            {
              "endsAt": "2021-03-16T20:00:00Z",
              "startsAt": "2021-03-16T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "nubia.hartmann"
                }
              }
            },
            {
              "endsAt": "2021-03-17T08:00:00Z",
              "startsAt": "2021-03-16T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-03-17T20:00:00Z",
              "startsAt": "2021-03-17T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-03-18T08:00:00Z",
              "startsAt": "2021-03-17T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            },
            {
              "endsAt": "2021-03-18T20:00:00Z",
              "startsAt": "2021-03-18T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "nubia.hartmann"
                }
              }
            },
            {
              "endsAt": "2021-03-19T08:00:00Z",
              "startsAt": "2021-03-18T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-03-19T20:00:00Z",
              "startsAt": "2021-03-19T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-03-20T08:00:00Z",
              "startsAt": "2021-03-19T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            },
            {
              "endsAt": "2021-03-20T20:00:00Z",
              "startsAt": "2021-03-20T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "nubia.hartmann"
                }
              }
            },
            {
              "endsAt": "2021-03-21T08:00:00Z",
              "startsAt": "2021-03-20T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-03-21T20:00:00Z",
              "startsAt": "2021-03-21T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-03-22T08:00:00Z",
              "startsAt": "2021-03-21T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            },
            {
              "endsAt": "2021-03-22T20:00:00Z",
              "startsAt": "2021-03-22T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "nubia.hartmann"
                }
              }
            },
            {
              "endsAt": "2021-03-23T08:00:00Z",
              "startsAt": "2021-03-22T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-03-23T20:00:00Z",
              "startsAt": "2021-03-23T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-03-24T08:00:00Z",
              "startsAt": "2021-03-23T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            },
            {
              "endsAt": "2021-03-24T20:00:00Z",
              "startsAt": "2021-03-24T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "nubia.hartmann"
                }
              }
            },
            {
              "endsAt": "2021-03-25T08:00:00Z",
              "startsAt": "2021-03-24T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-03-25T20:00:00Z",
              "startsAt": "2021-03-25T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-03-26T08:00:00Z",
              "startsAt": "2021-03-25T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            },
            {
              "endsAt": "2021-03-26T20:00:00Z",
              "startsAt": "2021-03-26T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "nubia.hartmann"
                }
              }
            },
            {
              "endsAt": "2021-03-27T08:00:00Z",
              "startsAt": "2021-03-26T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-03-27T20:00:00Z",
              "startsAt": "2021-03-27T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-03-28T08:00:00Z",
              "startsAt": "2021-03-27T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            },
            {
              "endsAt": "2021-03-28T20:00:00Z",
              "startsAt": "2021-03-28T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "nubia.hartmann"
                }
              }
            },
            {
              "endsAt": "2021-03-29T08:00:00Z",
              "startsAt": "2021-03-28T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-03-29T20:00:00Z",
              "startsAt": "2021-03-29T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-03-30T08:00:00Z",
              "startsAt": "2021-03-29T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            },
            {
              "endsAt": "2021-03-30T20:00:00Z",
              "startsAt": "2021-03-30T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "nubia.hartmann"
                }
              }
            },
            {
              "endsAt": "2021-03-31T08:00:00Z",
              "startsAt": "2021-03-30T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-03-31T20:00:00Z",
              "startsAt": "2021-03-31T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-04-01T08:00:00Z",
              "startsAt": "2021-03-31T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            },
            {
              "endsAt": "2021-04-01T20:00:00Z",
              "startsAt": "2021-04-01T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "nubia.hartmann"
                }
              }
            },
            {
              "endsAt": "2021-04-02T08:00:00Z",
              "startsAt": "2021-04-01T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-04-02T20:00:00Z",
              "startsAt": "2021-04-02T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-04-03T08:00:00Z",
              "startsAt": "2021-04-02T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            },
            {
              "endsAt": "2021-04-03T20:00:00Z",
              "startsAt": "2021-04-03T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "nubia.hartmann"
                }
              }
            },
            {
              "endsAt": "2021-04-04T08:00:00Z",
              "startsAt": "2021-04-03T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-04-04T20:00:00Z",
              "startsAt": "2021-04-04T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-04-05T08:00:00Z",
              "startsAt": "2021-04-04T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            },
            {
              "endsAt": "2021-04-05T20:00:00Z",
              "startsAt": "2021-04-05T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "nubia.hartmann"
                }
              }
            },
            {
              "endsAt": "2021-04-06T08:00:00Z",
              "startsAt": "2021-04-05T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-04-06T20:00:00Z",
              "startsAt": "2021-04-06T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-04-07T08:00:00Z",
              "startsAt": "2021-04-06T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            },
            {
              "endsAt": "2021-04-07T20:00:00Z",
              "startsAt": "2021-04-07T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "nubia.hartmann"
                }
              }
            },
            {
              "endsAt": "2021-04-08T08:00:00Z",
              "startsAt": "2021-04-07T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "juliane.labadie"
                }
              }
            },
            {
              "endsAt": "2021-04-08T20:00:00Z",
              "startsAt": "2021-04-08T08:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "colton"
                }
              }
            },
            {
              "endsAt": "2021-04-09T08:00:00Z",
              "startsAt": "2021-04-08T20:00:00Z",
              "participant": {
                "id": null,
                "user": {
                  "username": "pauletta_paucek"
                }
              }
            }
          ]
        }
      },
      "errors": []
    }
  }
}

*Noteworthy:

  • This MR swaps two fields from non-nullable to nullable, which technically constitutes a breaking change. However, Oncall Schedules has been feature-flagged through %13.11. So this GraphQL change will need to be merged within %13.11 in order to avoid the 6-release-long deprecation cycle.

Does this MR meet the acceptance criteria?

Conformity

Availability and Testing

Security

If this MR contains changes to processing or storing of credentials or tokens, authorization and authentication methods and other items described in the security review guidelines:

  • Label as security and @ mention @gitlab-com/gl-security/appsec
  • The MR includes necessary changes to maintain consistency between UI, API, email, or other methods
  • Security reports checked/validated by a reviewer from the AppSec team
Edited by Sarah Yasonik

Merge request reports