Request Approval Example

Note: This example (AccessStateTest.cs) can be found in 'JetfireCoreExampleTests' project, which is part of the release download (V1.0.8 or higher).

In Jetfire methods are equivalent to commands and properties to information. From an application perspective it is desirable to control when commands are allowed to execute and when information can be viewed or changed.

To accomplish this Jetfire uses the 'access' and 'states' programming construct. With these constructs access modifiers can be dynamic. That is a method's or property's access modifier (whether it is private or public) can be changed programmatically.

In this example a 'Request' workflow object is created. The 'Approve' and the 'Decline' methods(command) can only be executed by a user with the role of "Approver". Once the request is approved or declined it enters the finished state. The request can not be changed once it is in the 'Finished' state as the 'Decline' and 'Approve' methods are private.
State User Guest Joe/'Approver'
Pending Approve() private public
Decline() private public
Finished Approve() private private
Decline() private private


This test builds on request approval example.
The following Jetfire code shows how to create a user programatically.
string createUserAndRoles =
  @"namespace test 
   {
      public workflow CreateUserAndRole 
      {
         Role role;
         User user;
         // constructor
         public CreateUserAndRole()
         {
            role = new Role(""Approver"");
            user = new User(""Joe"");
            user.AddRole(role);
         }
      }
   }";


The following is a very simple Request workflow class. Requests are created, which in turn can be approved. This example does not demonstrate how to 'Decline' requests. Each 'Request' is an instance of the Request workflow class.
Note: Once a "Request" workflow object is created it will remain in the system until is explicitly deleted or the database is deleted.
@"
            namespace test
            {
                // This workflow can only approve requests.
                // The next example shows a more complete solution.
               public workflow Request
               {
                  Pending(){}   // declaration of the 'pending' state.
                  Finished(){}  // declaration of the 'finished' state.
                  // Constructor
                  public Request(string requestName, string requestDetails)
                  {
                     // use the built in description properties to store extra information.
                    this.Subject = requestName;
                    this.ToolTip = requestDetails;
                    enterstate Pending();
                  }
                  string status = ""pending"";
                  // For the 'Approve' method to be public
                  // the user must have the role 'Approver'
                  // and the workflow must be in the 
                  // 'Pending' state.
                  public void Approve()
                    : access(""Approver""),
                      states(Pending)
                  {
                     status = ""approved"";
                     enterstate Finished();
                  }
                  public void Decline()
                    : access(""Approver""),
                      states(Pending)
                  {
                     status = ""declined"";
                     enterstate Finished();
                  }
                  public string Status
                  {
                     get{ return status;}
                  }                  
               }
            }
            ";    

The method "Approve" is 'public' like any other method, provided a)that the logged in user has a role named 'Approver' and b)the workflow is in the 'Pending' state..
If the user does not have this role or the workflow is not in the 'Pending state then the method will be private.
Note: A Role and User are just workflow objects in Jetfire. They have some first class attributes; however they can be treated like any other workflow.
 public void Approve()
          : access(""Approver""),
           states(Pending)
 {
       status = ""approved"";
 }


The following test code demonstrates how to execute the Jetfire create user and 'Request' workflow class from C#.
[TestMethod]
        public void AccessState_ApprovalMethod()
        {
            //
            //  Login in as 'Guest'.
            //
            LinqStartObjects start = JetfireTest.NewNexusServer(
                "MyStorage",     // A handle for the memory based object storage
                TjServerStorageType.MemoryLinq, 
                TjStorageCommand.ClearStorage  // deletes any existing objects
                );
            TjNexus nexus = start.ClientNexus;
            //
            //  Create the user 'Joe'.
            //  Parse source code and create the Jetfire executable code expressions, 
            //  get the workflow class 'CreateUserAndRole',
            //  then instantiate a new workflow object.
            //
            nexus.ParseServer(createUserAndRoles);
            TjWorkflowClass createClass = nexus.FindClass<TjWorkflowClass>(
                "test", 
                "CreateUserAndRole");
            // the constructor automatically executes
            TjWorkflow createUserAndRole = createClass.NewWorkflow(); 
            //
            // Create the 'Request' workflow class
            //
            nexus.ParseServer(requestWorkflow);
            TjWorkflowClass requestClass = nexus.FindClass<TjWorkflowClass>("test", "Request");
            TjParameter[] parameters = TjParameter.ConvertToArray(
                nexus, 
                "Day Off", 
                "Boss, I'd like to take next Tuesday off.  Hope it is okay with you?");
            TjWorkflow dayOffRequest = requestClass.NewWorkflow(parameters);
            string guid = dayOffRequest.JetfireGuid.GuidValue;
            //
            // Check to see if the workflow was created properly.
            //
            Assert.AreEqual("Day Off", dayOffRequest.Get<string>("Subject"));
            //
            //  Get the method Approve and check its access modifier.
            //  Can't self approve becuse the access modifier is private.
            //
            Assert.AreEqual(
                TjAccessModifierType.Private, 
                dayOffRequest.DynamicAccessModifier("Approve"));
            //
            // Now the 'Approve' method is private 
            // (because the Guest user doesn't have the role "Approver").  
            // The human interface can prevent
            // the 'Approve' method from being executed.   If the method is executed
            // then an exception will occur.
            //
            JetfireTest.AssertNoIncidents(nexus);
            start.Transport.Dispose();
            //
            // Logon as Joe who has the "Approve" role.
            //
            LinqStartObjects start2 = JetfireTest.NewNexusServer(
               "MyStorage",     // A handle for the memory based object storage
               TjServerStorageType.MemoryLinq,
               TjStorageCommand.ReUseExisingStorage,      
               "Joe");
            TjNexus nexus2 = start2.ClientNexus;
            Assert.AreEqual("Joe", nexus2.UserLogin.Name);
            //
            // Get the request - It will be in the cache.
            //
            TjWorkflow request = nexus2.Cache.GetUnit<TjWorkflow>(new TjGuid(guid));
            //
            //  Get the method Approve and check its access modifier.
            //
            Assert.AreEqual(TjAccessModifierType.Public, request.DynamicAccessModifier("Approve"));
            //
            //   Approve the request.
            //
            request.Execute("Approve");
            Assert.AreEqual("approved", request.Get<string>("Status"));
            //
            //  Now the Approve method is private.
            //
            Assert.AreEqual(TjAccessModifierType.Private, request.DynamicAccessModifier("Approve"));

            JetfireTest.AssertNoIncidents(nexus2);
            start2.Transport.Dispose();
        }

Last edited Feb 21, 2009 at 9:29 PM by JohnHansen, version 9

Comments

No comments yet.