Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Project Workflow v2 #3236

Merged
merged 42 commits into from
Jun 12, 2024
Merged

Project Workflow v2 #3236

merged 42 commits into from
Jun 12, 2024

Conversation

CarsonF
Copy link
Member

@CarsonF CarsonF commented Jun 7, 2024

@CarsonF CarsonF changed the base branch from develop to finish-actor June 7, 2024 21:15
@CarsonF CarsonF force-pushed the project-workflow branch 2 times, most recently from 99f68d0 to 111673a Compare June 7, 2024 23:49
Copy link

github-actions bot commented Jun 8, 2024

🗞 GraphQL Summary

View schema changes
@@ -1608,8 +1608,28 @@
   """
   transition: ID
 }
 
+input ExecuteProjectTransitionInput {
+  """
+  Bypass the workflow, and go straight to this state.
+  `transition` is not required and ignored when using this.
+  """
+  bypassTo: ProjectStep
+
+  """Any additional user notes related to this transition"""
+  notes: RichText
+
+  """The project ID to transition"""
+  project: ID!
+
+  """
+  The transition `key` to execute.
+  This is required unless specifying bypassing the workflow with a `step` input.
+  """
+  transition: ID
+}
+
 """
 First scripture that has been created but managed _outside_ of CORD. `hasFirst` will always be true.
 """
 type ExternalFirstScripture implements FirstScripture {
@@ -2233,8 +2253,9 @@
 
   """The project members"""
   team(input: ProjectMemberListInput = {count: 25, filter: {}, order: ASC, page: 1, sort: "createdAt"}): SecuredProjectMemberList!
   type: ProjectType!
+  workflowEvents: [ProjectWorkflowEvent!]!
 }
 
 type InternshipProjectListOutput implements PaginatedList {
   """Whether the next page exists"""
@@ -2731,8 +2752,9 @@
 
   """The project members"""
   team(input: ProjectMemberListInput = {count: 25, filter: {}, order: ASC, page: 1, sort: "createdAt"}): SecuredProjectMemberList!
   type: ProjectType!
+  workflowEvents: [ProjectWorkflowEvent!]!
 }
 
 input MoveFileInput {
   """The file or directory's ID"""
@@ -2847,8 +2869,9 @@
 
   """The project members"""
   team(input: ProjectMemberListInput = {count: 25, filter: {}, order: ASC, page: 1, sort: "createdAt"}): SecuredProjectMemberList!
   type: ProjectType!
+  workflowEvents: [ProjectWorkflowEvent!]!
 }
 
 type Mutation {
   """Add a location to a language"""
@@ -3101,8 +3124,9 @@
     """
     pinned: Boolean
   ): Boolean!
   transitionProgressReport(input: ExecuteProgressReportTransitionInput!): ProgressReport!
+  transitionProject(input: ExecuteProjectTransitionInput!): Project!
 
   """Update a budget"""
   updateBudget(input: UpdateBudgetInput!): UpdateBudgetOutput!
 
@@ -4423,8 +4447,9 @@
 
   """The project members"""
   team(input: ProjectMemberListInput = {count: 25, filter: {}, order: ASC, page: 1, sort: "createdAt"}): SecuredProjectMemberList!
   type: ProjectType!
+  workflowEvents: [ProjectWorkflowEvent!]!
 }
 
 type ProjectChangeRequest implements Changeset & Resource {
   """Whether the changes have been applied to live data"""
@@ -4627,19 +4652,35 @@
   PendingReactivationApproval
   PendingRegionalDirectorApproval
   PendingSuspensionApproval
   PendingTerminationApproval
+
+  """@label Pending Field Operations Approval"""
   PendingZoneDirectorApproval
   PrepForConsultantEndorsement
   PrepForFinancialEndorsement
   Rejected
   Suspended
   Terminated
 }
 
+"""
+A transition for the project workflow.
+
+This is not a normalized entity.
+A transition represented by its `key` can have different field values
+based on the workflow's state.
+"""
 type ProjectStepTransition {
   disabled: Boolean!
   disabledReason: String
+
+  """
+  An local identifier for this transition.
+  It cannot be used to globally identify a transition.
+  It is passed to the transition mutation.
+  """
+  key: ID!
   label: String!
   to: ProjectStep!
   type: TransitionType!
 }
@@ -4672,8 +4713,19 @@
   projectTypes: [ProjectType!]!
   user: ID!
 }
 
+type ProjectWorkflowEvent {
+  at: DateTime!
+  id: ID!
+  notes: SecuredRichTextNullable!
+  to: ProjectStep!
+
+  """The transition taken, null if workflow was bypassed"""
+  transition: ProjectStepTransition
+  who: SecuredActor!
+}
+
 type Prompt {
   """Whether the requesting user can delete this resource"""
   canDelete: Boolean!
   createdAt: DateTime!
@@ -4878,8 +4930,9 @@
 
   """Look up project members"""
   projectMembers(input: ProjectMemberListInput = {count: 25, filter: {}, order: ASC, page: 1, sort: "createdAt"}): ProjectMemberListOutput! @deprecated(reason: "Query via project instead")
   projectTypeFinancialApprovers(projectTypes: [ProjectType!]): [ProjectTypeFinancialApprover!]!
+  projectWorkflow: Workflow!
 
   """Look up projects"""
   projects(input: ProjectListInput = {count: 25, filter: {}, order: ASC, page: 1, sort: "name"}): ProjectListOutput!
 
@@ -5194,8 +5247,19 @@
   canRead: Boolean!
 }
 
 """
+An object with a actor `value` and additional authorization information.
+The value is only given if `canRead` is `true` otherwise it is `null`.
+These `can*` authorization properties are specific to the user making the request.
+"""
+type SecuredActor implements Secured {
+  canEdit: Boolean!
+  canRead: Boolean!
+  value: Actor
+}
+
+"""
 An object with a boolean `value` and additional authorization information.
 The value is only given if `canRead` is `true` otherwise it is `null`.
 These `can*` authorization properties are specific to the user making the request.
 """
@@ -6167,15 +6231,15 @@
 """
 type SecuredProjectStep implements Secured {
   """
   Is the current user allowed to bypass transitions entirely
-  and change the step to any other step?
+  and change to any other state?
   """
   canBypassTransitions: Boolean!
   canEdit: Boolean!
   canRead: Boolean!
 
-  """The available steps a project can be transitioned to."""
+  """The transitions currently available to execute for this project"""
   transitions: [ProjectStepTransition!]!
   value: ProjectStep
 }
 
@@ -6657,8 +6721,9 @@
 
   """The project members"""
   team(input: ProjectMemberListInput = {count: 25, filter: {}, order: ASC, page: 1, sort: "createdAt"}): SecuredProjectMemberList!
   type: ProjectType!
+  workflowEvents: [ProjectWorkflowEvent!]!
 }
 
 type TranslationProjectListOutput implements PaginatedList {
   """Whether the next page exists"""
@@ -7558,4 +7623,69 @@
     """
     download: Boolean! = false
   ): URL!
 }
+
+type Workflow {
+  id: ID!
+  states: [WorkflowState!]!
+  transitions: [WorkflowTransition!]!
+}
+
+type WorkflowCondition {
+  label: String!
+}
+
+type WorkflowNotifier {
+  label: String!
+}
+
+type WorkflowState {
+  label: String!
+  value: String!
+}
+
+type WorkflowTransition {
+  conditions: [WorkflowCondition!]!
+  devName: String!
+  from: [WorkflowState!]!
+  key: ID!
+  label: String!
+  notifiers: [WorkflowNotifier!]!
+  permissions: [WorkflowTransitionPermission!]!
+  to: WorkflowTransitionTo!
+  type: TransitionType!
+}
+
+type WorkflowTransitionDynamicTo {
+  id: ID!
+  label: String!
+  relatedStates: [WorkflowState!]!
+}
+
+"""
+A permission for a transition.
+
+This will either have `readEvent` or `execute` as a boolean,
+specifying the action this permission defines.
+If this is true, there could still be a condition that must be met,
+described by the `condition` field.
+"""
+type WorkflowTransitionPermission {
+  """
+  The action for this permission is conditional, described by this field.
+  """
+  condition: String
+
+  """Can this role execute this transition?"""
+  execute: Boolean
+
+  """Can this role read historical events for this transition?"""
+  readEvent: Boolean
+  role: Role!
+}
+
+type WorkflowTransitionStaticTo {
+  state: WorkflowState!
+}
+
+union WorkflowTransitionTo = WorkflowTransitionDynamicTo | WorkflowTransitionStaticTo

Base automatically changed from finish-actor to develop June 10, 2024 19:47
@CarsonF CarsonF changed the title Project Workflow Project Workflow v2 Jun 11, 2024
This means that a single transition can have multiple states
based on other things (like project state).
This opens the door for `from` field & generic abstraction
This throws a better error message than "not available."
It is not that it is not available, it doesn't exist, it will never work.
This allows transitions to depend on membership, sensitivity, etc.
This will allow complex condition expressions
to be simplified based on a resolution of certain conditions within the expression.

This allows us to emit condition string for each transition
by resolving only that one condition.

i.e.
`(Member AND Transition { Approve }) OR Sens Low`
becomes
`Member OR Sens Low`

`(Member OR Transition { Approve }) AND Sens Low`
becomes
`Sens Low`
@CarsonF CarsonF marked this pull request as ready for review June 12, 2024 16:19
@CarsonF CarsonF merged commit e7c0af1 into develop Jun 12, 2024
15 checks passed
@CarsonF CarsonF deleted the project-workflow branch June 12, 2024 18:52
@atGit2021
Copy link
Contributor

@CarsonF Where did you handle the condition for internships and the requirement that project managers should not be able to approve the consultant endorsement step? I didn't see it, so wasn't sure if you added it to this PR or you were planning to add it to a different PR.

@CarsonF
Copy link
Member Author

CarsonF commented Jun 12, 2024

I may have missed it. Going to follow up in another PR with business logic changes. Much easier to discuss now that the workflow can be visualized.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants