private Status GenerateSuggestions(WorkflowInstance workflowInstance, ServerEntity entity, Dictionary<string, string> suggestionList) { Item item = entity as Item; if (item == null) { TraceLog.TraceError("Entity is not an Item"); return Status.Error; } try { BingSearch bingSearch = new BingSearch(); // retrieve and format the search template, or if one doesn't exist, use $(Intent) string searchTemplate = null; if (InputParameters.TryGetValue(ActivityParameters.SearchTemplate, out searchTemplate) == false) searchTemplate = String.Format("$({0})", ActivityVariables.Intent); string query = ExpandVariables(workflowInstance, searchTemplate); if (String.IsNullOrWhiteSpace(query)) { TraceLog.TraceInfo("No query to issue Bing"); return Status.Complete; } // make a synchronous webservice call to bing // // Async has the problem that the caller of this method assumes that a // populated suggestionList will come out. If it doesn't, the state will execute // again and trigger a fresh set of suggestions to be generated. Eventually all // queries will return and populate the suggestions DB with duplicate data. // This can be fixed once we move to a "real" workflow system such as WF. var results = bingSearch.Query(query); foreach (var r in results) { WebResult result = r as WebResult; if (result != null) suggestionList[result.Title] = result.Url; } } catch (Exception ex) { TraceLog.TraceException("Bing query failed", ex); return Status.Error; } // this activity is typically last and once links have been generated, no need // to keep the workflow instance around return Status.Complete; }
private Status GenerateSuggestions(WorkflowInstance workflowInstance, ServerEntity entity, Dictionary<string, string> suggestionList) { Item item = entity as Item; if (item == null) { TraceLog.TraceError("Entity is not an Item"); return Status.Error; } // TODO: get subject attributes from the Contacts folder, Facebook, and Cloud AD // these will hang off of the contact as well as in the workflow InstanceData // inexact match return Status.Pending; }
private Status GenerateSuggestions(WorkflowInstance workflowInstance, ServerEntity entity, Dictionary<string, string> suggestionList) { Item item = entity as Item; if (item == null) { TraceLog.TraceError("GenerateSuggestions: non-Item passed in"); return Status.Error; } // HACK: hardcode names for now until the graph queries are in place foreach (var like in "Golf;Seattle Sounders;Malcolm Gladwell".Split(';')) { suggestionList[like] = like; } return Status.Pending; }
private Status GenerateSuggestions(WorkflowInstance workflowInstance, ServerEntity entity, Dictionary<string, string> suggestionList) { Item item = entity as Item; if (item == null) { TraceLog.TraceError("Entity is not an Item"); return Status.Complete; } // HACK: hardcode names for now until the graph queries are in place foreach (var subject in "Mike Maples;Mike Smith;Mike Abbott".Split(';')) { Item contact = MakeContact(workflowInstance, item, subject); suggestionList[subject] = JsonSerializer.Serialize(contact); } // inexact match return Status.Pending; }
private Status GenerateSuggestions(WorkflowInstance workflowInstance, ServerEntity entity, Dictionary<string, string> suggestionList) { Item item = entity as Item; if (item == null) { TraceLog.TraceError("Entity is not an Item"); return Status.Error; } // if an intent was already matched, return it now var intentFV = item.GetFieldValue(ExtendedFieldNames.Intent); if (intentFV != null && !String.IsNullOrWhiteSpace(intentFV.Value)) { var wt = SuggestionsContext.WorkflowTypes.FirstOrDefault(t => t.Type == intentFV.Value); if (wt != null) { suggestionList[wt.Type] = wt.Type; return Status.Complete; } } // run NLP engine over the task name to extract intent (verb / noun) as well as a subject hint string name = item.Name; string verb = null; string noun = null; string subject = null; try { Phrase phrase = new Phrase(name); if (phrase.Task != null) { verb = phrase.Task.Verb.ToLower(); noun = phrase.Task.Article.ToLower(); subject = phrase.Task.Subject; if (!String.IsNullOrWhiteSpace(subject)) StoreInstanceData(workflowInstance, ActivityVariables.SubjectHint, subject); } } catch (Exception ex) { TraceLog.TraceException("Could not initialize NLP engine", ex); } // if NLP failed (e.g. data file not found), do "poor man's NLP" - assume a structure like <verb> <noun> [{for,with} <subject>] if (verb == null) { string sentence = name.ToLower(); // remove extra whitespace and filler words StringBuilder sb = new StringBuilder(); foreach (var word in sentence.Split(new char[] { ' ', '\n', '\t' }, StringSplitOptions.RemoveEmptyEntries)) { bool add = true; foreach (var filler in "a;an;the".Split(';')) if (word == filler) { add = false; break; } if (add) sb.AppendFormat("{0} ", word); } sentence = sb.ToString().Trim(); // poor man's NLP - assume first word is verb, second word is noun string[] parts = sentence.Split(' '); if (parts.Length >= 2) { verb = parts[0]; noun = parts[1]; } if (parts.Length >= 4) { if (parts[2] == "for" || parts[2] == "with") { // capitalize and store the word following "for" in the SubjectHint workflow parameter subject = parts[3]; subject = subject.Substring(0, 1).ToUpper() + subject.Substring(1); StoreInstanceData(workflowInstance, ActivityVariables.SubjectHint, subject); } } } // try to find an intent that exactly matches the verb/noun extracted by the NLP step Intent intent = SuggestionsContext.Intents.FirstOrDefault(i => i.Verb == verb && i.Noun == noun); if (intent != null) { var wt = SuggestionsContext.WorkflowTypes.FirstOrDefault(t => t.Type == intent.WorkflowType); if (wt != null) { suggestionList[intent.WorkflowType] = wt.Type; return Status.Complete; } } // get a list of all approximate matches and surface as suggestions to the user var intentList = SuggestionsContext.Intents.Where(i => i.Verb == verb || i.Noun == noun); foreach (var i in intentList) suggestionList[i.WorkflowType] = i.WorkflowType; // if there are no suggestions, we can terminate this workflow if (suggestionList.Count == 0) { TraceLog.TraceError("No possible intents were found, terminating workflow"); return Status.Error; } return Status.Pending; }
private Status GenerateSuggestions(WorkflowInstance workflowInstance, ServerEntity entity, Dictionary<string, string> suggestionList) { Item item = entity as Item; if (item == null) { TraceLog.TraceError("Entity is not an Item"); return Status.Error; } // make sure the subject was identified - if not move the state forward string subjectItem = GetInstanceData(workflowInstance, ActivityVariables.Contact); if (subjectItem == null) return Status.Complete; // set up the FB API context FBGraphAPI fbApi = new FBGraphAPI(); // get the current user User user = UserContext.GetUser(item.UserID, true); if (user == null) { TraceLog.TraceError("Could not find the user associated with item " + item.Name); return Status.Error; } UserCredential cred = user.GetCredential(UserCredential.FacebookConsent); if (cred != null && cred.AccessToken != null) { fbApi.AccessToken = cred.AccessToken; } else { // user not having a FB token is not an error condition, but there is no way to generate suggestions // just move forward from this state return Status.Complete; } Item subject = null; try { subject = JsonSerializer.Deserialize<Item>(subjectItem); // if the subjectItem is a reference, chase it down while (subject.ItemTypeID == SystemItemTypes.Reference) { FieldValue refID = subject.GetFieldValue(FieldNames.EntityRef); Guid refid = new Guid(refID.Value); subject = UserContext.Items.Include("FieldValues").Single(i => i.ID == refid); } } catch (Exception ex) { TraceLog.TraceException("Could not deserialize subject Item", ex); return Status.Error; } FieldValue fbID = subject.GetFieldValue(FieldNames.FacebookID); if (fbID == null || fbID.Value == null) { TraceLog.TraceError(String.Format("Could not find FacebookID for Contact {0}", subject.Name)); return Status.Complete; } try { // issue the query against the Facebook Graph API var results = fbApi.Query(fbID.Value, FBQueries.Likes); foreach (var like in results) { string name = (string) like[FBQueryResult.Name]; suggestionList[name] = name; } } catch (Exception ex) { TraceLog.TraceException("Error calling Facebook Graph API", ex); return Status.Complete; } return Status.Complete; }
private Status GenerateSuggestions(WorkflowInstance workflowInstance, ServerEntity entity, Dictionary<string, string> suggestionList) { Item item = entity as Item; if (item == null) { TraceLog.TraceError("Entity is not an Item"); return Status.Error; } User user = UserContext.GetUser(item.UserID, true); if (user == null) { TraceLog.TraceError("Could not find the user associated with Item " + item.Name); return Status.Error; } ADGraphAPI adApi = new ADGraphAPI(); string adRefreshToken = null; // check for FB and/or AD credentials UserCredential cred = user.GetCredential(UserCredential.FacebookConsent); if (cred != null && cred.AccessToken != null) { adApi.FacebookAccessToken = cred.AccessToken; } cred = user.GetCredential(UserCredential.CloudADConsent); if (cred != null && cred.RenewalToken != null) { adRefreshToken = cred.RenewalToken; } if (adApi.FacebookAccessToken == null && adRefreshToken == null) { // user not having either token is not an error condition, but there is no way to generate suggestions // just move forward from this state return Status.Complete; } // if a refresh token exists for AD, get an access token from Azure ACS for the Azure AD service if (adRefreshToken != null) { try { AccessTokenRequestWithRefreshToken request = new AccessTokenRequestWithRefreshToken(new Uri(AzureOAuthConfiguration.GetTokenUri())) { RefreshToken = adRefreshToken, ClientId = AzureOAuthConfiguration.GetClientIdentity(), ClientSecret = AzureOAuthConfiguration.ClientSecret, Scope = AzureOAuthConfiguration.RelyingPartyRealm, }; OAuthMessage message = OAuthClient.GetAccessToken(request); AccessTokenResponse authzResponse = message as AccessTokenResponse; adApi.ADAccessToken = authzResponse.AccessToken; // workaround for ACS trashing the refresh token if (!String.IsNullOrEmpty(authzResponse.RefreshToken)) { TraceLog.TraceInfo("Storing new CloudAD refresh token"); user.AddCredential(UserCredential.CloudADConsent, authzResponse.AccessToken, null, authzResponse.RefreshToken); UserContext.SaveChanges(); } } catch (Exception ex) { TraceLog.TraceException("Could not contact ACS to get an access token", ex); // Facebook credentials are not available if (adApi.FacebookAccessToken == null) return Status.Pending; // could be a temporary outage, do not move off this state } } // extract a subject hint if one hasn't been discovered yet string subjectHint = GetInstanceData(workflowInstance, ActivityVariables.SubjectHint); if (String.IsNullOrEmpty(subjectHint)) { try { Phrase phrase = new Phrase(item.Name); if (phrase.Task != null) { subjectHint = phrase.Task.Subject; if (!String.IsNullOrWhiteSpace(subjectHint)) StoreInstanceData(workflowInstance, ActivityVariables.SubjectHint, subjectHint); } } catch (Exception ex) { TraceLog.TraceException("Could not initialize NLP engine", ex); } } // get contacts from Cloud AD and Facebook via the AD Graph Person service // TODO: also get local contacts from the Contacts folder try { var results = adApi.Query(subjectHint ?? ""); foreach (var subject in results) { // serialize an existing contact corresponding to the subject, // or generate a new serialized contact if one wasn't found Item contact = MakeContact(workflowInstance, item, subject); suggestionList[contact.Name] = JsonSerializer.Serialize(contact); } } catch (Exception ex) { TraceLog.TraceException("Could not contact Person Service", ex); return Status.Error; } // inexact match return Status.Pending; }
/// <summary> /// This method will add a Suggestion with a RefreshEntity FieldName and State to the Suggestions /// for this ServerEntity. By convention, this will tell the UI to refresh the Entity. This method /// is called when an Activity changes the Item (e.g. a DueDate is parsed out of the Name, a Contacts /// FieldName is created, etc). /// </summary> /// <param name="workflowInstance"></param> /// <param name="entity"></param> protected void SignalEntityRefresh(WorkflowInstance workflowInstance, ServerEntity entity) { var sugg = new Suggestion() { ID = Guid.NewGuid(), EntityID = entity.ID, EntityType = entity.GetType().Name, WorkflowType = workflowInstance.WorkflowType, WorkflowInstanceID = workflowInstance.ID, State = SuggestionTypes.RefreshEntity, SuggestionType = SuggestionTypes.RefreshEntity, DisplayName = SuggestionTypes.RefreshEntity, GroupDisplayName = SuggestionTypes.RefreshEntity, Value = null, TimeSelected = null }; SuggestionsContext.Suggestions.Add(sugg); SuggestionsContext.SaveChanges(); }
// get or create an reference to the given entity in the UserFolder EntityRefs list public Item GetEntityRef(User user, ServerEntity entity) { Item entityRefsList = GetEntityRefsList(user); if (entityRefsList == null) return null; var entityID = entity.ID.ToString(); try { // get existing reference to given entity if (storage.Items.Include("FieldValues").Any(i => i.UserID == user.ID && i.FolderID == entityRefsList.FolderID && i.ParentID == entityRefsList.ID && i.FieldValues.Any(fv => fv.FieldName == FieldNames.EntityRef && fv.Value == entityID))) { return storage.Items.Include("FieldValues").Single(i => i.UserID == user.ID && i.FolderID == entityRefsList.FolderID && i.ParentID == entityRefsList.ID && i.FieldValues.Any(fv => fv.FieldName == FieldNames.EntityRef && fv.Value == entityID)); } else { // create new reference to given entity DateTime now = DateTime.UtcNow; var entityRefItemID = Guid.NewGuid(); var entityRefItem = new Item() { ID = entityRefItemID, Name = entity.Name, FolderID = entityRefsList.FolderID, UserID = user.ID, ItemTypeID = SystemItemTypes.Reference, ParentID = entityRefsList.ID, Created = now, LastModified = now, FieldValues = new List<FieldValue>() { new FieldValue() { ItemID = entityRefItemID, FieldName = FieldNames.EntityRef, Value = entityID }, new FieldValue() { ItemID = entityRefItemID, FieldName = FieldNames.EntityType, Value = entity.GetType().Name }, } }; storage.Items.Add(entityRefItem); storage.SaveChanges(); TraceLog.TraceInfo(String.Format("Created entity ref item {0} for user {1}", entity.Name, user.Name)); return entityRefItem; } } catch (Exception ex) { TraceLog.TraceException(String.Format("Created entity ref item {0} for user {1}", entity.Name, user.Name), ex); return null; } }
/// <summary> /// Create a set of suggestions from the results of calling the suggestion function /// </summary> /// <param name="workflowInstance">Workflow instance to operate over</param> /// <param name="item">Item to process</param> /// <param name="suggestionFunction">Suggestion generation function</param> /// <returns>Complete if an exact match was found, Pending if multiple suggestions created</returns> protected Status CreateSuggestions( WorkflowInstance workflowInstance, ServerEntity entity, Func<WorkflowInstance, ServerEntity, Dictionary<string,string>, Status> suggestionFunction) { // analyze the item for possible suggestions var suggestions = new Dictionary<string, string>(); Status status = suggestionFunction.Invoke(workflowInstance, entity, suggestions); TraceLog.TraceDetail(String.Format("Retrieved {0} suggestions from activity {1}", suggestions.Count, Name)); // if the function completed with an error, or without generating any data, return (this is typically a fail-fast state) if (status == Status.Error || suggestions.Count == 0) return status; // if an "exact match" was discovered without user input, store it now and return if (status == Status.Complete && suggestions.Count == 1) { string s = null; foreach (var value in suggestions.Values) s = value; StoreInstanceData(workflowInstance, ActivityVariables.LastStateData, s); StoreInstanceData(workflowInstance, OutputParameterName, s); TraceLog.TraceDetail(String.Format("Exact match {0} was found for activity {1}", s, Name)); return status; } // construct the group display name string groupDisplayName = GroupDisplayName; if (groupDisplayName == null) groupDisplayName = workflowInstance.State; else groupDisplayName = FormatStringTemplate(workflowInstance, groupDisplayName); // get the suggestion parent ID if available var parentID = GetInstanceData(workflowInstance, ActivityVariables.ParentID); // add suggestions received from the suggestion function try { int num = 0; foreach (var s in suggestions.Keys) { // limit to four suggestions if (num++ == 4) break; var sugg = new Suggestion() { ID = Guid.NewGuid(), ParentID = parentID == null ? (Guid?) null : new Guid(parentID), EntityID = entity.ID, EntityType = entity.GetType().Name, WorkflowType = workflowInstance.WorkflowType, WorkflowInstanceID = workflowInstance.ID, State = workflowInstance.State, SuggestionType = SuggestionType, DisplayName = s, GroupDisplayName = groupDisplayName, SortOrder = num, Value = suggestions[s], TimeSelected = null }; SuggestionsContext.Suggestions.Add(sugg); TraceLog.TraceDetail(String.Format("Created suggestion {0} in group {1} for activity {2}", s, groupDisplayName, Name)); } SuggestionsContext.SaveChanges(); return status; } catch (Exception ex) { TraceLog.TraceException("Activity execution failed", ex); return Status.Error; } }
/// <summary> /// Canned Execution method for an Activity for processing Items. This method will: /// 1. validate the entity as an Item /// 2. verify the item type /// 3. check whether the target field is set on the Item and set state appropriately /// 4. check whether the user made a selection (via the data parameter) and set state appropriately /// 5. if none of this is true, add a set of suggestions from a suggestion function passed in /// </summary> /// <param name="workflowInstance">Workflow instance to process</param> /// <param name="entity">Entity to process</param> /// <param name="data">User selection data passed in</param> /// <param name="suggestionFunction">Suggestion function to invoke</param> /// <returns>return value for the Function</returns> protected Status Execute( WorkflowInstance workflowInstance, ServerEntity entity, object data, Guid expectedItemType, Func<WorkflowInstance, ServerEntity, Dictionary<string, string>, Status> suggestionFunction) { Item item = entity as Item; if (item == null) { TraceLog.TraceError("Entity is not an Item"); return Status.Error; } if (VerifyItemType(item, expectedItemType) == false) return Status.Error; // if the target field has been set, this state can terminate if (CheckTargetField(workflowInstance, item)) return Status.Complete; // check for user selection if (data != null) return ProcessActivityData(workflowInstance, data); return CreateSuggestions(workflowInstance, entity, suggestionFunction); }
/// <summary> /// Execute the workflow instances associated with this entity /// </summary> /// <param name="entity"></param> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <returns>true if processing happened (and the operation doesn't need to be processed again), /// false if one the workflow instances was locked (causes the message to be reprocessed)</returns> public static bool ExecuteWorkflows(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, ServerEntity entity) { if (entity == null) return true; List<WorkflowInstance> wis = null; try { // get all the workflow instances for this Item wis = suggestionsContext.WorkflowInstances.Where(w => w.EntityID == entity.ID).ToList(); if (wis.Count > 0) { // if the instance is locked by someone else, stop processing // otherwise lock each of the workflow instances foreach (var instance in wis) { if (instance.LockedBy != null && instance.LockedBy != Me) return false; instance.LockedBy = Me; } suggestionsContext.SaveChanges(); // reacquire the lock list and verify they were all locked by Me (if not, stop processing) // projecting locks and not workflow instances to ensure that the database's lock values are returned (not from EF's cache) var locks = suggestionsContext.WorkflowInstances.Where(w => w.EntityID == entity.ID).Select(w => w.LockedBy).ToList(); foreach (var lockedby in locks) if (lockedby != Me) return false; // loop over the workflow instances and dispatch the new message foreach (var instance in wis) { Workflow workflow = null; try { var wt = suggestionsContext.WorkflowTypes.Single(t => t.Type == instance.WorkflowType); workflow = JsonSerializer.Deserialize<Workflow>(wt.Definition); } catch (Exception ex) { TraceLog.TraceException("Could not find or deserialize workflow definition", ex); continue; } // set the database contexts workflow.UserContext = userContext; workflow.SuggestionsContext = suggestionsContext; // invoke the workflow and process steps until workflow is blocked for user input or is done workflow.Run(instance, entity); } } return true; } catch (Exception ex) { TraceLog.TraceException("ExecuteWorkflows failed", ex); return true; } finally { // find and unlock all remaining workflow instances that relate to this entity // note that a new context is used for this - to avoid caching problems where the current thread // believes it is the owner but the database says otherwise. var context = Storage.NewSuggestionsContext; wis = context.WorkflowInstances.Where(w => w.EntityID == entity.ID).ToList(); if (wis.Count > 0) { // unlock each of the workflow instances foreach (var instance in wis) if (instance.LockedBy == Me) instance.LockedBy = null; context.SaveChanges(); } } }
/// <summary> /// Start a workflow of a certain type, passing it an entity and some instance data to start /// </summary> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <param name="type">String representing the workflow type</param> /// <param name="entity">Entity to associate with the workflow</param> /// <param name="instanceData">Instance data to pass into the workflow</param> public static void StartWorkflow(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, string type, ServerEntity entity, string instanceData) { WorkflowInstance instance = null; try { Workflow workflow = null; // get the workflow definition out of the database try { var wt = suggestionsContext.WorkflowTypes.Single(t => t.Type == type); workflow = JsonSerializer.Deserialize<Workflow>(wt.Definition); } catch (Exception ex) { TraceLog.TraceException("Could not find or deserialize workflow definition", ex); return; } // don't start a workflow with no states if (workflow.States.Count == 0) return; // store the database contexts workflow.UserContext = userContext; workflow.SuggestionsContext = suggestionsContext; // create the new workflow instance and store in the workflow DB DateTime now = DateTime.Now; instance = new WorkflowInstance() { ID = Guid.NewGuid(), EntityID = entity.ID, EntityName = entity.Name, WorkflowType = type, State = workflow.States[0].Name, InstanceData = instanceData ?? "{}", Created = now, LastModified = now, LockedBy = WorkflowHost.Me, }; suggestionsContext.WorkflowInstances.Add(instance); suggestionsContext.SaveChanges(); TraceLog.TraceInfo("Starting workflow " + type); // invoke the workflow and process steps until workflow is blocked for user input or is done workflow.Run(instance, entity); // unlock the workflowinstance instance.LockedBy = null; suggestionsContext.SaveChanges(); } catch (Exception ex) { TraceLog.TraceException("StartWorkflow failed", ex); if (instance != null && instance.LockedBy == WorkflowHost.Me) { // unlock the workflowinstance instance.LockedBy = null; suggestionsContext.SaveChanges(); } } }
/// <summary> /// Start workflows associated with a change in one or more of the entity's fields /// </summary> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <param name="entity"></param> /// <param name="oldEntity"></param> public static void StartTriggerWorkflows(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, ServerEntity entity, ServerEntity oldEntity) { if (entity == null || oldEntity == null) return; // only Item property triggers are supported at this time Item item = entity as Item; Item oldItem = oldEntity as Item; if (item != null) { // go through field by field, and if a field has changed, trigger the appropriate workflow ItemType itemType = userContext.ItemTypes.Include("Fields").Single(it => it.ID == item.ItemTypeID); foreach (var field in itemType.Fields) { object newValue = item.GetFieldValue(field); object oldValue = item.GetFieldValue(field); // skip fields that haven't changed if (newValue == null || newValue.Equals(oldValue)) continue; // do field-specific processing for select fields switch (field.Name) { case FieldNames.Name: //disable for now //RestartWorkflow(userContext, suggestionsContext, item, WorkflowNames.NewTask); break; } } } }
/// <summary> /// Start NewUser/NewFolder/NewItem workflows based on the entity type /// </summary> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <param name="entity"></param> public static void StartNewWorkflows(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, ServerEntity entity) { if (entity == null) return; // figure out what kind of entity this is Item item = entity as Item; Folder folder = entity as Folder; User user = entity as User; // verify there are no workflow instances associated with this item yet var wis = suggestionsContext.WorkflowInstances.Where(wi => wi.EntityID == entity.ID).ToList(); if (wis.Count > 0) return; if (item != null && item.IsList == false) { #if false if (item.ItemTypeID == SystemItemTypes.Task) StartWorkflow(userContext, suggestionsContext, WorkflowNames.NewTask, item, null); // the Contact and Grocery new item processing happens in ItemProcessor now if (item.ItemTypeID == SystemItemTypes.Contact) StartWorkflow(userContext, suggestionsContext, WorkflowNames.NewContact, item, null); if (item.ItemTypeID == SystemItemTypes.Grocery) Workflow.StartWorkflow(userContext, suggestionsContext, WorkflowNames.NewGrocery, item, null); #endif } if (folder != null) { } if (user != null) { StartWorkflow(userContext, suggestionsContext, WorkflowNames.NewUser, user, null); } }
/// <summary> /// Restart the workflows associated with an entity /// </summary> /// <param name="userContext"></param> /// <param name="suggestionsContext"></param> /// <param name="entity"></param> /// <param name="workflowType"></param> public static void RestartWorkflow(UserStorageContext userContext, SuggestionsStorageContext suggestionsContext, ServerEntity entity, string workflowType) { if (entity == null || workflowType == null) return; try { // kill all existing workflows associated with this Item // TODO: also need to mark the suggestions associated with this workflow as stale so that they don't // show up for the item again. var runningWFs = suggestionsContext.WorkflowInstances.Where(wi => wi.EntityID == entity.ID).ToList(); if (runningWFs.Count > 0) { foreach (var wf in runningWFs) suggestionsContext.WorkflowInstances.Remove(wf); suggestionsContext.SaveChanges(); } StartWorkflow(userContext, suggestionsContext, workflowType, entity, null); } catch (Exception) { StartWorkflow(userContext, suggestionsContext, workflowType, entity, null); } }