Table 469 Workflow Webhook Subscription, source in 29
Source29
src/Layers/W1/BaseApp/System/Workflow/WorkflowWebhookSubscription.Table.al463 lines, Copyright (c) Microsoft Corporation. MIT
namespace System.Automation;
using Microsoft.Finance.GeneralLedger.Journal;
using Microsoft.Integration.Entity;
using Microsoft.Inventory.Item;
using Microsoft.Purchases.Document;
using Microsoft.Purchases.Vendor;
using Microsoft.Sales.Customer;
using Microsoft.Sales.Document;
using Microsoft.Utilities;
using System;
using System.Environment;
using System.Reflection;
using System.Text;
table 469 "Workflow Webhook Subscription"
{
Caption = 'Workflow Webhook Subscription';
DataClassification = CustomerContent;
fields
{
field(1; Id; Guid)
{
Caption = 'Id';
}
field(2; "WF Definition Id"; Code[20])
{
Caption = 'WF Definition Id';
}
field(3; Conditions; BLOB)
{
Caption = 'Conditions';
}
field(4; "Notification Url"; BLOB)
{
Caption = 'Notification Url';
}
field(5; Enabled; Boolean)
{
Caption = 'Enabled';
}
field(6; "User Id"; Code[50])
{
Caption = 'User Id';
DataClassification = EndUserIdentifiableInformation;
}
field(7; "Client Id"; Guid)
{
Caption = 'Client Id';
}
field(8; "Client Type"; Text[50])
{
Caption = 'Client Type';
}
field(9; "Event Code"; Code[128])
{
Caption = 'Event Code';
}
field(10; "Created Date"; DateTime)
{
Caption = 'Created Date';
}
}
keys
{
key(Key1; Id)
{
Clustered = true;
}
key(Key2; "WF Definition Id", Enabled)
{
}
}
fieldgroups
{
}
trigger OnDelete()
var
Workflow: Record Workflow;
WorkflowWebhookSubBuffer: Record "Workflow Webhook Sub Buffer";
IsTaskSchedulerAllowed: Boolean;
begin
// this would also clean up related Workflow table entry and Workflow Steps
if Workflow.Get("WF Definition Id") then begin
WorkflowWebhookSubBuffer.Init();
WorkflowWebhookSubBuffer."WF Definition Id" := "WF Definition Id";
WorkflowWebhookSubBuffer."Client Id" := "Client Id";
WorkflowWebhookSubBuffer.Insert();
IsTaskSchedulerAllowed := true;
OnFindTaskSchedulerAllowed(IsTaskSchedulerAllowed);
if IsTaskSchedulerAllowed then
TASKSCHEDULER.CreateTask(CODEUNIT::"Workflow Webhook Sub Delete", 0, true,
CompanyName, 0DT, Workflow.RecordId)
else
CODEUNIT.Run(CODEUNIT::"Workflow Webhook Sub Delete", Workflow);
end;
end;
trigger OnInsert()
begin
Id := CreateGuid();
"User ID" := CopyStr(UserId(), 1, MaxStrLen("User ID"));
"Created Date" := CurrentDateTime;
end;
trigger OnModify()
var
WorkflowWebhookSubscriptionPreviousRec: Record "Workflow Webhook Subscription";
WorkflowWebhookSubBuffer: Record "Workflow Webhook Sub Buffer";
begin
// Inserting new record also calls OnModify so checking to ensure it's a proper insert rather than modify
if IsInsert() then begin
// Check if incoming Rec's Client ID exists already
WorkflowWebhookSubscriptionPreviousRec.SetRange("Client Id", "Client Id");
WorkflowWebhookSubscriptionPreviousRec.SetRange(Enabled, true);
WorkflowWebhookSubscriptionPreviousRec.SetCurrentKey("Created Date");
WorkflowWebhookSubscriptionPreviousRec.SetAscending("Created Date", false);
if WorkflowWebhookSubscriptionPreviousRec.FindFirst() then
// will be creating new workflow so disable previous one
DisableWorkflow(WorkflowWebhookSubscriptionPreviousRec."WF Definition Id")
else begin
WorkflowWebhookSubBuffer.SetRange("Client Id", "Client Id");
if WorkflowWebhookSubBuffer.FindSet() then
repeat
DisableWorkflow(WorkflowWebhookSubBuffer."WF Definition Id");
until WorkflowWebhookSubBuffer.Next() = 0;
end;
CreateWorkflowDefinition();
EnableSubscriptionAndWorkflow(Rec);
end;
end;
var
JSONManagement: Codeunit "JSON Management";
WorkflowWebhookSetup: Codeunit "Workflow Webhook Setup";
UnableToParseEncodingErr: Label 'Unable to parse the Conditions. The provided Conditions were not in the correct Base64 encoded format.';
UnableToParseInvalidJsonErr: Label 'Unable to parse the Conditions. The provided Conditions JSON was invalid.';
NoControlOnPageErr: Label 'Unable to find a field with control name ''%1'' on page ''%2''.', Comment = '%1=control name;%2=page name';
UnableToParseJsonArrayErr: Label 'Unable to parse ''%1'' because it was not a valid JSON array.', Comment = '%1=conditions property name';
procedure SetConditions(ConditionsTxt: Text)
var
StreamOutObj: OutStream;
begin
// store as blob
Clear(Conditions);
Conditions.CreateOutStream(StreamOutObj);
StreamOutObj.WriteText(ConditionsTxt);
end;
procedure SetNotificationUrl(NotificationURLTxt: Text)
var
StreamOutObj: OutStream;
begin
// store as blob
Clear("Notification Url");
"Notification Url".CreateOutStream(StreamOutObj);
StreamOutObj.WriteText(NotificationURLTxt);
end;
procedure GetConditions() ConditionsText: Text
var
ReadStream: InStream;
begin
CalcFields(Conditions);
Conditions.CreateInStream(ReadStream);
ReadStream.ReadText(ConditionsText);
end;
procedure GetNotificationUrl() NotificationUrlText: Text
var
ReadStream: InStream;
begin
CalcFields("Notification Url");
"Notification Url".CreateInStream(ReadStream);
ReadStream.ReadText(NotificationUrlText);
end;
local procedure CreateWorkflowDefinition()
var
EventConditions: Text;
begin
EventConditions := CreateEventConditions(GetConditions(), "Event Code");
// the second argument, Name, is empty at this point by design (hardcoded to be a text constant inside the function)
"WF Definition Id" := WorkflowWebhookSetup.CreateWorkflowDefinition("Event Code", '', EventConditions, UserId);
end;
local procedure IsInsert(): Boolean
begin
// This is how we know a new record is being inserted into table
exit("WF Definition Id" = '');
end;
local procedure EnableSubscription(var WorkflowWebhookSubscriptionRec: Record "Workflow Webhook Subscription")
begin
// Turn On a Subscription
WorkflowWebhookSubscriptionRec.Enabled := true;
WorkflowWebhookSubscriptionRec.Modify();
end;
local procedure EnableWorkflow(WorkflowCode: Code[20])
var
Workflow: Record Workflow;
begin
// Enable a workflow
if Workflow.Get(WorkflowCode) then begin
Workflow.Validate(Enabled, true);
Workflow.Modify();
end;
end;
local procedure DisableWorkflow(WorkflowCode: Code[20])
var
Workflow: Record Workflow;
begin
// Disable a workflow
if Workflow.Get(WorkflowCode) then begin
Workflow.Validate(Enabled, false);
Workflow.Modify();
end;
end;
local procedure EnableSubscriptionAndWorkflow(var WorkflowWebhookSubscriptionRec: Record "Workflow Webhook Subscription")
begin
// Enable Subscription and its corresponding Workflow
EnableSubscription(WorkflowWebhookSubscriptionRec);
EnableWorkflow(WorkflowWebhookSubscriptionRec."WF Definition Id");
end;
[Scope('OnPrem')]
procedure CreateEventConditions(ConditionsTxt: Text; EventCode: Code[128]): Text
var
WorkflowEventHandling: Codeunit "Workflow Event Handling";
RequestPageParametersHelper: Codeunit "Request Page Parameters Helper";
EventConditions: FilterPageBuilder;
ConditionsObject: DotNet JObject;
ConditionsCount: Integer;
Result: Text;
IsHandled: Boolean;
begin
if not TryDecodeConditions(ConditionsTxt) then
SendAndLogError(GetLastErrorText, UnableToParseEncodingErr);
if not TryParseJson(ConditionsTxt, ConditionsObject) then
SendAndLogError(GetLastErrorText, UnableToParseInvalidJsonErr);
ConditionsCount := 1;
case EventCode of
WorkflowEventHandling.RunWorkflowOnSendSalesDocForApprovalCode():
begin
AddEventConditionsWrapper(
'HeaderConditions', ConditionsObject, PAGE::"Sales Document Entity", EventConditions, ConditionsCount);
AddEventConditionsWrapper(
'LinesConditions', ConditionsObject, PAGE::"Sales Document Line Entity", EventConditions, ConditionsCount);
exit(
RequestPageParametersHelper.GetViewFromDynamicRequestPage(
EventConditions, WorkflowWebhookSetup.GetSalesDocCategoryTxt(), DATABASE::"Sales Header"));
end;
WorkflowEventHandling.RunWorkflowOnSendPurchaseDocForApprovalCode():
begin
AddEventConditionsWrapper(
'HeaderConditions', ConditionsObject, PAGE::"Purchase Document Entity", EventConditions, ConditionsCount);
AddEventConditionsWrapper(
'LinesConditions', ConditionsObject, PAGE::"Purchase Document Line Entity", EventConditions, ConditionsCount);
exit(
RequestPageParametersHelper.GetViewFromDynamicRequestPage(
EventConditions, WorkflowWebhookSetup.GetPurchaseDocCategoryTxt(), DATABASE::"Purchase Header"));
end;
WorkflowEventHandling.RunWorkflowOnSendGeneralJournalBatchForApprovalCode():
begin
AddEventConditionsWrapper(
'Conditions', ConditionsObject, PAGE::"Gen. Journal Batch Entity", EventConditions, ConditionsCount);
exit(
RequestPageParametersHelper.GetViewFromDynamicRequestPage(
EventConditions, WorkflowWebhookSetup.GetFinCategoryTxt(), DATABASE::"Gen. Journal Batch"));
end;
WorkflowEventHandling.RunWorkflowOnSendGeneralJournalLineForApprovalCode():
begin
AddEventConditionsWrapper(
'Conditions', ConditionsObject, PAGE::"Gen. Journal Line Entity", EventConditions, ConditionsCount);
exit(
RequestPageParametersHelper.GetViewFromDynamicRequestPage(
EventConditions, WorkflowWebhookSetup.GetFinCategoryTxt(), DATABASE::"Gen. Journal Line"));
end;
WorkflowEventHandling.RunWorkflowOnSendCustomerForApprovalCode():
begin
AddEventConditionsWrapper(
'Conditions', ConditionsObject, PAGE::"Workflow - Customer Entity", EventConditions, ConditionsCount);
exit(
RequestPageParametersHelper.GetViewFromDynamicRequestPage(
EventConditions, WorkflowWebhookSetup.GetSalesMktCategoryTxt(), DATABASE::Customer));
end;
WorkflowEventHandling.RunWorkflowOnSendItemForApprovalCode():
begin
AddEventConditionsWrapper(
'Conditions', ConditionsObject, PAGE::"Workflow - Item Entity", EventConditions, ConditionsCount);
exit(
RequestPageParametersHelper.GetViewFromDynamicRequestPage(
EventConditions, WorkflowWebhookSetup.GetSalesMktCategoryTxt(), DATABASE::Item));
end;
WorkflowEventHandling.RunWorkflowOnSendVendorForApprovalCode():
begin
AddEventConditionsWrapper(
'Conditions', ConditionsObject, PAGE::"Workflow - Vendor Entity", EventConditions, ConditionsCount);
exit(
RequestPageParametersHelper.GetViewFromDynamicRequestPage(
EventConditions, WorkflowWebhookSetup.GetPurchPayCategoryTxt(), DATABASE::Vendor));
end
else begin
OnCreateWorkflowEventConditions(ConditionsTxt, EventCode, ConditionsObject, EventConditions, ConditionsCount, Result, IsHandled);
if IsHandled then
exit(Result);
SendAndLogError(
StrSubstNo(WorkflowWebhookSetup.GetUnsupportedWorkflowEventCodeErr(), EventCode),
StrSubstNo(WorkflowWebhookSetup.GetUnsupportedWorkflowEventCodeErr(), EventCode));
end;
end;
end;
local procedure AddEventConditionsWrapper(ConditionsPropertyName: Text; ConditionsObject: DotNet JObject; SourcePageNo: Integer; var EventConditions: FilterPageBuilder; var ConditionsCount: Integer)
var
ConditionsCollection: DotNet JToken;
begin
if ConditionsObject.TryGetValue(ConditionsPropertyName, ConditionsCollection) then begin
if not TryInitializeCollection(ConditionsCollection) then
SendAndLogError(GetLastErrorText, StrSubstNo(UnableToParseJsonArrayErr, ConditionsPropertyName));
AddEventConditions(ConditionsCollection, EventConditions, SourcePageNo, ConditionsCount);
ConditionsCount := ConditionsCount + 1;
end;
end;
[TryFunction]
local procedure TryInitializeCollection(var ConditionsCollection: DotNet JToken)
begin
JSONManagement.InitializeCollectionFromJArray(ConditionsCollection);
// need to do some action on the collection to check if it is of Collection type
if JSONManagement.GetCollectionCount() < 0 then
Error(GetLastErrorText);
end;
local procedure AddEventConditions(ConditionsArray: DotNet JObject; var EventConditions: FilterPageBuilder; SourcePageNo: Integer; ConditionIndex: Integer)
var
TableMetadata: Record "Table Metadata";
PageControlField: Record "Page Control Field";
RecRef: RecordRef;
FieldRef: FieldRef;
ConditionName: DotNet JToken;
Condition: DotNet JObject;
ConditionValue: DotNet JToken;
FieldId: Integer;
tableNo: Integer;
begin
// get source table id
PageControlField.SetFilter(PageNo, '%1', SourcePageNo);
PageControlField.FindFirst();
tableNo := PageControlField.TableNo;
RecRef.Open(tableNo);
PageControlField.Reset();
PageControlField.SetFilter(PageNo, '%1', SourcePageNo);
foreach Condition in ConditionsArray do
if Condition.TryGetValue('Name', ConditionName) and Condition.TryGetValue('Value', ConditionValue) then begin
// get id of the field from the page in the page's source table
PageControlField.SetFilter(ControlName, ConditionName.ToString());
if not PageControlField.FindFirst() then
SendAndLogError(GetLastErrorText, StrSubstNo(NoControlOnPageErr, ConditionName.ToString(), GetPageName(SourcePageNo)));
FieldId := PageControlField.FieldNo;
FieldRef := RecRef.Field(FieldId);
// filter Header/Lines Table
// throws an error message if can not convert types
FieldRef.SetFilter(ConditionValue.ToString());
end;
// create Filter Page Builder
TableMetadata.Get(tableNo);
EventConditions.AddTable(TableMetadata.Caption, tableNo);
EventConditions.SetView(EventConditions.Name(ConditionIndex), RecRef.GetView());
end;
[TryFunction]
local procedure TryParseJson(ConditionsTxt: Text; var ConditionsArray: DotNet JObject)
begin
JSONManagement.InitializeObject(ConditionsTxt);
JSONManagement.GetJSONObject(ConditionsArray);
end;
[TryFunction]
local procedure TryDecodeConditions(var ConditionsTxt: Text)
var
Convert: DotNet Convert;
Encoding: DotNet Encoding;
begin
ConditionsTxt := Encoding.UTF8.GetString(Convert.FromBase64String(ConditionsTxt));
end;
local procedure SendAndLogError(ErrorText: Text; Description: Text)
var
Company: Record Company;
ActivityLog: Record "Activity Log";
begin
// log exact error message
Company.Get(CompanyName);
ActivityLog.LogActivityForUser(
Company.RecordId, ActivityLog.Status::Failed, 'Power Automate', Description, ErrorText, UserId);
// send descriptive error to user
Error(Description);
end;
procedure GetPageName(PageId: Integer): Text
var
AllObj: Record AllObj;
begin
AllObj.SetFilter("Object ID", Format(PageId));
AllObj.SetFilter("Object Type", 'PAGE');
AllObj.FindFirst();
exit(AllObj."Object Name");
end;
procedure GetUnableToParseEncodingErr(): Text
begin
exit(UnableToParseEncodingErr);
end;
procedure GetUnableToParseInvalidJsonErr(): Text
begin
exit(UnableToParseInvalidJsonErr);
end;
procedure GetNoControlOnPageErr(): Text
begin
exit(NoControlOnPageErr);
end;
procedure GetUnableToParseJsonArrayErr(): Text
begin
exit(UnableToParseJsonArrayErr);
end;
[IntegrationEvent(false, false)]
local procedure OnFindTaskSchedulerAllowed(var IsTaskSchedulingAllowed: Boolean)
begin
end;
[IntegrationEvent(true, false)]
local procedure OnCreateWorkflowEventConditions(ConditionsTxt: Text; EventCode: Code[128]; ConditionsObject: DotNet JObject; var EventConditions: FilterPageBuilder; var ConditionsCount: Integer; var Result: Text; var IsHandled: Boolean)
begin
end;
}