├── SECURITY.md
├── res
├── Usage.gif
├── 1AADBC.png
├── 2DGSetup.png
├── 1AADBCes-ES.png
├── 2DGSetupes-ES.png
├── PortalAzure001.png
├── PortalAzure002.png
└── PortalAzure003.png
├── Dependency-Graph.Test
├── AppSourceCop.json
├── res
│ └── logo.png
├── doc
│ └── Dependency-Graph.Test.pdf
├── src
│ └── Test
│ │ ├── FillingProcessTablesMockANJ.EnumExt.al
│ │ ├── ExpectedValuesANJ.Codeunit.al
│ │ ├── NumberSequenceTestANJ.Codeunit.al
│ │ ├── GenerateFiguresTestANJ.Codeunit.al
│ │ ├── TemporaryTablesTestANJ.Codeunit.al
│ │ └── FillingProTablesMockANJ.Codeunit.al
├── DepGraphTestANJ.permissionset.al
├── .vscode
│ ├── launch.json
│ └── settings.json
└── app.json
├── Dependency-Graph
├── .vscode
│ ├── extensions.json
│ ├── settings.json
│ └── launch.json
├── res
│ └── logo.png
├── doc
│ └── Dependency-Graph.pdf
├── src
│ ├── ControlAddinViewer
│ │ ├── Start.js
│ │ ├── Style.css
│ │ ├── Scripts.js
│ │ ├── MarkdownViewerFSANJ.ControlAddin.al
│ │ └── MarkdownViewerANJ.ControlAddin.al
│ ├── GenerateFigure
│ │ ├── ExtensionScopeANJ.Enum.al
│ │ ├── IFigureInGraphANJ.Interface.al
│ │ ├── DefaultFigureIMPANJ.Codeunit.al
│ │ ├── CircleIMPANJ.Codeunit.al
│ │ ├── RhombusIMPANJ.Codeunit.al
│ │ ├── RoundRectangleIMPANJ.Codeunit.al
│ │ ├── SquareRectangleIMPANJ.Codeunit.al
│ │ ├── GeometricFigureANJ.Enum.al
│ │ └── GenerateFigureANJ.Codeunit.al
│ ├── Base
│ │ ├── IFillingProcessingTablesANJ.Interface.al
│ │ ├── FillingProcessingTablesANJ.Enum.al
│ │ ├── DefaultFillProcessTabImpANJ.Codeunit.al
│ │ ├── JSONMethodsANJ.Codeunit.al
│ │ ├── MarkdownFactboxANJ.Page.al
│ │ ├── ShowInGraphANJ.Page.al
│ │ ├── CleanTemporaryTablesANJ.Codeunit.al
│ │ ├── GenerateTablesANJ.Codeunit.al
│ │ ├── DependencyGraphFacadeANJ.Codeunit.al
│ │ └── WSAndMDInfoImpANJ.Codeunit.al
│ ├── UpgradeTagsExample
│ │ ├── UpgradeCodeunitANJ.Codeunit.al
│ │ └── DataUpgradesANJ.Codeunit.al
│ ├── Markdown
│ │ ├── MarkdownViewerFullANJ.Page.al
│ │ ├── MarkdownViewerANJ.Page.al
│ │ └── MarkdownMgmtANJ.Codeunit.al
│ ├── Relations
│ │ ├── RelationsANJ.Page.al
│ │ ├── RelationsANJ.Table.al
│ │ └── GenerateRelationsTableANJ.Codeunit.al
│ ├── ExtensionTable
│ │ ├── ExtensionsANJ.Page.al
│ │ ├── NumberSequenceMgmtANJ.Codeunit.al
│ │ ├── ExtensionsANJ.Table.al
│ │ └── GenerateExtensionTableANJ.Codeunit.al
│ └── Setup
│ │ ├── DependencyGraphSetupANJ.Page.al
│ │ └── DependencyGraphSetupANJ.Table.al
├── AppSourceCop.json
├── app.json
├── Test Odata
│ └── Test API.http
└── DependencyGraphANJ.permissionset.al
├── .github
├── Test Current.settings.json
├── Test Next Major.settings.json
├── Test Next Minor.settings.json
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── FeatureSuggestion.yml
│ └── BugReport.yml
├── AL-Go-Settings.json
├── pull_request_template.md
├── workflows
│ ├── Troubleshooting.yaml
│ ├── DeployReferenceDocumentation.yaml
│ ├── AddExistingAppOrTestApp.yaml
│ ├── IncrementVersionNumber.yaml
│ ├── CreateApp.yaml
│ ├── CreateTestApp.yaml
│ ├── CreatePerformanceTestApp.yaml
│ ├── Current.yaml
│ ├── NextMajor.yaml
│ ├── NextMinor.yaml
│ ├── PullRequestHandler.yaml
│ ├── UpdateGitHubGoSystemFiles.yaml
│ ├── CreateOnlineDevelopmentEnvironment.yaml
│ └── PublishToEnvironment.yaml
└── copilot-instructions.md
├── Dependency-Graph.code-workspace
├── .gitignore
├── Environment.ps1
├── .AL-Go
├── settings.json
├── cloudDevEnv.ps1
└── localDevEnv.ps1
├── LICENSE.md
├── CONTRIBUTING.md
├── README.md
└── CODE_OF_CONDUCT.md
/SECURITY.md:
--------------------------------------------------------------------------------
1 | ## Reporting a vulnerability
2 | Contact me directly at anovoa@novoadev.com.
3 |
--------------------------------------------------------------------------------
/res/Usage.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovoaDev/Dependency-Graph-BCExt/HEAD/res/Usage.gif
--------------------------------------------------------------------------------
/res/1AADBC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovoaDev/Dependency-Graph-BCExt/HEAD/res/1AADBC.png
--------------------------------------------------------------------------------
/res/2DGSetup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovoaDev/Dependency-Graph-BCExt/HEAD/res/2DGSetup.png
--------------------------------------------------------------------------------
/res/1AADBCes-ES.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovoaDev/Dependency-Graph-BCExt/HEAD/res/1AADBCes-ES.png
--------------------------------------------------------------------------------
/res/2DGSetupes-ES.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovoaDev/Dependency-Graph-BCExt/HEAD/res/2DGSetupes-ES.png
--------------------------------------------------------------------------------
/res/PortalAzure001.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovoaDev/Dependency-Graph-BCExt/HEAD/res/PortalAzure001.png
--------------------------------------------------------------------------------
/res/PortalAzure002.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovoaDev/Dependency-Graph-BCExt/HEAD/res/PortalAzure002.png
--------------------------------------------------------------------------------
/res/PortalAzure003.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovoaDev/Dependency-Graph-BCExt/HEAD/res/PortalAzure003.png
--------------------------------------------------------------------------------
/Dependency-Graph.Test/AppSourceCop.json:
--------------------------------------------------------------------------------
1 | {
2 | "mandatorySuffix": "_ANJ",
3 | "supportedCountries": ["US"]
4 | }
--------------------------------------------------------------------------------
/Dependency-Graph/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "humao.rest-client"
4 | ]
5 | }
--------------------------------------------------------------------------------
/Dependency-Graph/res/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovoaDev/Dependency-Graph-BCExt/HEAD/Dependency-Graph/res/logo.png
--------------------------------------------------------------------------------
/.github/Test Current.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "artifact": "////latest",
3 | "cacheImageName": "",
4 | "versioningStrategy": 15
5 | }
6 |
--------------------------------------------------------------------------------
/Dependency-Graph.Test/res/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovoaDev/Dependency-Graph-BCExt/HEAD/Dependency-Graph.Test/res/logo.png
--------------------------------------------------------------------------------
/Dependency-Graph/doc/Dependency-Graph.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovoaDev/Dependency-Graph-BCExt/HEAD/Dependency-Graph/doc/Dependency-Graph.pdf
--------------------------------------------------------------------------------
/.github/Test Next Major.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "artifact": "////nextmajor/{INSIDERSASTOKEN}",
3 | "cacheImageName": "",
4 | "versioningStrategy": 15
5 | }
6 |
--------------------------------------------------------------------------------
/.github/Test Next Minor.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "artifact": "////nextminor/{INSIDERSASTOKEN}",
3 | "cacheImageName": "",
4 | "versioningStrategy": 15
5 | }
6 |
--------------------------------------------------------------------------------
/Dependency-Graph.Test/doc/Dependency-Graph.Test.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NovoaDev/Dependency-Graph-BCExt/HEAD/Dependency-Graph.Test/doc/Dependency-Graph.Test.pdf
--------------------------------------------------------------------------------
/Dependency-Graph/src/ControlAddinViewer/Start.js:
--------------------------------------------------------------------------------
1 | HTMLContainer = document.getElementById("controlAddIn");
2 | mermaid.initialize({ startOnLoad: false });
3 | Microsoft.Dynamics.NAV.InvokeExtensibilityMethod("Ready", []);
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: GitHub Community Support
4 | url: https://github.com/NovoaDev/Dependency-Graph-BCExt/discussions
5 | about: Please ask and answer questions here.
--------------------------------------------------------------------------------
/Dependency-Graph/src/ControlAddinViewer/Style.css:
--------------------------------------------------------------------------------
1 | iframe {
2 | margin: 0 auto;
3 | }
4 |
5 | .MermaidDiv {
6 | width: 100%;
7 | height: auto;
8 | word-wrap: break-word;
9 | padding-left: 1%;
10 | padding-right: 1;
11 | }
--------------------------------------------------------------------------------
/Dependency-Graph.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": ".AL-Go"
5 | },
6 | {
7 | "path": "Dependency-Graph"
8 | },
9 | {
10 | "path": "Dependency-Graph.Test"
11 | }
12 | ],
13 | "settings": {}
14 | }
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.app
2 | *.flf
3 | *.bclicense
4 | .DS_Store
5 | Thumbs.db
6 | TestResults*.xml
7 | bcptTestResults*.json
8 | BuildOutput.txt
9 | rad.json
10 | .output/
11 | .dependencies/
12 | .buildartifacts/
13 | .alpackages/
14 | .packages/
15 | .alcache/
16 | .altemplates/
17 | .snapshots/
18 | cache_*
19 | ~$*
20 |
--------------------------------------------------------------------------------
/.github/AL-Go-Settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "PTE",
3 | "nextMajorSchedule": "0 0 1,15 * *",
4 | "nextMinorSchedule": "0 0 15 * *",
5 | "UpdateGitHubGoSystemFilesSchedule": "0 0 1,15 * *",
6 | "templateUrl": "https://github.com/microsoft/AL-Go-PTE@main",
7 | "templateSha": "a91a50b7fa2a88d8fa0b8eab99a66b9032ac476d"
8 | }
9 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | - [ ] The PR was created from a branch associated to an Issue.
2 | - [ ] The pull request is complete and the project can be compiled.
3 | - [ ] The ticket acceptance criteria have been met.
4 | - [ ] There has been some kind of fedback from another contributor.
5 | - [ ] Documentation is up to date.
6 | - [ ] The automated tests are written (Optional).
--------------------------------------------------------------------------------
/Dependency-Graph.Test/src/Test/FillingProcessTablesMockANJ.EnumExt.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// EnumExtension FillingProcessingTablesMock_ANJ (ID 99990) extends Record FillingProcessingTables_ANJ.
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | enumextension 99990 FillingProcessTablesMock_ANJ extends FillingProcessingTables_ANJ
6 | {
7 | value(99990; Mock_ANJ)
8 | {
9 | Caption = 'Mock';
10 | Implementation = IFillingProcessingTables_ANJ = FillingProTablesMock_ANJ;
11 | }
12 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/GenerateFigure/ExtensionScopeANJ.Enum.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Enum ExtensionScope_ANJ (ID 80801)
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | enum 80801 ExtensionScope_ANJ
6 | {
7 | Access = Public;
8 | Caption = 'Extension Scope';
9 | Extensible = false;
10 |
11 | value(1; Global)
12 | {
13 | Caption = 'Global';
14 | }
15 | value(2; Dev)
16 | {
17 | Caption = 'Dev';
18 | }
19 | value(3; PTE)
20 | {
21 | Caption = 'PTE';
22 | }
23 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/GenerateFigure/IFigureInGraphANJ.Interface.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Interface "FigureInGraph_ANJ."
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | interface IFigureInGraph_ANJ
6 | {
7 | Access = Public;
8 |
9 | ///
10 | /// GenerateFigureText.
11 | ///
12 | /// Text.
13 | /// Text.
14 | /// Return value of type Text.
15 | procedure GenerateFigureText(Identity: Text; Content: Text): Text
16 | }
--------------------------------------------------------------------------------
/Dependency-Graph.Test/DepGraphTestANJ.permissionset.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// permissionset DepGraphTest_ANJ (ID 99990).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | permissionset 99990 DepGraphTest_ANJ
6 | {
7 | Access = Public;
8 | Assignable = true;
9 | Caption = 'Dep. Graph Test', Locked = true;
10 | Permissions = codeunit ExpectedValues_ANJ = X,
11 | codeunit FillingProTablesMock_ANJ = X,
12 | codeunit GenerateFiguresTest_ANJ = X,
13 | codeunit NumberSequenceTest_ANJ = X,
14 | codeunit TemporaryTablesTest_ANJ = X;
15 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/Base/IFillingProcessingTablesANJ.Interface.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Interface "FillingProcessingTables_ANJ."
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | interface IFillingProcessingTables_ANJ
6 | {
7 | Access = Public;
8 |
9 | ///
10 | /// GetExtensions.
11 | ///
12 | /// Return value of type Text.
13 | procedure GetExtensions(): Text
14 |
15 | ///
16 | /// GetRelations.
17 | ///
18 | /// Return value of type Text.
19 | procedure GetRelations() JsonText: Text
20 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/ControlAddinViewer/Scripts.js:
--------------------------------------------------------------------------------
1 | /*
2 | Embed mermaid in Javascript learned from video by Erick houguard
3 | https://www.youtube.com/watch?v=OIVVpFSJLmw
4 | https://www.hougaard.com/
5 |
6 | - Thank you very much! -
7 | */
8 |
9 | function Draw(Markdown) {
10 | try {
11 | const insertSvg = function (svgCode, bindFunctions) {
12 | document.getElementById('controlAddIn').innerHTML = '
' + svgCode + '
';
13 | };
14 | mermaid.mermaidAPI.render('chart', Markdown, insertSvg);
15 | }
16 | catch (e) {
17 | console.log(e);
18 | }
19 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/ControlAddinViewer/MarkdownViewerFSANJ.ControlAddin.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// ControlAddIn "MarkdownViewer_ANJ._ANJ"
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | controladdin MarkdownViewerFS_ANJ
6 | {
7 | HorizontalStretch = true;
8 | Scripts = 'src\ControlAddinViewer\Scripts.js', 'https://cdnjs.cloudflare.com/ajax/libs/mermaid/9.3.0/mermaid.min.js';
9 | StartupScript = 'src\ControlAddinViewer\Start.js';
10 | VerticalStretch = true;
11 |
12 | ///
13 | /// Ready.
14 | ///
15 | event Ready();
16 |
17 | ///
18 | /// Draw.
19 | ///
20 | /// Text.
21 | procedure Draw(Markdown: Text)
22 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/Base/FillingProcessingTablesANJ.Enum.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Enum FillingProcessingTables_ANJ (ID 80802).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | enum 80802 FillingProcessingTables_ANJ implements IFillingProcessingTables_ANJ
6 | {
7 | Access = Public;
8 | Caption = 'Filling Processing Tables';
9 | DefaultImplementation = IFillingProcessingTables_ANJ = DefaultFillProcessTabImp_ANJ;
10 | Extensible = true;
11 | UnknownValueImplementation = IFillingProcessingTables_ANJ = DefaultFillProcessTabImp_ANJ;
12 |
13 | value(1; WSAndModuleDependencyInfo)
14 | {
15 | Caption = 'WS And ModuleDependencyInfo';
16 | Implementation = IFillingProcessingTables_ANJ = WSAndMDInfoImp_ANJ;
17 | }
18 | }
--------------------------------------------------------------------------------
/Environment.ps1:
--------------------------------------------------------------------------------
1 | # container
2 | $ContainerName = 'DependencyGraph'
3 | $licenseFile = 'C:\License\Dev.bclicense'
4 |
5 | # Image
6 | $artifactUrl = Get-BCArtifactUrl -country "es" -version 25
7 |
8 | # User
9 | $UserName = 'User'
10 | $Password = ConvertTo-SecureString "Pass123" -AsPlainText -Force
11 | $Credential = New-Object System.Management.Automation.PSCredential ($UserName, $Password)
12 |
13 | New-BCContainer -accept_eula `
14 | -accept_outdated `
15 | -updateHosts `
16 | -containername $containername `
17 | -artifactUrl $artifactUrl `
18 | -auth NavUserPassword `
19 | -Credential $credential `
20 | -isolation hyperv `
21 | -includeTestToolkit
--------------------------------------------------------------------------------
/Dependency-Graph/src/GenerateFigure/DefaultFigureIMPANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "DefaultFigureIMP_ANJ" (ID 80814) implements Interface FigureInGraph_ANJ.
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80814 DefaultFigureIMP_ANJ implements IFigureInGraph_ANJ
6 | {
7 | Access = Internal;
8 |
9 | ///
10 | /// GenerateFigureText.
11 | ///
12 | /// Text.
13 | /// Text.
14 | /// Return value of type Text.
15 | procedure GenerateFigureText(Identity: Text; Content: Text): Text
16 | begin
17 | Error(ImpErr);
18 | end;
19 |
20 | var
21 | ImpErr: Label 'An implementation should be specified to the related figure.';
22 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/ControlAddinViewer/MarkdownViewerANJ.ControlAddin.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// ControlAddIn "MarkdownViewer_ANJ."
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | controladdin MarkdownViewer_ANJ
6 | {
7 | HorizontalShrink = true;
8 | RequestedHeight = 600;
9 | RequestedWidth = 650;
10 | Scripts = 'src\ControlAddinViewer\Scripts.js', 'https://cdnjs.cloudflare.com/ajax/libs/mermaid/9.3.0/mermaid.min.js';
11 | StartupScript = 'src\ControlAddinViewer\Start.js';
12 | StyleSheets = 'src\ControlAddinViewer\Style.css';
13 | VerticalShrink = true;
14 |
15 | ///
16 | /// Ready.
17 | ///
18 | event Ready();
19 |
20 | ///
21 | /// Draw.
22 | ///
23 | /// Text.
24 | procedure Draw(Markdown: Text)
25 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/GenerateFigure/CircleIMPANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "CircleIMP_ANJ" (ID 80801) implements Interface FigureInGraph_ANJ.
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80801 CircleIMP_ANJ implements IFigureInGraph_ANJ
6 | {
7 | Access = Internal;
8 |
9 | ///
10 | /// GenerateFigureText.
11 | ///
12 | /// Text.
13 | /// Text.
14 | /// Return value of type Text.
15 | procedure GenerateFigureText(Identity: Text; Content: Text): Text
16 | begin
17 | exit(StrSubstNo(FigureLbl, Identity, Content));
18 | end;
19 |
20 | var
21 | FigureLbl: Label '%1((%2))', Comment = 'Placeholder %1 represents the Identity, and Placeholder %2 represents the Content.';
22 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/GenerateFigure/RhombusIMPANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "RhombusIMP_ANJ" (ID 80803) implements Interface FigureInGraph_ANJ.
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80803 RhombusIMP_ANJ implements IFigureInGraph_ANJ
6 | {
7 | Access = Internal;
8 |
9 | ///
10 | /// GenerateFigureText.
11 | ///
12 | /// Text.
13 | /// Text.
14 | /// Return value of type Text.
15 | procedure GenerateFigureText(Identity: Text; Content: Text): Text
16 | begin
17 | exit(StrSubstNo(FigureLbl, Identity, Content));
18 | end;
19 |
20 | var
21 | FigureLbl: Label '%1{%2}', Comment = 'Placeholder %1 represents the Identity, and Placeholder %2 represents the Content.';
22 | }
--------------------------------------------------------------------------------
/Dependency-Graph/AppSourceCop.json:
--------------------------------------------------------------------------------
1 | {
2 | "supportedCountries":
3 | ["AL", "DZ", "AD", "AO", "AR", "AM", "AU", "AT", "AZ", "BS", "BH", "BD", "BE", "BM", "BA", "BW", "VG", "BR", "BG", "KH", "CM", "CA", "KY", "CL", "CO", "CR",
4 | "HR", "CY", "CZ", "DK", "DO", "EC", "SV", "EG", "EE", "ET", "DK", "FJ", "FI", "FR", "GE", "DE", "GH", "HN", "HK", "HU", "IS", "IN", "ID", "IE", "IM", "IL",
5 | "IT", "JM", "JP", "JE", "JO", "KZ", "KE", "KW", "LB", "LV", "LT", "LU", "MO", "MK", "MG", "MW", "MY", "MV", "MT", "MU", "MX", "ME", "MA", "MZ", "MM", "NA",
6 | "NP", "NL", "NZ", "NI", "NG", "OM", "PK", "PA", "PE", "PH", "PL", "PT", "PR", "QA", "RO", "RU", "SA", "SN", "SG", "SK", "ZA", "KR", "ES", "LK", "SE", "CH",
7 | "TW", "TZ", "TH", "TT", "TN", "TR", "UG", "UA", "AE", "GB", "US", "UY", "VN", "VG", "GB", "US", "UY", "ZM", "ZW"],
8 | "mandatorySuffix": "_ANJ"
9 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/GenerateFigure/RoundRectangleIMPANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "RoundRectangleIMP_ANJ" (ID 80802) implements Interface FigureInGraph_ANJ.
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80802 RoundRectangleIMP_ANJ implements IFigureInGraph_ANJ
6 | {
7 | Access = Internal;
8 |
9 | ///
10 | /// GenerateFigureText.
11 | ///
12 | /// Text.
13 | /// Text.
14 | /// Return value of type Text.
15 | procedure GenerateFigureText(Identity: Text; Content: Text): Text
16 | begin
17 | exit(StrSubstNo(FigureLbl, Identity, Content));
18 | end;
19 |
20 | var
21 | FigureLbl: Label '%1(%2)', Comment = 'Placeholder %1 represents the Identity, and Placeholder %2 represents the Content.';
22 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/GenerateFigure/SquareRectangleIMPANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "SquareRectangleIMP_ANJ" (ID 80800) implements Interface FigureInGraph_ANJ.
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80800 SquareRectangleIMP_ANJ implements IFigureInGraph_ANJ
6 | {
7 | Access = Internal;
8 |
9 | ///
10 | /// GenerateFigureText.
11 | ///
12 | /// Text.
13 | /// Text.
14 | /// Return value of type Text.
15 | procedure GenerateFigureText(Identity: Text; Content: Text): Text
16 | begin
17 | exit(StrSubstNo(FigureLbl, Identity, Content));
18 | end;
19 |
20 | var
21 | FigureLbl: Label '%1[%2]', Comment = 'Placeholder %1 represents the Identity, and Placeholder %2 represents the Content.';
22 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/UpgradeTagsExample/UpgradeCodeunitANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "UpgradeCodeunit_ANJ" (ID 80817).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80817 UpgradeCodeunit_ANJ
6 | {
7 | Access = Internal;
8 | Subtype = Upgrade;
9 |
10 | trigger OnUpgradePerCompany()
11 | begin
12 | DataUpgrade();
13 | end;
14 |
15 | ///
16 | /// DataUpgrade.
17 | ///
18 | local procedure DataUpgrade()
19 | var
20 | DataUpgrades: Codeunit DataUpgrades_ANJ;
21 | UpgradeTag: Codeunit System.Upgrade."Upgrade Tag";
22 | begin
23 | if not UpgradeTag.HasUpgradeTag(DataUpgrades.GetUpgradeTagDataUpgrade1()) then begin
24 | DataUpgrades.RunDataUpgrade1();
25 | UpgradeTag.SetUpgradeTag(DataUpgrades.GetUpgradeTagDataUpgrade1());
26 | end;
27 | end;
28 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/Markdown/MarkdownViewerFullANJ.Page.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Page "MarkdownViewerFull_ANJ" (ID 80804).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | page 80806 MarkdownViewerFull_ANJ
6 | {
7 | ApplicationArea = All;
8 | Caption = 'Markdown Viewer';
9 | Extensible = false;
10 | PageType = Card;
11 | UsageCategory = None;
12 |
13 | layout
14 | {
15 | area(Content)
16 | {
17 | usercontrol(MarkdownViewerFS; MarkdownViewerFS_ANJ)
18 | {
19 | }
20 | }
21 | }
22 |
23 | trigger OnAfterGetCurrRecord()
24 | var
25 | MarkdownMermaidFieldID: Integer;
26 | begin
27 | MarkdownMermaidFieldID := 15;
28 | CurrPage.MarkdownViewerFS.Draw(DependencyGraphFacade.GetMarkdownText(MarkdownMermaidFieldID));
29 | end;
30 |
31 | var
32 | DependencyGraphFacade: Codeunit DependencyGraphFacade_ANJ;
33 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/Base/DefaultFillProcessTabImpANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "DefaultFillProcessTabImp_ANJ" (ID 80815) implements Interface FillingProcessingTables_ANJ.
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80815 DefaultFillProcessTabImp_ANJ implements IFillingProcessingTables_ANJ
6 | {
7 | Access = Internal;
8 |
9 | ///
10 | /// GetExtensions.
11 | ///
12 | /// Return value of type Text.
13 | procedure GetExtensions(): Text
14 | begin
15 | Error(ImpErr);
16 | end;
17 |
18 | ///
19 | /// GetRelations.
20 | ///
21 | /// Return variable JsonText of type Text.
22 | procedure GetRelations() JsonText: Text
23 | begin
24 | Error(ImpErr);
25 | end;
26 |
27 | var
28 | ImpErr: Label 'An implementation for the generation of related relationship tables must be specified.';
29 |
30 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/FeatureSuggestion.yml:
--------------------------------------------------------------------------------
1 | name: Suggestion Feature
2 | description: Request a suggestion feature
3 | title: "[Suggestion]: "
4 | labels: ["enhancement"]
5 | projects: ["Dependency-Graph"]
6 | assignees:
7 | - novoadev
8 | body:
9 | - type: markdown
10 | attributes:
11 | value: |
12 | Thanks for taking the time to fill out this Suggestion report!
13 |
14 | I promise to read it and take it into considerations :two_hearts:
15 | - type: textarea
16 | id: New-Feature
17 | attributes:
18 | label: Describe the new feature and what need it meets
19 | description: Describe a small use case.
20 | placeholder: ...
21 | validations:
22 | required: true
23 | - type: textarea
24 | id: alternatives
25 | attributes:
26 | label: Describe alternatives you've considered
27 | description: A clear and concise description of any alternative solutions or features you've considered.
28 | placeholder: ...
29 | validations:
30 | required: false
--------------------------------------------------------------------------------
/.github/workflows/Troubleshooting.yaml:
--------------------------------------------------------------------------------
1 | name: 'Troubleshooting'
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | displayNameOfSecrets:
7 | description: Display the name (not the value) of secrets available to the repository
8 | type: boolean
9 | default: false
10 |
11 | permissions:
12 | actions: read
13 | contents: read
14 |
15 | defaults:
16 | run:
17 | shell: powershell
18 |
19 | env:
20 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
21 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
22 |
23 | jobs:
24 | Troubleshooting:
25 | runs-on: [ windows-latest ]
26 | steps:
27 | - name: Checkout
28 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
29 | with:
30 | lfs: true
31 |
32 | - name: Troubleshooting
33 | uses: microsoft/AL-Go-Actions/Troubleshooting@v7.0
34 | with:
35 | shell: powershell
36 | gitHubSecrets: ${{ toJson(secrets) }}
37 | displayNameOfSecrets: ${{ github.event.inputs.displayNameOfSecrets }}
38 |
--------------------------------------------------------------------------------
/.AL-Go/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "country": "es",
3 | "environments": [
4 | "TESTING",
5 | "PRODUCTION (Production)"
6 | ],
7 | "DeployToTESTING": {
8 | "EnvironmentName": "TESTING",
9 | "Projects": "*",
10 | "SyncMode": "ForceSync"
11 | },
12 | "DeployToPRODUCTION": {
13 | "EnvironmentName": "PRODUCTION",
14 | "Projects": "*",
15 | "ContinuousDeployment": false
16 | },
17 | "appFolders": [],
18 | "testFolders": [],
19 | "bcptTestFolders": [],
20 | "doNotRunBCPTTests": true,
21 | "doNotSignApps": true,
22 | "enableCodeCop": true,
23 | "enableUICop": true,
24 | "enablePerTenantExtensionCop": true,
25 | "enableAppSourceCop": true,
26 | "AppSourceCopMandatoryAffixes": [
27 | "_ANJ"
28 | ],
29 | "customCodeCops": [
30 | "https://github.com/StefanMaron/BusinessCentral.LinterCop/releases/latest/download/BusinessCentral.LinterCop.current.dll"
31 | ],
32 | "rulesetFile": "Dependency-Graph/.vscode/ruleset.json",
33 | "ALDoc": {
34 | "continuousDeployment": false
35 | },
36 | "skipUpgrade": true,
37 | "repoVersion": "1.1"
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Antonio Novoa
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Dependency-Graph/src/GenerateFigure/GeometricFigureANJ.Enum.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Enum GeometricFigure_ANJ (ID 80800) implements Interface FigureInGraph_ANJ.
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | enum 80800 GeometricFigure_ANJ implements IFigureInGraph_ANJ
6 | {
7 | Access = Public;
8 | Caption = 'Geometric Figure';
9 | DefaultImplementation = IFigureInGraph_ANJ = DefaultFigureIMP_ANJ;
10 | Extensible = true;
11 | UnknownValueImplementation = IFigureInGraph_ANJ = DefaultFigureIMP_ANJ;
12 |
13 | value(1; Circle)
14 | {
15 | Caption = 'Circle';
16 | Implementation = IFigureInGraph_ANJ = CircleIMP_ANJ;
17 | }
18 | value(2; RoundRectangle)
19 | {
20 | Caption = 'Round Rectangle';
21 | Implementation = IFigureInGraph_ANJ = RoundRectangleIMP_ANJ;
22 | }
23 | value(3; Rhombus)
24 | {
25 | Caption = 'Rhombus';
26 | Implementation = IFigureInGraph_ANJ = RhombusIMP_ANJ;
27 | }
28 | value(4; SquareRectangle)
29 | {
30 | Caption = 'Square/Rectangle';
31 | Implementation = IFigureInGraph_ANJ = SquareRectangleIMP_ANJ;
32 | }
33 | }
--------------------------------------------------------------------------------
/Dependency-Graph/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "al.enableCodeAnalysis": true,
3 | "al.backgroundCodeAnalysis": "Project",
4 | "al.codeAnalyzers": [
5 | "${CodeCop}",
6 | "${UICop}",
7 | "${PerTenantExtensionCop}",
8 | "${AppSourceCop}",
9 | "${analyzerFolder}BusinessCentral.LinterCop.dll"
10 | ],
11 | "al.enableCodeActions": true,
12 | "al.ruleSetPath": "./.vscode/ruleset.json",
13 | "CRS.FileNamePattern": "..al",
14 | "CRS.FileNamePatternExtensions": "..al",
15 | "CRS.FileNamePatternPageCustomizations": "..al",
16 | "CRS.OnSaveAlFileAction": "DoNothing",
17 | "CRS.RenameWithGit": true,
18 | "editor.formatOnSave": true,
19 | "CRS.ObjectNameSuffix": "_ANJ",
20 | "rest-client.rememberCookiesForSubsequentRequests": false,
21 | "editor.codeActionsOnSave": {
22 | "source.fixAll.al": "explicit"
23 | },
24 | "alOutline.codeActionsOnSave": [
25 | "SortProperties",
26 | "SortVariables",
27 | "FormatDocument"
28 | ]
29 | }
--------------------------------------------------------------------------------
/Dependency-Graph/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "3c9a4c22-42f6-4e8f-8b96-744c7fef331f",
3 | "name": "Dependency-Graph",
4 | "publisher": "ANJ",
5 | "version": "4.17.0.0",
6 | "brief": "App to generate the dependency tree",
7 | "description": "App to generate the dependency tree of the extensions we have installed.",
8 | "privacyStatement": "https://github.com/NovoaDev/Dependency-Graph-BCExt",
9 | "EULA": "https://github.com/NovoaDev/Dependency-Graph-BCExt",
10 | "help": "https://github.com/NovoaDev/Dependency-Graph-BCExt",
11 | "url": "https://github.com/NovoaDev/Dependency-Graph-BCExt",
12 | "logo": "res/logo.png",
13 | "contextSensitiveHelpUrl": "https://github.com/NovoaDev/Dependency-Graph-BCExt",
14 | "dependencies": [],
15 | "screenshots": [],
16 | "platform": "1.0.0.0",
17 | "application": "25.0.0.0",
18 | "idRanges": [
19 | {
20 | "from": 80800,
21 | "to": 80849
22 | }
23 | ],
24 | "features": [
25 | "NoImplicitWith",
26 | "TranslationFile"
27 | ],
28 | "resourceExposurePolicy": {
29 | "allowDebugging": true,
30 | "allowDownloadingSource": false,
31 | "includeSourceInSymbolFile": false
32 | },
33 | "runtime": "14.0"
34 | }
35 |
--------------------------------------------------------------------------------
/Dependency-Graph/src/Relations/RelationsANJ.Page.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Page Relations_ANJ (ID 80803).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | page 80803 Relations_ANJ
6 | {
7 | ApplicationArea = All;
8 | Caption = 'Relations';
9 | DeleteAllowed = false;
10 | Extensible = true;
11 | InsertAllowed = false;
12 | PageType = ListPart;
13 | SourceTable = Relations_ANJ;
14 | UsageCategory = None;
15 |
16 | layout
17 | {
18 | area(Content)
19 | {
20 | repeater(General)
21 | {
22 | field(SourceAppName; Rec.SourceAppName)
23 | {
24 | }
25 | field(LinkText; Rec.LinkText)
26 | {
27 | trigger OnValidate()
28 | begin
29 | CurrPage.Update(true);
30 | end;
31 | }
32 | field(DestinationAppName; Rec.DestinationAppName)
33 | {
34 | }
35 | field(ShowInGraph; Rec.ShowInGraph)
36 | {
37 | trigger OnValidate()
38 | begin
39 | CurrPage.Update(true);
40 | end;
41 | }
42 | }
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/BugReport.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: File a bug report
3 | title: "[Bug]: "
4 | labels: ["bug"]
5 | projects: ["Dependency-Graph"]
6 | assignees:
7 | - novoadev
8 | body:
9 | - type: markdown
10 | attributes:
11 | value: |
12 | Thanks for taking the time to fill out this bug report!
13 |
14 | With your help we will try to annihilate all those :bug: bastards.
15 | - type: textarea
16 | id: What-bug-did-you-find
17 | attributes:
18 | label: What bug did you find?
19 | description: A clear and concise description of what the problem encountered is.
20 | placeholder: Tell us what you see!
21 | validations:
22 | required: true
23 | - type: textarea
24 | id: Steps-To-Reproduce
25 | attributes:
26 | label: What are the steps to reproduce it?
27 | description: Please indicate which are the steps so that we can reproduce this bug.
28 | placeholder: What do we have to do?
29 | validations:
30 | required: true
31 | - type: textarea
32 | id: Positive-Result
33 | attributes:
34 | label: Satisfactory solution result?
35 | description: Do you know what the expected behavior would be if the bug is fixed?.
36 | placeholder: Tell us what you expect!
37 | validations:
38 | required: false
--------------------------------------------------------------------------------
/Dependency-Graph/src/Base/JSONMethodsANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "JSONMethods_ANJ" (ID 80813).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80813 JSONMethods_ANJ
6 | {
7 | Access = Internal;
8 |
9 | ///
10 | /// GetJsonValue.
11 | ///
12 | /// Text.
13 | /// Text.
14 | /// Return value of type Boolean.
15 | internal procedure GetJsonValue(JsonKey: Text; ResponseText: Text): Text
16 | var
17 | AuxJsonObject: JsonObject;
18 | AuxJsonToken: JsonToken;
19 | JsonText: Text;
20 | begin
21 | if not AuxJsonObject.ReadFrom(ResponseText) then
22 | Error(ReadingJsonErr);
23 |
24 | if not AuxJsonObject.Get(JsonKey, AuxJsonToken) then
25 | Error(ReadingJsonErr);
26 |
27 | if (not AuxJsonToken.IsValue()) and (not AuxJsonToken.IsArray()) then
28 | Error(ReadingJsonErr);
29 |
30 | if AuxJsonToken.IsValue() then
31 | JsonText := AuxJsonToken.AsValue().AsText()
32 | else
33 | AuxJsonToken.AsArray().WriteTo(JsonText);
34 |
35 | exit(JsonText.Trim());
36 | end;
37 |
38 | var
39 | ReadingJsonErr: Label 'Error reading JSON response.';
40 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | ## Contribute
2 | ## What to contribute
3 | Any bug fixes or new features are welcome in this project. PRs will be accepted after review and testing without promises or dates. They should be associated with an Issues. There may or may not be a previous conversation, either in discussions or in the issue itself.
4 |
5 | ## How to contribute
6 | In case you want to contribute code to this project, you must first establish what type of contribution it is and create an issue according to its type:
7 |
8 | ### A small bug fix. 🐛
9 | In this case the review will be lighter, only the functionality will be tested and tests will be added or modified if necessary.
10 |
11 | ### New capabilities or major change implementations. 🥇
12 | The process will be slower.
13 | - There should be a prior conversation about the functionality you want to implement, either through [discussions](https://github.com/NovoaDev/Dependency-Graph-BCExt/discussions). or within an issue. The point of this is that you don't waste time writing code, which then may not be accepted.
14 | - Fork the repository.
15 | - Create a Pull Request with the changes.
16 | - The next step is to wait for the approval of the PR, it will be evaluated and approved or rejected.
17 |
18 | Feel free to contact any of the maintainers if you have any questions about the contribution process.
19 |
--------------------------------------------------------------------------------
/Dependency-Graph/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "CDX - Development Sandbox",
6 | "request": "launch",
7 | "type": "al",
8 | "environmentType": "Sandbox",
9 | "environmentName": "DEVELOPMENT",
10 | "startupObjectId": 80800,
11 | "startupObjectType": "Page",
12 | "breakOnError": "All",
13 | "launchBrowser": true,
14 | "enableLongRunningSqlStatements": true,
15 | "enableSqlInformationDebugger": true,
16 | "tenant": "6de27021-dcdb-4539-b0e1-c5ecb27c4e48"
17 | },
18 | {
19 | "name": "Docker - DependencyGraph",
20 | "request": "launch",
21 | "type": "al",
22 | "environmentType": "OnPrem",
23 | "server": "http://DependencyGraph",
24 | "serverInstance": "BC",
25 | "authentication": "UserPassword",
26 | "startupObjectId": 22,
27 | "startupObjectType": "Page",
28 | "breakOnError": "All",
29 | "launchBrowser": true,
30 | "enableLongRunningSqlStatements": true,
31 | "enableSqlInformationDebugger": true,
32 | "tenant": "default",
33 | "usePublicURLFromServer": true
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/Dependency-Graph.Test/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "CDX - Development Sandbox",
6 | "request": "launch",
7 | "type": "al",
8 | "environmentType": "Sandbox",
9 | "environmentName": "DEVELOPMENT",
10 | "startupObjectId": 80800,
11 | "startupObjectType": "Page",
12 | "breakOnError": "All",
13 | "launchBrowser": true,
14 | "enableLongRunningSqlStatements": true,
15 | "enableSqlInformationDebugger": true,
16 | "tenant": "6de27021-dcdb-4539-b0e1-c5ecb27c4e48"
17 | },
18 | {
19 | "name": "Docker - DependencyGraph",
20 | "request": "launch",
21 | "type": "al",
22 | "environmentType": "OnPrem",
23 | "server": "http://DependencyGraph",
24 | "serverInstance": "BC",
25 | "authentication": "UserPassword",
26 | "startupObjectId": 22,
27 | "startupObjectType": "Page",
28 | "breakOnError": "All",
29 | "launchBrowser": true,
30 | "enableLongRunningSqlStatements": true,
31 | "enableSqlInformationDebugger": true,
32 | "tenant": "default",
33 | "usePublicURLFromServer": true
34 | }
35 | ]
36 | }
--------------------------------------------------------------------------------
/Dependency-Graph/Test Odata/Test API.http:
--------------------------------------------------------------------------------
1 | @TenantId = {{$processEnv DependencyGraphTenantId}}
2 | @AppId = {{$processEnv DependencyGraphAppId}}
3 | @Secret = {{$processEnv DependencyGraphSecret}}
4 | @Environment = {{$processEnv DependencyGraphEnvironment}}
5 |
6 | # @name AuthResponse
7 | POST https://login.microsoftonline.com/{{TenantId}}/oauth2/v2.0/token
8 | Content-type: application/x-www-form-urlencoded
9 |
10 | grant_type=client_credentials
11 | &client_id={{AppId}}
12 | &client_secret={{Secret}}
13 | &scope=https://api.businesscentral.dynamics.com/.default
14 |
15 | #### Token
16 | @AuthHeader = Bearer {{AuthResponse.response.body.$.access_token}}
17 |
18 | ### Metadata ODataV4 ###
19 | GET https://api.businesscentral.dynamics.com/v2.0/{{TenantId}}/{{Environment}}/ODataV4/$metadata
20 | Authorization: {{AuthHeader}}
21 | ###
22 |
23 | ### Companies ###
24 | GET https://api.businesscentral.dynamics.com/v2.0/{{TenantId}}/{{Environment}}/api/v2.0/companies
25 | Authorization: {{AuthHeader}}
26 | ###
27 |
28 | ### Get All extensiones
29 | @CompanyID = {{$processEnv DependencyGraphCompanyID}}
30 | GET https://api.businesscentral.dynamics.com/v2.0/{{Environment}}/api/microsoft/automation/v2.0/companies({{CompanyID}})/extensions
31 | Authorization: {{AuthHeader}}
32 |
33 | ### Get All extensiones with filter
34 | GET https://api.businesscentral.dynamics.com/v2.0/{{Environment}}/api/microsoft/automation/v2.0/companies({{CompanyID}})/extensions?$filter=publisher ne 'Microsoft'
35 | Authorization: {{AuthHeader}}
36 |
--------------------------------------------------------------------------------
/Dependency-Graph/src/Markdown/MarkdownViewerANJ.Page.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Page "MarkdownViewer_ANJ" (ID 80804).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | page 80804 MarkdownViewer_ANJ
6 | {
7 | ApplicationArea = All;
8 | Caption = 'Markdown Viewer';
9 | Extensible = false;
10 | PageType = CardPart;
11 | UsageCategory = None;
12 |
13 | layout
14 | {
15 | area(Content)
16 | {
17 | usercontrol(MarkdownViewer; MarkdownViewer_ANJ)
18 | {
19 | }
20 | }
21 | }
22 | actions
23 | {
24 | area(Processing)
25 | {
26 | action(GenerateMarkdown)
27 | {
28 | ApplicationArea = All;
29 | Caption = 'Display dependency graph';
30 | Image = View;
31 | ToolTip = 'Executes the Display dependency graph action.';
32 |
33 | trigger OnAction()
34 | begin
35 | Page.Run(Page::MarkdownViewerFull_ANJ);
36 | end;
37 | }
38 | }
39 | }
40 |
41 | trigger OnAfterGetCurrRecord()
42 | begin
43 | CurrPage.MarkdownViewer.Draw(Markdown);
44 | end;
45 |
46 | ///
47 | /// SetMarkdown.
48 | ///
49 | /// Text.
50 | internal procedure SetMarkdown(AuxMarkdown: Text)
51 | begin
52 | Markdown := AuxMarkdown;
53 | end;
54 |
55 | var
56 | Markdown: Text;
57 | }
--------------------------------------------------------------------------------
/Dependency-Graph.Test/src/Test/ExpectedValuesANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit ExpectedValues_ANJ (ID 99994).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 99994 ExpectedValues_ANJ
6 | {
7 | Access = Internal;
8 |
9 | ///
10 | /// GetExpectedMarkdownText.
11 | ///
12 | /// Return value of type Text.
13 | procedure GetExpectedMarkdownText(): Text
14 | var
15 | ExpectedValueTextBuilder: TextBuilder;
16 | begin
17 | ExpectedValueTextBuilder.AppendLine(Header1Lbl);
18 | ExpectedValueTextBuilder.AppendLine(Header2Lbl);
19 | ExpectedValueTextBuilder.AppendLine(ExpectedRelationLbl);
20 | ExpectedValueTextBuilder.AppendLine();
21 | ExpectedValueTextBuilder.AppendLine(FooterLbl);
22 |
23 | exit(ExpectedValueTextBuilder.ToText());
24 | end;
25 |
26 | ///
27 | /// GetExpectedMarkdownMermaidText.
28 | ///
29 | /// Return value of type Text.
30 | procedure GetExpectedMarkdownMermaidText(): Text
31 | var
32 | ExpectedValueTextBuilder: TextBuilder;
33 | begin
34 | ExpectedValueTextBuilder.AppendLine(Header2Lbl);
35 | ExpectedValueTextBuilder.AppendLine(ExpectedRelationLbl);
36 |
37 | exit(ExpectedValueTextBuilder.ToText());
38 | end;
39 |
40 | var
41 | ExpectedRelationLbl: Label 'E1[Take Order Sample] --> E2[DependencyGraph]';
42 | FooterLbl: Label '```';
43 | Header1Lbl: Label '```mermaid';
44 | Header2Lbl: Label 'graph BT';
45 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/Base/MarkdownFactboxANJ.Page.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Page MarkdownFactbox_ANJ (ID 80805).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | page 80805 MarkdownFactbox_ANJ
6 | {
7 | ApplicationArea = All;
8 | Caption = 'Markdown Text';
9 | Editable = false;
10 | Extensible = true;
11 | PageType = CardPart;
12 | UsageCategory = None;
13 |
14 | layout
15 | {
16 | area(Content)
17 | {
18 | field(MarkdownText; MarkdownText)
19 | {
20 | Editable = false;
21 | MultiLine = true;
22 | ShowCaption = false;
23 | }
24 | }
25 | }
26 | actions
27 | {
28 | area(Processing)
29 | {
30 | action(DownloadMarkdown)
31 | {
32 | ApplicationArea = All;
33 | Caption = 'Download Markdown as .md';
34 | Image = Download;
35 | ToolTip = 'Executes the Download Markdown as .md action.';
36 |
37 | trigger OnAction()
38 | begin
39 | MarkdownMgmt.DownloadMarkdown();
40 | end;
41 | }
42 | }
43 | }
44 |
45 | ///
46 | /// SetMarkdownText.
47 | ///
48 | /// Text.
49 | internal procedure SetMarkdownText(AuxMarkdownText: Text)
50 | begin
51 | MarkdownText := AuxMarkdownText;
52 | end;
53 |
54 | var
55 | MarkdownMgmt: Codeunit MarkdownMgmt_ANJ;
56 | MarkdownText: Text;
57 | }
--------------------------------------------------------------------------------
/Dependency-Graph.Test/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "al.enableCodeAnalysis": true,
3 | "al.backgroundCodeAnalysis": "Project",
4 | "al.codeAnalyzers": [
5 | "${CodeCop}",
6 | "${UICop}",
7 | "${PerTenantExtensionCop}",
8 | "${AppSourceCop}",
9 | "${analyzerFolder}BusinessCentral.LinterCop.dll"
10 | ],
11 | "al.enableCodeActions": true,
12 | "al.ruleSetPath": "./.vscode/ruleset.json",
13 | "CRS.FileNamePattern": "..al",
14 | "CRS.FileNamePatternExtensions": "..al",
15 | "CRS.FileNamePatternPageCustomizations": "..al",
16 | "CRS.OnSaveAlFileAction": "DoNothing",
17 | "CRS.RenameWithGit": true,
18 | "editor.formatOnSave": true,
19 | "CRS.ObjectNameSuffix": "_ANJ",
20 | "rest-client.rememberCookiesForSubsequentRequests": false,
21 | "editor.codeActionsOnSave": {
22 | "source.fixAll.al": "explicit"
23 | },
24 | "alOutline.codeActionsOnSave": [
25 | "SortProperties",
26 | "SortVariables",
27 | "FormatDocument"
28 | ],
29 | "al-xml-doc.CheckObjectDocumentationInformationLevel": "Error",
30 | "al-xml-doc.CheckProcedureDocumentationInformationLevel": "Error",
31 | "al-xml-doc.CheckProcedureDocumentationForType": [
32 | "Global Procedures",
33 | "Local Procedures",
34 | "Internal Procedures",
35 | "Protected Procedures",
36 | "Event Publisher"
37 | ],
38 | "al-xml-doc.CheckProcedureDocumentationForAccessLevel": [
39 | "Public",
40 | "Internal",
41 | "Local"
42 | ]
43 | }
--------------------------------------------------------------------------------
/Dependency-Graph/DependencyGraphANJ.permissionset.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// permissionset DependencyGraph_ANJ (ID 80800).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | permissionset 80800 DependencyGraph_ANJ
6 | {
7 | Access = Public;
8 | Assignable = true;
9 | Caption = 'DependencyGraph', Locked = true;
10 | Permissions = tabledata DependencyGraphSetup_ANJ = RIMD,
11 | table DependencyGraphSetup_ANJ = X,
12 | codeunit CircleIMP_ANJ = X,
13 | codeunit GenerateFigure_ANJ = X,
14 | codeunit RhombusIMP_ANJ = X,
15 | codeunit RoundRectangleIMP_ANJ = X,
16 | codeunit SquareRectangleIMP_ANJ = X,
17 | page DependencyGraphSetup_ANJ = X,
18 | tabledata Extensions_ANJ = RIMD,
19 | table Extensions_ANJ = X,
20 | page Extensions_ANJ = X,
21 | codeunit GenerateExtensionTable_ANJ = X,
22 | page ShowInGraph_ANJ = X,
23 | codeunit NumberSequenceMgmt_ANJ = X,
24 | tabledata Relations_ANJ = RIMD,
25 | table Relations_ANJ = X,
26 | page Relations_ANJ = X,
27 | codeunit GenerateTables_ANJ = X,
28 | codeunit GenerateRelationsTable_ANJ = X,
29 | codeunit MarkdownMgmt_ANJ = X,
30 | page MarkdownViewer_ANJ = X,
31 | codeunit DependencyGraphFacade_ANJ = X,
32 | page MarkdownFactbox_ANJ = X,
33 | page MarkdownViewerFull_ANJ = X,
34 | codeunit CleanTemporaryTables_ANJ = X,
35 | codeunit JSONMethods_ANJ = X,
36 | codeunit WSAndMDInfoImp_ANJ = X,
37 | codeunit DefaultFigureIMP_ANJ = X,
38 | codeunit DefaultFillProcessTabImp_ANJ = X,
39 | codeunit DataUpgrades_ANJ = X,
40 | codeunit UpgradeCodeunit_ANJ = X;
41 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/ExtensionTable/ExtensionsANJ.Page.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Page Extensions_ANJ (ID 80801).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | page 80801 Extensions_ANJ
6 | {
7 | ApplicationArea = All;
8 | Caption = 'Extensions';
9 | DeleteAllowed = false;
10 | Extensible = true;
11 | InsertAllowed = false;
12 | PageType = ListPart;
13 | SourceTable = Extensions_ANJ;
14 | UsageCategory = None;
15 |
16 | layout
17 | {
18 | area(Content)
19 | {
20 | repeater(General)
21 | {
22 | field(Name; Rec.Name)
23 | {
24 | Editable = false;
25 | }
26 | field(DisplayName; Rec.DisplayName)
27 | {
28 | trigger OnValidate()
29 | begin
30 | DoGenerateRelationsTable();
31 | end;
32 | }
33 | field(Publisher; Rec.Publisher)
34 | {
35 | Editable = false;
36 | }
37 | field(PublishedAs; Rec.PublishedAs)
38 | {
39 | Editable = false;
40 | }
41 | field(ShowInGraph; Rec.ShowInGraph)
42 | {
43 |
44 | trigger OnValidate()
45 | begin
46 | DoGenerateRelationsTable();
47 | end;
48 | }
49 | }
50 | }
51 | }
52 |
53 | ///
54 | /// DoGenerateRelationsTable.
55 | ///
56 | local procedure DoGenerateRelationsTable()
57 | begin
58 | CurrPage.SaveRecord();
59 | Rec.UpdateRelationTable();
60 | CurrPage.Update(false);
61 | end;
62 | }
--------------------------------------------------------------------------------
/Dependency-Graph.Test/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "d7bcd883-c9cb-4e98-a771-a3b2ba51444e",
3 | "name": "Dependency-Graph.Test",
4 | "publisher": "ANJ",
5 | "version": "2.9.0.0",
6 | "brief": "App to test Dependency-Graph",
7 | "description": "App to test Dependency-Graph",
8 | "privacyStatement": "https://github.com/NovoaDev/Dependency-Graph-BCExt",
9 | "EULA": "https://github.com/NovoaDev/Dependency-Graph-BCExt",
10 | "help": "https://github.com/NovoaDev/Dependency-Graph-BCExt",
11 | "url": "https://github.com/NovoaDev/Dependency-Graph-BCExt",
12 | "logo": "res/logo.png",
13 | "contextSensitiveHelpUrl": "https://github.com/NovoaDev/Dependency-Graph-BCExt",
14 | "dependencies": [
15 | {
16 | "publisher": "Microsoft",
17 | "name": "Library Assert",
18 | "id": "dd0be2ea-f733-4d65-bb34-a28f4624fb14",
19 | "version": "23.0.0.0"
20 | },
21 | {
22 | "id": "5d86850b-0d76-4eca-bd7b-951ad998e997",
23 | "publisher": "Microsoft",
24 | "name": "Tests-TestLibraries",
25 | "version": "23.0.0.0"
26 | },
27 | {
28 | "publisher": "Microsoft",
29 | "name": "Any",
30 | "id": "e7320ebb-08b3-4406-b1ec-b4927d3e280b",
31 | "version": "23.0.0.0"
32 | },
33 | {
34 | "id": "3c9a4c22-42f6-4e8f-8b96-744c7fef331f",
35 | "name": "Dependency-Graph",
36 | "publisher": "ANJ",
37 | "version": "3.6.0.0"
38 | }
39 | ],
40 | "screenshots": [],
41 | "platform": "1.0.0.0",
42 | "application": "23.0.0.0",
43 | "idRanges": [
44 | {
45 | "from": 99990,
46 | "to": 99999
47 | }
48 | ],
49 | "features": [
50 | "NoImplicitWith",
51 | "TranslationFile"
52 | ],
53 | "resourceExposurePolicy": {
54 | "allowDebugging": true,
55 | "allowDownloadingSource": false,
56 | "includeSourceInSymbolFile": false
57 | },
58 | "runtime": "13.0"
59 | }
60 |
--------------------------------------------------------------------------------
/Dependency-Graph/src/Base/ShowInGraphANJ.Page.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Page "ShowInGraph_ANJ" (ID 80802).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | page 80802 ShowInGraph_ANJ
6 | {
7 | ApplicationArea = All;
8 | Caption = 'Show In Dependency Graph';
9 | Extensible = true;
10 | PageType = Document;
11 | UsageCategory = None;
12 |
13 | layout
14 | {
15 | area(Content)
16 | {
17 | part(Extensions_ANJ; Extensions_ANJ)
18 | {
19 | UpdatePropagation = Both;
20 | }
21 | part(Relations_ANJ; Relations_ANJ)
22 | {
23 | UpdatePropagation = Both;
24 | }
25 | }
26 | area(FactBoxes)
27 | {
28 | part(MarkdownFactbox_ANJ; MarkdownFactbox_ANJ)
29 | {
30 | }
31 | part(MarkdownViewer_ANJ; MarkdownViewer_ANJ)
32 | {
33 | }
34 | }
35 | }
36 |
37 | actions
38 | {
39 | area(Processing)
40 | {
41 | action(GenTables)
42 | {
43 | ApplicationArea = All;
44 | Caption = 'Generate data';
45 | Image = NewSparkle;
46 | Promoted = true;
47 | PromotedCategory = Process;
48 | PromotedOnly = true;
49 | ToolTip = 'Executes the Generate data action.';
50 |
51 | trigger OnAction()
52 | begin
53 | DependencyGraphFacade.GenerateAllTables(false);
54 | end;
55 | }
56 | }
57 | }
58 |
59 | trigger OnAfterGetCurrRecord()
60 | var
61 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
62 | begin
63 | DependencyGraphFacade.GenerateGraph();
64 | CurrPage.MarkdownFactbox_ANJ.Page.SetMarkdownText(DependencyGraphFacade.GetMarkdownText(DependencyGraphSetup.FieldNo(Markdown)));
65 | CurrPage.MarkdownViewer_ANJ.Page.SetMarkdown(DependencyGraphFacade.GetMarkdownText(DependencyGraphSetup.FieldNo(MarkdownMermaid)));
66 | end;
67 |
68 | var
69 | DependencyGraphFacade: Codeunit DependencyGraphFacade_ANJ;
70 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/ExtensionTable/NumberSequenceMgmtANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "NumberSequenceMgmt_ANJ" (ID 80806).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80806 NumberSequenceMgmt_ANJ
6 | {
7 | Access = Public;
8 |
9 | ///
10 | /// Initialize
11 | ///
12 | internal procedure Initialize()
13 | begin
14 | if NumberSequence.Exists(NumberSequenceNameTok, true) then
15 | NumberSequence.Delete(NumberSequenceNameTok, true);
16 |
17 | NumberSequence.Insert(NumberSequenceNameTok, 0001, 1, true);
18 | end;
19 |
20 | ///
21 | /// GetNextNo.
22 | ///
23 | /// Return value of type Text.
24 | internal procedure GetNextNo() NewIdentity: Text
25 | var
26 | IsHandled: Boolean;
27 | begin
28 | OnBeforeBuildIdentity(IsHandled);
29 | NewIdentity := DoBuildIdentity(IsHandled);
30 | OnAfterBuildIdentity(NewIdentity);
31 | end;
32 |
33 | ///
34 | /// DoBuildIdentity.
35 | ///
36 | /// Boolean.
37 | /// Return value of type Text.
38 | local procedure DoBuildIdentity(IsHandled: Boolean): Text
39 | var
40 | NewIdentity: Text;
41 | begin
42 | if IsHandled then
43 | exit;
44 |
45 | NewIdentity := StrSubstNo(IdentityLbl, Format(NumberSequence.Next(NumberSequenceNameTok, true)));
46 | exit(NewIdentity);
47 | end;
48 |
49 | ///
50 | /// OnBeforeBuildIdentity.
51 | ///
52 | /// VAR Boolean.
53 | [IntegrationEvent(false, false)]
54 | local procedure OnBeforeBuildIdentity(var IsHandled: Boolean)
55 | begin
56 | end;
57 |
58 | ///
59 | /// OnAfterBuildIdentity.
60 | ///
61 | /// VAR Text.
62 | [IntegrationEvent(false, false)]
63 | local procedure OnAfterBuildIdentity(var NewIdentity: Text)
64 | begin
65 | end;
66 |
67 | var
68 | IdentityLbl: Label 'E%1', Comment = 'Placeholder %1 for the identity label';
69 | NumberSequenceNameTok: Label 'DependencyGraph', Locked = true;
70 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Dependency Graph
2 | Small tool designed to create a graph showing installed extensions and their respective dependencies, all from the **Business Central** environment in which the extension is running. In addition, it allows you to preview and download the generated text to easily add it to the client's documentation in Markdown format.
3 | **Usage video:**
4 | https://youtu.be/PsgThQVQAw4
5 | Very proud to be on Stefan Maron's Open Source Business Central Applications list:
6 | https://stefanmaron.com/opensourcealprojects/
7 | **PS:**
8 | Although there are numerous more advanced tools available, this one has been created for the simple purpose of practice, but it could prove useful for other users.
9 |
10 | ## Usage
11 | 
12 |
13 | ## Previous configuration
14 | ### Azure
15 | #### Create App registrations
16 | **Supported account types:**
17 | Organization accounts only.
18 | **Redirect URI:**
19 | "Web" https://businesscentral.dynamics.com/OAuthLanding.htm
20 | 
21 |
22 | #### Create Client Secret
23 | 
24 |
25 | #### Assign API permissions (With Administrator consent)
26 | API. ReadWrite.All
27 | Automation.ReadWrite.All
28 | 
29 |
30 | ### Business Central
31 | Create new record in **Azure Active Directory Applications** Indicating the application ID created in Azure.
32 | Add **D365 AUTOMATION** and **EXTEN MGT. -ADMIN** to user permission sets.
33 | Change status to **Enabled**
34 | **Grant consent**
35 | 
36 |
37 | ## Dependency Graph Settings page
38 | 
39 |
40 | - App registrations - **Azure**
41 | Configuration of the **Azure** Registry ID required for operation.
42 |
43 | - App shapes inside the diagram
44 | Select which shape an application will be represented by its scope.
45 |
46 | - Include when generating the graph
47 | Features to include for chart generation:
48 | - Microsoft applications.
49 | - Link text between dependencies.
50 |
51 | - Last time it was generated
52 | Historical data.
53 |
54 | - Dependency graph
55 | Action to access the data generation page for the graph.
56 |
--------------------------------------------------------------------------------
/Dependency-Graph.Test/src/Test/NumberSequenceTestANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit NumberSequenceTest_ANJ (ID 99990).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 99990 NumberSequenceTest_ANJ
6 | {
7 | Access = Public;
8 | Subtype = Test;
9 |
10 | ///
11 | /// TestNumberSequence.
12 | ///
13 | [Test]
14 | procedure TestNumberSequence()
15 | var
16 | FirstRequest: Text;
17 | SecondRequest: Text;
18 | ThirdRequest: Text;
19 | begin
20 | // [Scenario]
21 | // Verify that it is initialized and a number series is created correctly.
22 |
23 | // [Given] Setup:
24 | LibraryLowerPermissions.SetO365BusFull();
25 |
26 | // [When] Exercise:
27 | CheckInitializeAndCreateSomeNumberSeries(FirstRequest, SecondRequest, ThirdRequest);
28 |
29 | // [Then] Verify:
30 | LibraryAssert.AreEqual(FirstRequest, FirstRequestLbl, NumberSequenceErr);
31 | LibraryAssert.AreEqual(SecondRequest, SecondRequestLbl, NumberSequenceErr);
32 | LibraryAssert.AreEqual(ThirdRequest, ThirdRequestLbl, NumberSequenceErr);
33 | end;
34 |
35 | ///
36 | /// CheckInitializeAndCreateSomeNumberSeries.
37 | ///
38 | /// VAR Text.
39 | /// VAR Text.
40 | /// VAR Text.
41 | local procedure CheckInitializeAndCreateSomeNumberSeries(var FirstRequest: Text; var SecondRequest: Text; var ThirdRequest: Text)
42 | begin
43 | DependencyGraphFacadeANJ.InitializeNumberSequence();
44 | FirstRequest := DependencyGraphFacadeANJ.GetNextNumberSequence();
45 | SecondRequest := DependencyGraphFacadeANJ.GetNextNumberSequence();
46 | ThirdRequest := DependencyGraphFacadeANJ.GetNextNumberSequence();
47 | end;
48 |
49 | var
50 | DependencyGraphFacadeANJ: Codeunit DependencyGraphFacade_ANJ;
51 | LibraryLowerPermissions: Codeunit "Library - Lower Permissions";
52 | LibraryAssert: Codeunit System.TestLibraries.Utilities."Library Assert";
53 | FirstRequestLbl: Label 'E1';
54 | NumberSequenceErr: Label 'The number secuence does not correspond to the expected one';
55 | SecondRequestLbl: Label 'E2';
56 | ThirdRequestLbl: Label 'E3';
57 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/Relations/RelationsANJ.Table.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Table Relations_ANJ (ID 80802).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | table 80802 Relations_ANJ
6 | {
7 | Access = Public;
8 | Caption = 'Relations';
9 | DataClassification = CustomerContent;
10 | DrillDownPageId = Relations_ANJ;
11 | Extensible = true;
12 | LookupPageId = Relations_ANJ;
13 |
14 | fields
15 | {
16 | field(1; RelationNo; Integer)
17 | {
18 | AllowInCustomizations = Never;
19 | Caption = 'Relation No.';
20 | }
21 | field(2; SourceAppID; Guid)
22 | {
23 | AllowInCustomizations = Never;
24 | Caption = 'Source App ID';
25 | }
26 | field(3; SourceAppName; Text[2048])
27 | {
28 | CalcFormula = lookup(Extensions_ANJ.DisplayName where(AppID = field(SourceAppID)));
29 | Caption = 'Source App Name';
30 | Editable = false;
31 | FieldClass = FlowField;
32 | ToolTip = 'Specifies the value of the Source App Name field.';
33 | }
34 | field(4; DestinationAppID; Guid)
35 | {
36 | AllowInCustomizations = Never;
37 | Caption = 'Destination App ID';
38 | }
39 | field(5; DestinationAppName; Text[2048])
40 | {
41 | CalcFormula = lookup(Extensions_ANJ.DisplayName where(AppID = field(DestinationAppID)));
42 | Caption = 'Destination App Name';
43 | Editable = false;
44 | FieldClass = FlowField;
45 | ToolTip = 'Specifies the value of the Destination App Name field.';
46 | }
47 | field(6; LinkText; Text[2048])
48 | {
49 | Caption = 'Link Text';
50 | ToolTip = 'Specifies the value of the Link Text field.';
51 | }
52 | field(7; ShowInGraph; Boolean)
53 | {
54 | Caption = 'Show In Graph';
55 | ToolTip = 'Specifies the value of the Show In Graph field.';
56 | }
57 | }
58 | keys
59 | {
60 | key(Key1; RelationNo)
61 | {
62 | Clustered = true;
63 | }
64 | }
65 | fieldgroups
66 | {
67 | fieldgroup(DropDown; SourceAppName, DestinationAppName)
68 | {
69 | }
70 | fieldgroup(Brick; SourceAppName, DestinationAppName)
71 | {
72 | }
73 | }
74 | }
--------------------------------------------------------------------------------
/.github/workflows/DeployReferenceDocumentation.yaml:
--------------------------------------------------------------------------------
1 | name: ' Deploy Reference Documentation'
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | permissions:
7 | actions: read
8 | contents: read
9 | id-token: write
10 | pages: write
11 |
12 | defaults:
13 | run:
14 | shell: powershell
15 |
16 | env:
17 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
18 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
19 |
20 | jobs:
21 | DeployALDoc:
22 | runs-on: [ windows-latest ]
23 | name: Deploy Reference Documentation
24 | environment:
25 | name: github-pages
26 | url: ${{ steps.deployment.outputs.page_url }}
27 | steps:
28 | - name: Checkout
29 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
30 |
31 | - name: Initialize the workflow
32 | id: init
33 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
34 | with:
35 | shell: powershell
36 |
37 | - name: Read settings
38 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
39 | with:
40 | shell: powershell
41 |
42 | - name: Determine Deployment Environments
43 | id: DetermineDeploymentEnvironments
44 | uses: microsoft/AL-Go-Actions/DetermineDeploymentEnvironments@v7.0
45 | env:
46 | GITHUB_TOKEN: ${{ github.token }}
47 | with:
48 | shell: powershell
49 | getEnvironments: 'github-pages'
50 | type: 'Publish'
51 |
52 | - name: Setup Pages
53 | if: steps.DetermineDeploymentEnvironments.outputs.deployALDocArtifact == 1
54 | uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5.0.0
55 |
56 | - name: Build Reference Documentation
57 | uses: microsoft/AL-Go-Actions/BuildReferenceDocumentation@v7.0
58 | with:
59 | shell: powershell
60 | artifacts: 'latest'
61 |
62 | - name: Upload pages artifact
63 | uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3.0.1
64 | with:
65 | path: ".aldoc/_site/"
66 |
67 | - name: Deploy to GitHub Pages
68 | if: steps.DetermineDeploymentEnvironments.outputs.deployALDocArtifact == 1
69 | id: deployment
70 | uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5
71 |
72 | - name: Finalize the workflow
73 | if: always()
74 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
75 | env:
76 | GITHUB_TOKEN: ${{ github.token }}
77 | with:
78 | shell: powershell
79 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
80 | currentJobContext: ${{ toJson(job) }}
81 |
--------------------------------------------------------------------------------
/Dependency-Graph/src/Base/CleanTemporaryTablesANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "CleanTemporaryTables_ANJ" (ID 80811).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80811 CleanTemporaryTables_ANJ
6 | {
7 | Access = Public;
8 | Permissions = tabledata Extensions_ANJ = RD, tabledata Relations_ANJ = RD;
9 |
10 | ///
11 | /// Clean.
12 | ///
13 | /// Boolean.
14 | /// Boolean.
15 | internal procedure Clean(ExtensionsTable: Boolean; RelationsTable: Boolean)
16 | var
17 | IsHandled: Boolean;
18 | begin
19 | OnBeforeClean(ExtensionsTable, RelationsTable, IsHandled);
20 | DoClean(ExtensionsTable, RelationsTable, IsHandled);
21 | OnAfterClean(ExtensionsTable, RelationsTable);
22 | end;
23 |
24 | ///
25 | /// DoClean.
26 | ///
27 | /// Boolean.
28 | /// Boolean.
29 | /// Boolean.
30 | local procedure DoClean(ExtensionsTable: Boolean; RelationsTable: Boolean; IsHandled: Boolean)
31 | begin
32 | if IsHandled then
33 | exit;
34 |
35 | if ExtensionsTable then
36 | CleanExtensionsTable();
37 |
38 | if RelationsTable then
39 | CleanRelationsTable();
40 | end;
41 |
42 | ///
43 | /// CleanRelationsTable.
44 | ///
45 | local procedure CleanExtensionsTable()
46 | var
47 | Extensions: Record Extensions_ANJ;
48 | begin
49 | if not Extensions.IsEmpty() then
50 | Extensions.DeleteAll(true);
51 | end;
52 |
53 | ///
54 | /// CleanRelationsTable.
55 | ///
56 | local procedure CleanRelationsTable()
57 | var
58 | Relations: Record Relations_ANJ;
59 | begin
60 | if not Relations.IsEmpty() then
61 | Relations.DeleteAll(true);
62 | end;
63 |
64 | ///
65 | /// OnBeforeClean.
66 | ///
67 | /// VAR Boolean.
68 | /// VAR Boolean.
69 | /// VAR Boolean.
70 | [IntegrationEvent(false, false)]
71 | local procedure OnBeforeClean(var ExtensionsTable: Boolean; var RelationsTable: Boolean; var IsHandled: Boolean)
72 | begin
73 | end;
74 |
75 | ///
76 | /// OnAfterClean.
77 | ///
78 | /// Boolean.
79 | /// Boolean.
80 | [IntegrationEvent(false, false)]
81 | local procedure OnAfterClean(ExtensionsTable: Boolean; RelationsTable: Boolean)
82 | begin
83 | end;
84 | }
--------------------------------------------------------------------------------
/.github/workflows/AddExistingAppOrTestApp.yaml:
--------------------------------------------------------------------------------
1 | name: 'Add existing app or test app'
2 |
3 | run-name: "Add existing app or test app in [${{ github.ref_name }}]"
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | project:
9 | description: Project name if the repository is setup for multiple projects
10 | required: false
11 | default: '.'
12 | url:
13 | description: Direct Download Url of .app or .zip file
14 | required: true
15 | directCommit:
16 | description: Direct Commit?
17 | type: boolean
18 | default: false
19 | useGhTokenWorkflow:
20 | description: Use GhTokenWorkflow for PR/Commit?
21 | type: boolean
22 | default: false
23 |
24 | permissions:
25 | actions: read
26 | contents: write
27 | id-token: write
28 | pull-requests: write
29 |
30 | defaults:
31 | run:
32 | shell: powershell
33 |
34 | env:
35 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
36 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
37 |
38 | jobs:
39 | AddExistingAppOrTestApp:
40 | needs: [ ]
41 | runs-on: [ windows-latest ]
42 | steps:
43 | - name: Dump Workflow Information
44 | uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@v7.0
45 | with:
46 | shell: powershell
47 |
48 | - name: Checkout
49 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
50 |
51 | - name: Initialize the workflow
52 | id: init
53 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
54 | with:
55 | shell: powershell
56 |
57 | - name: Read settings
58 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
59 | with:
60 | shell: powershell
61 |
62 | - name: Read secrets
63 | id: ReadSecrets
64 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
65 | with:
66 | shell: powershell
67 | gitHubSecrets: ${{ toJson(secrets) }}
68 | getSecrets: 'TokenForPush'
69 | useGhTokenWorkflowForPush: '${{ github.event.inputs.useGhTokenWorkflow }}'
70 |
71 | - name: Add existing app
72 | uses: microsoft/AL-Go-Actions/AddExistingApp@v7.0
73 | with:
74 | shell: powershell
75 | token: ${{ steps.ReadSecrets.outputs.TokenForPush }}
76 | project: ${{ github.event.inputs.project }}
77 | url: ${{ github.event.inputs.url }}
78 | directCommit: ${{ github.event.inputs.directCommit }}
79 |
80 | - name: Finalize the workflow
81 | if: always()
82 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
83 | env:
84 | GITHUB_TOKEN: ${{ github.token }}
85 | with:
86 | shell: powershell
87 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
88 | currentJobContext: ${{ toJson(job) }}
89 |
--------------------------------------------------------------------------------
/.github/workflows/IncrementVersionNumber.yaml:
--------------------------------------------------------------------------------
1 | name: ' Increment Version Number'
2 |
3 | run-name: "Increment Version Number in [${{ github.ref_name }}]"
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | projects:
9 | description: Comma-separated list of project name patterns if the repository is setup for multiple projects (default is * for all projects)
10 | required: false
11 | default: '*'
12 | versionNumber:
13 | description: New Version Number in main branch. Use Major.Minor (optionally add .Build for versioningstrategy 3) for absolute change, or +1, +0.1 (or +0.0.1 for versioningstrategy 3) incremental change.
14 | required: false
15 | default: ''
16 | skipUpdatingDependencies:
17 | description: Skip updating dependency version numbers in all apps.
18 | type: boolean
19 | default: false
20 | directCommit:
21 | description: Direct Commit?
22 | type: boolean
23 | default: false
24 | useGhTokenWorkflow:
25 | description: Use GhTokenWorkflow for PR/Commit?
26 | type: boolean
27 | default: false
28 |
29 | defaults:
30 | run:
31 | shell: powershell
32 |
33 | env:
34 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
35 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
36 |
37 | jobs:
38 | IncrementVersionNumber:
39 | needs: [ ]
40 | runs-on: [ windows-latest ]
41 | permissions:
42 | actions: read
43 | contents: write
44 | id-token: write
45 | pull-requests: write
46 | steps:
47 | - name: Dump Workflow Information
48 | uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@v7.0
49 | with:
50 | shell: powershell
51 |
52 | - name: Checkout
53 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
54 |
55 | - name: Initialize the workflow
56 | id: init
57 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
58 | with:
59 | shell: powershell
60 |
61 | - name: Read settings
62 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
63 | with:
64 | shell: powershell
65 |
66 | - name: Read secrets
67 | id: ReadSecrets
68 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
69 | with:
70 | shell: powershell
71 | gitHubSecrets: ${{ toJson(secrets) }}
72 | getSecrets: 'TokenForPush'
73 | useGhTokenWorkflowForPush: '${{ github.event.inputs.useGhTokenWorkflow }}'
74 |
75 | - name: Increment Version Number
76 | uses: microsoft/AL-Go-Actions/IncrementVersionNumber@v7.0
77 | with:
78 | shell: powershell
79 | token: ${{ steps.ReadSecrets.outputs.TokenForPush }}
80 | projects: ${{ github.event.inputs.projects }}
81 | versionNumber: ${{ github.event.inputs.versionNumber }}
82 | skipUpdatingDependencies: ${{ github.event.inputs.skipUpdatingDependencies }}
83 | directCommit: ${{ github.event.inputs.directCommit }}
84 |
85 | - name: Finalize the workflow
86 | if: always()
87 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
88 | env:
89 | GITHUB_TOKEN: ${{ github.token }}
90 | with:
91 | shell: powershell
92 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
93 | currentJobContext: ${{ toJson(job) }}
94 |
--------------------------------------------------------------------------------
/.github/workflows/CreateApp.yaml:
--------------------------------------------------------------------------------
1 | name: 'Create a new app'
2 |
3 | run-name: "Create a new app in [${{ github.ref_name }}]"
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | project:
9 | description: Project name if the repository is setup for multiple projects
10 | required: false
11 | default: '.'
12 | name:
13 | description: Name
14 | required: true
15 | publisher:
16 | description: Publisher
17 | required: true
18 | idrange:
19 | description: ID range (from..to)
20 | required: true
21 | sampleCode:
22 | description: Include Sample code?
23 | type: boolean
24 | default: true
25 | directCommit:
26 | description: Direct Commit?
27 | type: boolean
28 | default: false
29 | useGhTokenWorkflow:
30 | description: Use GhTokenWorkflow for PR/Commit?
31 | type: boolean
32 | default: false
33 |
34 | permissions:
35 | actions: read
36 | contents: write
37 | id-token: write
38 | pull-requests: write
39 |
40 | defaults:
41 | run:
42 | shell: powershell
43 |
44 | env:
45 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
46 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
47 |
48 | jobs:
49 | CreateApp:
50 | needs: [ ]
51 | runs-on: [ windows-latest ]
52 | steps:
53 | - name: Dump Workflow Information
54 | uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@v7.0
55 | with:
56 | shell: powershell
57 |
58 | - name: Checkout
59 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
60 |
61 | - name: Initialize the workflow
62 | id: init
63 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
64 | with:
65 | shell: powershell
66 |
67 | - name: Read settings
68 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
69 | with:
70 | shell: powershell
71 | get: type
72 |
73 | - name: Read secrets
74 | id: ReadSecrets
75 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
76 | with:
77 | shell: powershell
78 | gitHubSecrets: ${{ toJson(secrets) }}
79 | getSecrets: 'TokenForPush'
80 | useGhTokenWorkflowForPush: '${{ github.event.inputs.useGhTokenWorkflow }}'
81 |
82 | - name: Creating a new app
83 | uses: microsoft/AL-Go-Actions/CreateApp@v7.0
84 | with:
85 | shell: powershell
86 | token: ${{ steps.ReadSecrets.outputs.TokenForPush }}
87 | project: ${{ github.event.inputs.project }}
88 | type: ${{ env.type }}
89 | name: ${{ github.event.inputs.name }}
90 | publisher: ${{ github.event.inputs.publisher }}
91 | idrange: ${{ github.event.inputs.idrange }}
92 | sampleCode: ${{ github.event.inputs.sampleCode }}
93 | directCommit: ${{ github.event.inputs.directCommit }}
94 |
95 | - name: Finalize the workflow
96 | if: always()
97 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
98 | env:
99 | GITHUB_TOKEN: ${{ github.token }}
100 | with:
101 | shell: powershell
102 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
103 | currentJobContext: ${{ toJson(job) }}
104 |
--------------------------------------------------------------------------------
/.github/workflows/CreateTestApp.yaml:
--------------------------------------------------------------------------------
1 | name: 'Create a new test app'
2 |
3 | run-name: "Create a new test app in [${{ github.ref_name }}]"
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | project:
9 | description: Project name if the repository is setup for multiple projects
10 | required: false
11 | default: '.'
12 | name:
13 | description: Name
14 | required: true
15 | default: '.Test'
16 | publisher:
17 | description: Publisher
18 | required: true
19 | idrange:
20 | description: ID range
21 | required: true
22 | default: '50000..99999'
23 | sampleCode:
24 | description: Include Sample code?
25 | type: boolean
26 | default: true
27 | directCommit:
28 | description: Direct Commit?
29 | type: boolean
30 | default: false
31 | useGhTokenWorkflow:
32 | description: Use GhTokenWorkflow for PR/Commit?
33 | type: boolean
34 | default: false
35 |
36 | permissions:
37 | actions: read
38 | contents: write
39 | id-token: write
40 | pull-requests: write
41 |
42 | defaults:
43 | run:
44 | shell: powershell
45 |
46 | env:
47 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
48 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
49 |
50 | jobs:
51 | CreateTestApp:
52 | needs: [ ]
53 | runs-on: [ windows-latest ]
54 | steps:
55 | - name: Dump Workflow Information
56 | uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@v7.0
57 | with:
58 | shell: powershell
59 |
60 | - name: Checkout
61 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
62 |
63 | - name: Initialize the workflow
64 | id: init
65 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
66 | with:
67 | shell: powershell
68 |
69 | - name: Read settings
70 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
71 | with:
72 | shell: powershell
73 |
74 | - name: Read secrets
75 | id: ReadSecrets
76 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
77 | with:
78 | shell: powershell
79 | gitHubSecrets: ${{ toJson(secrets) }}
80 | getSecrets: 'TokenForPush'
81 | useGhTokenWorkflowForPush: '${{ github.event.inputs.useGhTokenWorkflow }}'
82 |
83 | - name: Creating a new test app
84 | uses: microsoft/AL-Go-Actions/CreateApp@v7.0
85 | with:
86 | shell: powershell
87 | token: ${{ steps.ReadSecrets.outputs.TokenForPush }}
88 | project: ${{ github.event.inputs.project }}
89 | type: 'Test App'
90 | name: ${{ github.event.inputs.name }}
91 | publisher: ${{ github.event.inputs.publisher }}
92 | idrange: ${{ github.event.inputs.idrange }}
93 | sampleCode: ${{ github.event.inputs.sampleCode }}
94 | directCommit: ${{ github.event.inputs.directCommit }}
95 |
96 | - name: Finalize the workflow
97 | if: always()
98 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
99 | env:
100 | GITHUB_TOKEN: ${{ github.token }}
101 | with:
102 | shell: powershell
103 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
104 | currentJobContext: ${{ toJson(job) }}
105 |
--------------------------------------------------------------------------------
/Dependency-Graph.Test/src/Test/GenerateFiguresTestANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "GenerateFiguresTest_ANJ" (ID 99991).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 99991 GenerateFiguresTest_ANJ
6 | {
7 | Access = Public;
8 | Subtype = Test;
9 | TestPermissions = Disabled;
10 |
11 | ///
12 | /// GenerateFigures.
13 | ///
14 | [Test]
15 | procedure GenerateFigures()
16 | var
17 | ScopeDevFigure: Text;
18 | ScopeGlobalFigure: Text;
19 | ScopePTEFigure: Text;
20 | begin
21 | // [Scenario]
22 | // Verify that the figures are being generated according to each of the extension scopes.
23 |
24 | // [Given] Setup:
25 | InitializeDependencyGraphSetup();
26 |
27 | // [When] Exercise:
28 | GetFigureText(ScopeDevFigure, ScopeGlobalFigure, ScopePTEFigure);
29 |
30 | // [Then] Verify:
31 | LibraryAssert.AreEqual(ScopePTEFigure, StrSubstNo(SquareRectangleFigureLbl, IdentityLbl, AppNameLbl), GenerateFigureErr);
32 | LibraryAssert.AreEqual(ScopeGlobalFigure, StrSubstNo(CircleFigureLbl, IdentityLbl, AppNameLbl), GenerateFigureErr);
33 | LibraryAssert.AreEqual(ScopeDevFigure, StrSubstNo(RhombusFigureLbl, IdentityLbl, AppNameLbl), GenerateFigureErr);
34 | end;
35 |
36 | ///
37 | /// InitializeDependencyGraphSetup
38 | ///
39 | local procedure InitializeDependencyGraphSetup()
40 | var
41 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
42 | begin
43 | DependencyGraphSetup.GetInstance();
44 | DependencyGraphSetup.Validate(ScopePTEFigure, Enum::GeometricFigure_ANJ::SquareRectangle);
45 | DependencyGraphSetup.Validate(ScopeGlobalFigure, Enum::GeometricFigure_ANJ::Circle);
46 | DependencyGraphSetup.Validate(ScopeDevFigure, Enum::GeometricFigure_ANJ::Rhombus);
47 | DependencyGraphSetup.Modify(true);
48 | end;
49 |
50 | ///
51 | /// GetFigureText.
52 | ///
53 | /// VAR Text.
54 | /// VAR Text.
55 | /// VAR Text.
56 | local procedure GetFigureText(var ScopeDevFigure: Text; var ScopeGlobalFigure: Text; var ScopePTEFigure: Text)
57 | begin
58 | ScopePTEFigure := DependencyGraphFacadeANJ.GenerateFigures(Enum::ExtensionScope_ANJ::PTE, IdentityLbl, AppNameLbl);
59 | ScopeGlobalFigure := DependencyGraphFacadeANJ.GenerateFigures(Enum::ExtensionScope_ANJ::Global, IdentityLbl, AppNameLbl);
60 | ScopeDevFigure := DependencyGraphFacadeANJ.GenerateFigures(Enum::ExtensionScope_ANJ::Dev, IdentityLbl, AppNameLbl);
61 | end;
62 |
63 | var
64 | DependencyGraphFacadeANJ: Codeunit DependencyGraphFacade_ANJ;
65 | LibraryAssert: Codeunit System.TestLibraries.Utilities."Library Assert";
66 | AppNameLbl: Label 'App';
67 | CircleFigureLbl: Label '%1((%2))', Comment = 'Placeholder %1 represents the Identity, and Placeholder %2 represents the Content.';
68 | GenerateFigureErr: Label 'Error generating figures.';
69 | IdentityLbl: Label 'E1';
70 | RhombusFigureLbl: Label '%1{%2}', Comment = 'Placeholder %1 represents the Identity, and Placeholder %2 represents the Content.';
71 | SquareRectangleFigureLbl: Label '%1[%2]', Comment = 'Placeholder %1 represents the Identity, and Placeholder %2 represents the Content.';
72 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/Base/GenerateTablesANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit GenerateTables_ANJ (ID 80807)
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80807 GenerateTables_ANJ
6 | {
7 | Access = Public;
8 | Permissions = tabledata Extensions_ANJ = RD, tabledata Relations_ANJ = RD, tabledata DependencyGraphSetup_ANJ = RM;
9 |
10 | ///
11 | /// GenerateTables.
12 | ///
13 | /// Boolean.
14 | procedure Generate(HideDialog: Boolean)
15 | var
16 | Extensions: Record Extensions_ANJ;
17 | IsHandled: Boolean;
18 | begin
19 | if not Extensions.IsEmpty() then
20 | if not ConfirmGenerateTables(HideDialog) then
21 | exit;
22 |
23 | OnBeforeGenerateTables(IsHandled);
24 | DoGenerateTables(IsHandled);
25 | OnAfterGenerateTables();
26 | UpdateSetupTable();
27 | AcknowledgeGenerateTables(HideDialog);
28 | end;
29 | ///
30 | /// ConfirmGenerateTables.
31 | ///
32 | /// Boolean.
33 | /// Return variable Answer of type Boolean.
34 | local procedure ConfirmGenerateTables(HideDialog: Boolean) Answer: Boolean
35 | begin
36 | Answer := true;
37 | if not HideDialog then
38 | Answer := ConfirmManagement.GetResponseOrDefault(ConfirmQst, Answer);
39 | end;
40 |
41 | ///
42 | /// DoGenerateTables.
43 | ///
44 | /// Boolean.
45 | local procedure DoGenerateTables(IsHandled: Boolean)
46 | begin
47 | if IsHandled then
48 | exit;
49 |
50 | DependencyGraphFacade.CleanExtensionsTable();
51 | DependencyGraphFacade.CleanRelationsTable();
52 |
53 | DependencyGraphFacade.GenerateExtensionsTable();
54 | DependencyGraphFacade.GenerateRelationTable();
55 | end;
56 |
57 | ///
58 | /// UpdateSetupTable.
59 | ///
60 | local procedure UpdateSetupTable()
61 | var
62 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
63 | begin
64 | DependencyGraphSetup.GetInstance();
65 | DependencyGraphSetup.Validate(DateLastGeneration, Today());
66 | DependencyGraphSetup.Validate(TimeLastGeneration, Time());
67 | DependencyGraphSetup.Modify(true);
68 | end;
69 |
70 | ///
71 | /// AcknowledgeGenerateTables.
72 | ///
73 | /// Boolean.
74 | local procedure AcknowledgeGenerateTables(HideDialog: Boolean)
75 | begin
76 | if (not GuiAllowed()) or (HideDialog) then
77 | exit;
78 |
79 | Message(ProcessFinishMsg);
80 | end;
81 |
82 | ///
83 | /// OnBeforeGenerateTables.
84 | ///
85 | /// VAR Boolean.
86 | [IntegrationEvent(false, false)]
87 | local procedure OnBeforeGenerateTables(var IsHandled: Boolean)
88 | begin
89 | end;
90 |
91 | ///
92 | /// OnAfterGenerateTables.
93 | ///
94 | [IntegrationEvent(false, false)]
95 | local procedure OnAfterGenerateTables()
96 | begin
97 | end;
98 |
99 | var
100 | DependencyGraphFacade: Codeunit DependencyGraphFacade_ANJ;
101 | ConfirmManagement: Codeunit System.Utilities."Confirm Management";
102 | ConfirmQst: Label 'When generating the tables again, custom data will be deleted, do you want to continue?';
103 | ProcessFinishMsg: Label 'The tables have been updated correctly.';
104 | }
--------------------------------------------------------------------------------
/.github/workflows/CreatePerformanceTestApp.yaml:
--------------------------------------------------------------------------------
1 | name: 'Create a new performance test app'
2 |
3 | run-name: "Create a new performance test app in [${{ github.ref_name }}]"
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | project:
9 | description: Project name if the repository is setup for multiple projects
10 | required: false
11 | default: '.'
12 | name:
13 | description: Name
14 | required: true
15 | default: '.PerformanceTest'
16 | publisher:
17 | description: Publisher
18 | required: true
19 | idrange:
20 | description: ID range
21 | required: true
22 | default: '50000..99999'
23 | sampleCode:
24 | description: Include Sample code?
25 | type: boolean
26 | default: true
27 | sampleSuite:
28 | description: Include Sample BCPT Suite?
29 | type: boolean
30 | default: true
31 | directCommit:
32 | description: Direct Commit?
33 | type: boolean
34 | default: false
35 | useGhTokenWorkflow:
36 | description: Use GhTokenWorkflow for PR/Commit?
37 | type: boolean
38 | default: false
39 |
40 | permissions:
41 | actions: read
42 | contents: write
43 | id-token: write
44 | pull-requests: write
45 |
46 | defaults:
47 | run:
48 | shell: powershell
49 |
50 | env:
51 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
52 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
53 |
54 | jobs:
55 | CreatePerformanceTestApp:
56 | needs: [ ]
57 | runs-on: [ windows-latest ]
58 | steps:
59 | - name: Dump Workflow Information
60 | uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@v7.0
61 | with:
62 | shell: powershell
63 |
64 | - name: Checkout
65 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
66 |
67 | - name: Initialize the workflow
68 | id: init
69 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
70 | with:
71 | shell: powershell
72 |
73 | - name: Read settings
74 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
75 | with:
76 | shell: powershell
77 |
78 | - name: Read secrets
79 | id: ReadSecrets
80 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
81 | with:
82 | shell: powershell
83 | gitHubSecrets: ${{ toJson(secrets) }}
84 | getSecrets: 'TokenForPush'
85 | useGhTokenWorkflowForPush: '${{ github.event.inputs.useGhTokenWorkflow }}'
86 |
87 | - name: Creating a new test app
88 | uses: microsoft/AL-Go-Actions/CreateApp@v7.0
89 | with:
90 | shell: powershell
91 | token: ${{ steps.ReadSecrets.outputs.TokenForPush }}
92 | project: ${{ github.event.inputs.project }}
93 | type: 'Performance Test App'
94 | name: ${{ github.event.inputs.name }}
95 | publisher: ${{ github.event.inputs.publisher }}
96 | idrange: ${{ github.event.inputs.idrange }}
97 | sampleCode: ${{ github.event.inputs.sampleCode }}
98 | sampleSuite: ${{ github.event.inputs.sampleSuite }}
99 | directCommit: ${{ github.event.inputs.directCommit }}
100 |
101 | - name: Finalize the workflow
102 | if: always()
103 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
104 | env:
105 | GITHUB_TOKEN: ${{ github.token }}
106 | with:
107 | shell: powershell
108 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
109 | currentJobContext: ${{ toJson(job) }}
110 |
--------------------------------------------------------------------------------
/Dependency-Graph/src/ExtensionTable/ExtensionsANJ.Table.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Table "Extensions_ANJ" (ID 80801).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | table 80801 Extensions_ANJ
6 | {
7 | Access = Public;
8 | Caption = 'Extensions';
9 | DataClassification = CustomerContent;
10 | DrillDownPageId = Extensions_ANJ;
11 | Extensible = true;
12 | LookupPageId = Extensions_ANJ;
13 |
14 | fields
15 | {
16 | field(1; AppID; Guid)
17 | {
18 | AllowInCustomizations = Never;
19 | Caption = 'App ID';
20 | }
21 | field(2; Name; Text[2048])
22 | {
23 | Caption = 'Name';
24 | ToolTip = 'Specifies the value of the Name field.';
25 | }
26 | field(3; DisplayName; Text[2048])
27 | {
28 | Caption = 'Display Name';
29 | ToolTip = 'Specifies the value of the Display Name field.';
30 |
31 | trigger OnValidate()
32 | begin
33 | UpdateFigure();
34 | end;
35 | }
36 | field(4; Publisher; Text[2048])
37 | {
38 | Caption = 'Publisher';
39 | ToolTip = 'Specifies the value of the Publisher field.';
40 | }
41 | field(5; PublishedAs; Enum ExtensionScope_ANJ)
42 | {
43 | Caption = 'Published As';
44 | InitValue = PTE;
45 | ToolTip = 'Specifies the value of the Published As field.';
46 | }
47 | field(6; Identity; Text[2048])
48 | {
49 | AllowInCustomizations = Never;
50 | Caption = 'Identity';
51 |
52 | trigger OnValidate()
53 | begin
54 | UpdateFigure();
55 | end;
56 | }
57 | field(7; ShowInGraph; Boolean)
58 | {
59 | Caption = 'Show In Graph';
60 | ToolTip = 'Specifies the value of the ShowInGraph field.';
61 | }
62 | field(8; Figure; Text[2048])
63 | {
64 | AllowInCustomizations = Never;
65 | Caption = 'Figure';
66 | }
67 | field(9; HasStartRelationships; Boolean)
68 | {
69 | AllowInCustomizations = Never;
70 | CalcFormula = exist(Relations_ANJ where(SourceAppID = field(AppID)));
71 | Caption = 'Has Relationships';
72 | Editable = false;
73 | FieldClass = FlowField;
74 | }
75 | field(10; HasRelationships; Boolean)
76 | {
77 | AllowInCustomizations = Never;
78 | CalcFormula = exist(Relations_ANJ where(DestinationAppID = field(AppID)));
79 | Caption = 'Has Relationships';
80 | Editable = false;
81 | FieldClass = FlowField;
82 | }
83 | }
84 | keys
85 | {
86 | key(Key1; AppID)
87 | {
88 | Clustered = true;
89 | }
90 | }
91 | fieldgroups
92 | {
93 | fieldgroup(DropDown; DisplayName, Publisher, PublishedAs)
94 | {
95 | }
96 | fieldgroup(Brick; DisplayName, Publisher, PublishedAs)
97 | {
98 | }
99 | }
100 |
101 | ///
102 | /// UpdateFigure
103 | ///
104 | local procedure UpdateFigure()
105 | begin
106 | Validate(Figure, CopyStr(DependencyGraphFacade.GenerateFigures(PublishedAs, Identity, DisplayName), 1, 2048));
107 | end;
108 |
109 | ///
110 | /// UpdateRelationTable.
111 | ///
112 | internal procedure UpdateRelationTable()
113 | begin
114 | DependencyGraphFacade.CleanRelationsTable();
115 | DependencyGraphFacade.GenerateRelationTable();
116 | end;
117 |
118 | var
119 | DependencyGraphFacade: Codeunit DependencyGraphFacade_ANJ;
120 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/Setup/DependencyGraphSetupANJ.Page.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Page DependencyGraphSetup_ANJ (ID 80800).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | page 80800 DependencyGraphSetup_ANJ
6 | {
7 | ApplicationArea = All;
8 | Caption = 'Dependency Graph Setup';
9 | Extensible = true;
10 | PageType = Card;
11 | SourceTable = DependencyGraphSetup_ANJ;
12 | UsageCategory = Administration;
13 |
14 | layout
15 | {
16 | area(Content)
17 | {
18 | group(Auth)
19 | {
20 | Caption = 'App registrations - Azure';
21 |
22 | field(ClientID; Rec.ClientID)
23 | {
24 | }
25 | field(Secret; Rec.Secret)
26 | {
27 | ExtendedDatatype = Masked;
28 | }
29 | }
30 | group(Figure)
31 | {
32 | Caption = 'App shapes inside the diagram';
33 |
34 | field(ScopePTEFigure; Rec.ScopePTEFigure)
35 | {
36 | }
37 | field(ScopeGlobalFigure; Rec.ScopeGlobalFigure)
38 | {
39 | }
40 | field(ScopeDevFigure; Rec.ScopeDevFigure)
41 | {
42 | }
43 | }
44 | group(Include)
45 | {
46 | Caption = 'Include when generating the graph';
47 |
48 | field(IncludeMicrosoftApps; Rec.IncludeMicrosoftApps)
49 | {
50 | Caption = 'Microsoft Apps';
51 | }
52 | field(IncludeLinkText; Rec.IncludeLinkText)
53 | {
54 | Caption = 'Link text between dependencies';
55 | }
56 | }
57 | group(FillTables)
58 | {
59 | Caption = 'Fill tables';
60 |
61 | field(FillingProcessingTables; Rec.FillingProcessingTables)
62 | {
63 | Editable = false;
64 | }
65 | }
66 | group(LastGeneration)
67 | {
68 | Caption = 'Last time it was generated';
69 |
70 | group(Tables)
71 | {
72 | Caption = 'Tables to customize the graph';
73 |
74 | field(DateLastGeneration; Rec.DateLastGeneration)
75 | {
76 | Editable = false;
77 | }
78 | field(TimeLastGeneration; Rec.TimeLastGeneration)
79 | {
80 | Editable = false;
81 | }
82 | }
83 | group(Markdown)
84 | {
85 | Caption = 'Dependency graph';
86 |
87 | field(DateLastGenerationMarkdown; Rec.DateLastGenerationMarkdown)
88 | {
89 | Editable = false;
90 | }
91 | field(TimeLastGenerationMarkdown; Rec.TimeLastGenerationMarkdown)
92 | {
93 | Editable = false;
94 | }
95 | }
96 | }
97 | }
98 | }
99 | actions
100 | {
101 | area(Processing)
102 | {
103 | action(Extensions)
104 | {
105 | ApplicationArea = All;
106 | Caption = 'Dependency Graph';
107 | Image = Table;
108 | Promoted = true;
109 | PromotedCategory = Process;
110 | PromotedOnly = true;
111 | RunObject = page ShowInGraph_ANJ;
112 | ToolTip = 'Executes the Show In Graph action.';
113 | }
114 | }
115 | }
116 |
117 | trigger OnOpenPage()
118 | begin
119 | Rec.GetInstance();
120 | end;
121 | }
--------------------------------------------------------------------------------
/Dependency-Graph.Test/src/Test/TemporaryTablesTestANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "TemporaryTablesTest_ANJ" (ID 99992).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 99992 TemporaryTablesTest_ANJ
6 | {
7 | Access = Public;
8 | Subtype = Test;
9 | TestPermissions = Disabled;
10 |
11 | ///
12 | /// GenerateFigures.
13 | ///
14 | [Test]
15 | procedure GenerateFigures()
16 | var
17 | ExtensionRecords: Integer;
18 | RelationsRecords: Integer;
19 | ExpectedMarkdownMermaidText: Text;
20 | ExpectedMarkdownText: Text;
21 | MarkdownMermaidText: Text;
22 | MarkdownText: Text;
23 | begin
24 | // [Scenario]
25 | // A single test is carried out with the entire cycle.
26 |
27 | // [Given] Setup:
28 | InitializeDependencyGraphSetup();
29 |
30 | // [When] Exercise:
31 | DependencyGraphFacade.GenerateAllTables(true);
32 | ExtensionRecords := GetExtensionRecords();
33 | RelationsRecords := GetRelationsRecords();
34 |
35 | GetMarkdownTexts(MarkdownText, MarkdownMermaidText);
36 | ExpectedMarkdownText := ExpectedValues.GetExpectedMarkdownText();
37 | ExpectedMarkdownMermaidText := ExpectedValues.GetExpectedMarkdownMermaidText();
38 | // [Then] Verify:
39 | LibraryAssert.AreEqual(ExtensionRecords, 2, StrSubstNo(DiferentNumberErr, Format(Database::Extensions_ANJ)));
40 | LibraryAssert.AreEqual(RelationsRecords, 1, StrSubstNo(DiferentNumberErr, Format(Database::Relations_ANJ)));
41 | LibraryAssert.AreEqual(MarkdownText, ExpectedMarkdownText, MarkdownTextErr);
42 | LibraryAssert.AreEqual(MarkdownMermaidText, ExpectedMarkdownMermaidText, MarkdownTextErr);
43 | end;
44 |
45 | ///
46 | /// InitializeDependencyGraphSetup
47 | ///
48 | local procedure InitializeDependencyGraphSetup()
49 | var
50 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
51 | begin
52 | DependencyGraphSetup.GetInstance();
53 | DependencyGraphSetup.Validate(ScopePTEFigure, Enum::GeometricFigure_ANJ::SquareRectangle);
54 | DependencyGraphSetup.Validate(FillingProcessingTables, Enum::FillingProcessingTables_ANJ::Mock_ANJ);
55 | DependencyGraphSetup.Modify(true);
56 | end;
57 |
58 | ///
59 | /// GetExtensionRecords.
60 | ///
61 | /// Return value of type Integer.
62 | local procedure GetExtensionRecords(): Integer
63 | var
64 | Extensions: Record Extensions_ANJ;
65 | begin
66 | exit(Extensions.Count());
67 | end;
68 |
69 | ///
70 | /// GetRelationsRecords.
71 | ///
72 | /// Return value of type Integer.
73 | local procedure GetRelationsRecords(): Integer
74 | var
75 | Relations: Record Relations_ANJ;
76 | begin
77 | exit(Relations.Count());
78 | end;
79 |
80 | ///
81 | /// GetMarkdownTexts.
82 | ///
83 | /// VAR Text.
84 | /// VAR Text.
85 | local procedure GetMarkdownTexts(var MarkdownText: Text; var MarkdownMermaidText: Text)
86 | var
87 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
88 | begin
89 | DependencyGraphFacade.GenerateGraph();
90 | MarkdownText := DependencyGraphFacade.GetMarkdownText(DependencyGraphSetup.FieldNo(Markdown));
91 | MarkdownMermaidText := DependencyGraphFacade.GetMarkdownText(DependencyGraphSetup.FieldNo(MarkdownMermaid));
92 | end;
93 |
94 | var
95 | DependencyGraphFacade: Codeunit DependencyGraphFacade_ANJ;
96 | ExpectedValues: Codeunit ExpectedValues_ANJ;
97 | LibraryAssert: Codeunit System.TestLibraries.Utilities."Library Assert";
98 | DiferentNumberErr: Label 'Diferent number of records in table %1 than expected', Comment = 'Error message for different number of records in a table %1.';
99 | MarkdownTextErr: Label 'Markdown text is different than expected';
100 | }
--------------------------------------------------------------------------------
/.AL-Go/cloudDevEnv.ps1:
--------------------------------------------------------------------------------
1 | #
2 | # Script for creating cloud development environment
3 | # Please do not modify this script as it will be auto-updated from the AL-Go Template
4 | # Recommended approach is to use as is or add a script (freddyk-devenv.ps1), which calls this script with the user specific parameters
5 | #
6 | Param(
7 | [string] $environmentName = "",
8 | [bool] $reuseExistingEnvironment,
9 | [switch] $fromVSCode,
10 | [switch] $clean
11 | )
12 |
13 | $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0
14 |
15 | function DownloadHelperFile {
16 | param(
17 | [string] $url,
18 | [string] $folder
19 | )
20 |
21 | $prevProgressPreference = $ProgressPreference; $ProgressPreference = 'SilentlyContinue'
22 | $name = [System.IO.Path]::GetFileName($url)
23 | Write-Host "Downloading $name from $url"
24 | $path = Join-Path $folder $name
25 | Invoke-WebRequest -UseBasicParsing -uri $url -OutFile $path
26 | $ProgressPreference = $prevProgressPreference
27 | return $path
28 | }
29 |
30 | try {
31 | Clear-Host
32 | Write-Host
33 | Write-Host -ForegroundColor Yellow @'
34 | _____ _ _ _____ ______
35 | / ____| | | | | __ \ | ____|
36 | | | | | ___ _ _ __| | | | | | _____ __ |__ _ ____ __
37 | | | | |/ _ \| | | |/ _` | | | | |/ _ \ \ / / __| | '_ \ \ / /
38 | | |____| | (_) | |_| | (_| | | |__| | __/\ V /| |____| | | \ V /
39 | \_____|_|\___/ \__,_|\__,_| |_____/ \___| \_/ |______|_| |_|\_/
40 |
41 | '@
42 |
43 | $tmpFolder = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString())"
44 | New-Item -Path $tmpFolder -ItemType Directory -Force | Out-Null
45 | $GitHubHelperPath = DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v7.0/Github-Helper.psm1' -folder $tmpFolder
46 | $ALGoHelperPath = DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v7.0/AL-Go-Helper.ps1' -folder $tmpFolder
47 | DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v7.0/Packages.json' -folder $tmpFolder | Out-Null
48 |
49 | Import-Module $GitHubHelperPath
50 | . $ALGoHelperPath -local
51 |
52 | $baseFolder = GetBaseFolder -folder $PSScriptRoot
53 | $project = GetProject -baseFolder $baseFolder -projectALGoFolder $PSScriptRoot
54 |
55 | Write-Host @'
56 |
57 | This script will create a cloud based development environment (Business Central SaaS Sandbox) for your project.
58 | All apps and test apps will be compiled and published to the environment in the development scope.
59 | The script will also modify launch.json to have a "Cloud Sandbox ()" configuration point to your environment.
60 |
61 | '@
62 |
63 | if (Test-Path (Join-Path $PSScriptRoot "NewBcContainer.ps1")) {
64 | Write-Host -ForegroundColor Red "WARNING: The project has a NewBcContainer override defined. Typically, this means that you cannot run a cloud development environment"
65 | }
66 |
67 | Write-Host
68 |
69 | if (-not $environmentName) {
70 | $environmentName = Enter-Value `
71 | -title "Environment name" `
72 | -question "Please enter the name of the environment to create" `
73 | -default "$($env:USERNAME)-sandbox" `
74 | -trimCharacters @('"',"'",' ')
75 | }
76 |
77 | if ($PSBoundParameters.Keys -notcontains 'reuseExistingEnvironment') {
78 | $reuseExistingEnvironment = (Select-Value `
79 | -title "What if the environment already exists?" `
80 | -options @{ "Yes" = "Reuse existing environment"; "No" = "Recreate environment" } `
81 | -question "Select behavior" `
82 | -default "No") -eq "Yes"
83 | }
84 |
85 | CreateDevEnv `
86 | -kind cloud `
87 | -caller local `
88 | -environmentName $environmentName `
89 | -reuseExistingEnvironment:$reuseExistingEnvironment `
90 | -baseFolder $baseFolder `
91 | -project $project `
92 | -clean:$clean
93 | }
94 | catch {
95 | Write-Host -ForegroundColor Red "Error: $($_.Exception.Message)`nStacktrace: $($_.scriptStackTrace)"
96 | }
97 | finally {
98 | if ($fromVSCode) {
99 | Read-Host "Press ENTER to close this window"
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Dependency-Graph.Test/src/Test/FillingProTablesMockANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "FillingProTablesMock_ANJ" (ID 99993).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 99993 FillingProTablesMock_ANJ implements IFillingProcessingTables_ANJ
6 | {
7 | Access = Internal;
8 |
9 | ///
10 | /// GetExtensions.
11 | ///
12 | /// Return value of type Text.
13 | procedure GetExtensions(): Text
14 | var
15 | ExtensionArry: JsonArray;
16 | AuxJsonObject: JsonObject;
17 | Extensions: Text;
18 | begin
19 | AddNewExtensionToJsonArry(
20 | ExtensionArry,
21 | Ext01IDLbl,
22 | Ext01NameLbl,
23 | Ext01PublisherLbl,
24 | ScopeLbl);
25 |
26 | AddNewExtensionToJsonArry(
27 | ExtensionArry,
28 | Ext02IDLbl,
29 | Ext02NameLbl,
30 | Ext02PublisherLbl,
31 | ScopeLbl);
32 |
33 | AuxJsonObject.Add(ValueLbl, ExtensionArry);
34 | AuxJsonObject.WriteTo(Extensions);
35 |
36 | exit(Extensions);
37 | end;
38 |
39 | ///
40 | /// GetRelations.
41 | ///
42 | /// Return value of type Text.
43 | procedure GetRelations(): Text
44 | var
45 | RelationsArry: JsonArray;
46 | Relations: Text;
47 | begin
48 | AddNewRelationToJsonArry(RelationsArry,
49 | Ext01IDLbl,
50 | Ext02IDLbl);
51 | RelationsArry.WriteTo(Relations);
52 |
53 | exit(Relations);
54 | end;
55 |
56 | ///
57 | /// AddNewExtensionToJsonArry.
58 | ///
59 | /// VAR JsonArray.
60 | /// Text.
61 | /// Text.
62 | /// Text.
63 | /// Text.
64 | local procedure AddNewExtensionToJsonArry(
65 | var ExtensionArry: JsonArray;
66 | PackageId: Text;
67 | DisplayName: Text;
68 | Publisher: Text;
69 | PublishedAs: Text)
70 | var
71 | ExtensionJsonObject: JsonObject;
72 | begin
73 | ExtensionJsonObject.Add(IdLbl, PackageId);
74 | ExtensionJsonObject.Add(PackageIdLbl, PackageId);
75 | ExtensionJsonObject.Add(DisplayNameLbl, DisplayName);
76 | ExtensionJsonObject.Add(PublisherLbl, Publisher);
77 | ExtensionJsonObject.Add(PublishedAsLbl, PublishedAs);
78 | ExtensionJsonObject.Add(IsInstalledLbl, true);
79 |
80 | ExtensionArry.Add(ExtensionJsonObject);
81 | end;
82 |
83 | ///
84 | /// AddNewRelationToJsonArry.
85 | ///
86 | /// VAR JsonArray.
87 | /// Guid.
88 | /// Guid.
89 | local procedure AddNewRelationToJsonArry(
90 | var RelationsArry: JsonArray;
91 | SourceAppID: Guid;
92 | DestinationAppID: Guid)
93 | var
94 | RelationJsonObject: JsonObject;
95 | begin
96 | RelationJsonObject.Add(SourceAppIDLbl, SourceAppID);
97 | RelationJsonObject.Add(DestinationAppIDLbl, DestinationAppID);
98 |
99 | RelationsArry.Add(RelationJsonObject);
100 | end;
101 |
102 | var
103 | DestinationAppIDLbl: Label 'DestinationAppID';
104 | DisplayNameLbl: Label 'displayName';
105 | Ext01IDLbl: Label 'a1f81352-6244-48a4-96a0-a81c5aaaa581';
106 | Ext01NameLbl: Label 'Take Order Sample';
107 | Ext01PublisherLbl: Label 'BusinessCentralDemos';
108 | Ext02IDLbl: Label '3c9a4c22-42f6-4e8f-8b96-744c7fef331f';
109 | Ext02NameLbl: Label 'Dependency-Graph';
110 | Ext02PublisherLbl: Label 'ANJ';
111 | IdLbl: Label 'id';
112 | IsInstalledLbl: Label 'isInstalled';
113 | PackageIdLbl: Label 'packageId';
114 | PublishedAsLbl: Label 'publishedAs';
115 | PublisherLbl: Label 'publisher';
116 | ScopeLbl: Label 'PTE';
117 | SourceAppIDLbl: Label 'SourceAppID';
118 | ValueLbl: Label 'value';
119 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/UpgradeTagsExample/DataUpgradesANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "DataUpgrades_ANJ" (ID 80816).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80816 DataUpgrades_ANJ
6 | {
7 | Access = Internal;
8 | Permissions = tabledata DependencyGraphSetup_ANJ = RM, tabledata Extensions_ANJ = RMD, tabledata Relations_ANJ = RD;
9 |
10 | ///
11 | /// OnGetPerCompanyUpgradeTags.
12 | ///
13 | /// VAR List of [Code[250]].
14 | [EventSubscriber(ObjectType::Codeunit, Codeunit::System.Upgrade."Upgrade Tag", OnGetPerCompanyUpgradeTags, '', false, false)]
15 | local procedure OnGetPerCompanyUpgradeTags(var PerCompanyUpgradeTags: List of [Code[250]])
16 | begin
17 | PerCompanyUpgradeTags.Add(GetUpgradeTagDataUpgrade1());
18 | end;
19 |
20 | #region Data Upgrade 1 - Upgrade data from previous versions to version 4.15.0.0. - Change reserved ordinal 0 to another one.
21 | ///
22 | /// GetUpgradeTagDataUpgrade1.
23 | ///
24 | /// Return value of type Code[250].
25 | internal procedure GetUpgradeTagDataUpgrade1(): Code[250]
26 | begin
27 | exit(DT1IdTok)
28 | end;
29 |
30 | ///
31 | /// RunDataUpgrade1.
32 | ///
33 | internal procedure RunDataUpgrade1()
34 | begin
35 | CheckAndUpdateDependencyGraphSetupFillingProcessingTablesEnum();
36 | CheckAndUpdateExtensionsExtensionScopeEnum();
37 | CheckAndUpdateDependencyGraphSetupGeometricFigureEnum();
38 | end;
39 |
40 | ///
41 | /// CheckAndUpdateDependencyGraphSetupFillingProcessingTablesEnum.
42 | ///
43 | local procedure CheckAndUpdateDependencyGraphSetupFillingProcessingTablesEnum()
44 | var
45 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
46 | begin
47 | DependencyGraphSetup.GetInstance();
48 | if DependencyGraphSetup.FillingProcessingTables.AsInteger() <> 0 then
49 | exit;
50 |
51 | DependencyGraphSetup.Validate(FillingProcessingTables, DependencyGraphSetup.FillingProcessingTables::WSAndModuleDependencyInfo);
52 | DependencyGraphSetup.Modify(false);
53 | end;
54 |
55 | ///
56 | /// CheckAndUpdateExtensionsExtensionScopeEnum.
57 | ///
58 | local procedure CheckAndUpdateExtensionsExtensionScopeEnum()
59 | var
60 | Extensions: Record Extensions_ANJ;
61 | begin
62 | if Extensions.IsEmpty() then
63 | exit;
64 |
65 | if Extensions.FindSet(true) then
66 | repeat
67 | if Extensions.PublishedAs.AsInteger() = 0 then begin
68 | Extensions.Validate(PublishedAs, Extensions.PublishedAs::PTE);
69 | Extensions.Modify(false)
70 | end;
71 | until Extensions.Next() = 0;
72 | end;
73 |
74 | ///
75 | /// CheckAndUpdateDependencyGraphSetupGeometricFigureEnum.
76 | ///
77 | local procedure CheckAndUpdateDependencyGraphSetupGeometricFigureEnum()
78 | var
79 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
80 | begin
81 | DependencyGraphSetup.GetInstance();
82 | if (DependencyGraphSetup.ScopePTEFigure.AsInteger() <> 0) and
83 | (DependencyGraphSetup.ScopeGlobalFigure.AsInteger() <> 0) and
84 | (DependencyGraphSetup.ScopeDevFigure.AsInteger() <> 0) then
85 | exit;
86 |
87 | if DependencyGraphSetup.ScopePTEFigure.AsInteger() = 0 then
88 | DependencyGraphSetup.Validate(ScopePTEFigure, DependencyGraphSetup.ScopePTEFigure::SquareRectangle);
89 |
90 | if DependencyGraphSetup.ScopeGlobalFigure.AsInteger() = 0 then
91 | DependencyGraphSetup.Validate(ScopeGlobalFigure, DependencyGraphSetup.ScopeGlobalFigure::SquareRectangle);
92 |
93 | if DependencyGraphSetup.ScopeDevFigure.AsInteger() = 0 then
94 | DependencyGraphSetup.Validate(ScopeDevFigure, DependencyGraphSetup.ScopeDevFigure::SquareRectangle);
95 | DependencyGraphSetup.Modify(false);
96 | end;
97 | #endregion
98 |
99 | var
100 | DT1IdTok: Label 'ANJ-41500-FixEnumsZeroOrdinal-20240303', Locked = true;
101 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/Relations/GenerateRelationsTableANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "GenerateRelationsTable_ANJ" (ID 80808).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80808 GenerateRelationsTable_ANJ
6 | {
7 | Access = Public;
8 | Permissions = tabledata Relations_ANJ = RIM;
9 | ///
10 | /// Generate.
11 | ///
12 | internal procedure Generate()
13 | var
14 | Relations: Record Relations_ANJ;
15 | IsHandled: Boolean;
16 | begin
17 | OnBeforeGenerateRelationsTable(Relations, IsHandled);
18 | DoGenerateRelationsTable(IsHandled);
19 | OnAfterGenerateRelationsTable(Relations);
20 | end;
21 |
22 | ///
23 | /// DoGenerateRelationsTable.
24 | ///
25 | /// Boolean.
26 | local procedure DoGenerateRelationsTable(IsHandled: Boolean)
27 | var
28 | FillingProcessingTables: Interface IFillingProcessingTables_ANJ;
29 | ResponseText: Text;
30 | begin
31 | if IsHandled then
32 | exit;
33 |
34 | DependencyGraphFacade.GetInterfaceFillProcessingTables(FillingProcessingTables);
35 | ResponseText := FillingProcessingTables.GetRelations();
36 | ProcessRelationsAndInsertLines(ResponseText);
37 | end;
38 |
39 | ///
40 | /// ProcessRelationsAndInsertLines.
41 | ///
42 | /// Text.
43 | local procedure ProcessRelationsAndInsertLines(ResponseText: Text)
44 | var
45 | RelationsArry: JsonArray;
46 | RelationJsonToken: JsonToken;
47 | JsonText: Text;
48 | begin
49 | if ResponseText = '' then
50 | exit;
51 |
52 | if not RelationsArry.ReadFrom(ResponseText) then
53 | exit;
54 |
55 | foreach RelationJsonToken in RelationsArry do begin
56 | RelationJsonToken.WriteTo(JsonText);
57 | InsertNewRelation(JSONMethods.GetJsonValue(SourceAppIDTok, JsonText), JSONMethods.GetJsonValue(DestinationAppIDTok, JsonText));
58 | end;
59 | end;
60 |
61 | ///
62 | /// InsertNewRelation
63 | ///
64 | ///
65 | ///
66 | local procedure InsertNewRelation(SourceAppID: Guid; DestinationAppID: Guid)
67 | var
68 | Relations: Record Relations_ANJ;
69 | begin
70 | Clear(Relations);
71 | Relations.Init();
72 | Relations.Validate(RelationNo, GetNewRelationsLineNo());
73 | Relations.Insert(true);
74 | Relations.Validate(SourceAppID, SourceAppID);
75 | Relations.Validate(DestinationAppID, DestinationAppID);
76 | Relations.Validate(ShowInGraph, true);
77 | Relations.Modify(true);
78 | end;
79 |
80 | ///
81 | /// GetNewRelationsLineNo.
82 | /// ///
83 | /// Return variable NewRelationsLine of type Integer.
84 | local procedure GetNewRelationsLineNo() NewRelationsLine: Integer
85 | var
86 | Relations: Record Relations_ANJ;
87 | begin
88 | NewRelationsLine := 10000;
89 | if Relations.FindLast() then
90 | NewRelationsLine += Relations.RelationNo;
91 | end;
92 |
93 | ///
94 | /// OnBeforeGenerateRelationsTable.
95 | ///
96 | /// VAR Record Relations_ANJ.
97 | /// VAR Boolean.
98 | [IntegrationEvent(false, false)]
99 | local procedure OnBeforeGenerateRelationsTable(var Relations: Record Relations_ANJ; var IsHandled: Boolean)
100 | begin
101 | end;
102 |
103 | ///
104 | /// OnAfterGenerateRelationsTable.
105 | ///
106 | /// VAR Record Relations_ANJ.
107 |
108 | [IntegrationEvent(false, false)]
109 | local procedure OnAfterGenerateRelationsTable(var Relations: Record Relations_ANJ)
110 | begin
111 | end;
112 |
113 | var
114 | DependencyGraphFacade: Codeunit DependencyGraphFacade_ANJ;
115 | JSONMethods: Codeunit JSONMethods_ANJ;
116 | DestinationAppIDTok: Label 'DestinationAppID', Locked = true;
117 | SourceAppIDTok: Label 'SourceAppID', Locked = true;
118 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/GenerateFigure/GenerateFigureANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit GenerateFigure_ANJ (ID 80804).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80804 GenerateFigure_ANJ
6 | {
7 | Access = Public;
8 |
9 | ///
10 | /// Generate.
11 | ///
12 | /// Enum ExtensionScope_ANJ.
13 | /// Text.
14 | /// Text.
15 | /// Return variable ReturnText of type Text.
16 | internal procedure Generate(ExtensionScope: Enum ExtensionScope_ANJ; Identity: Text; AppName: Text) ReturnText: Text
17 | var
18 | IsHandled: Boolean;
19 | begin
20 | OnBeforeGenerate(ExtensionScope, Identity, AppName, IsHandled);
21 | ReturnText := DoGenerate(ExtensionScope, Identity, AppName, IsHandled);
22 | OnAfterGenerate(ReturnText);
23 | end;
24 |
25 | ///
26 | /// DoGenerate.
27 | ///
28 | /// Enum ExtensionScope_ANJ.
29 | /// Text.
30 | /// Text.
31 | /// Boolean.
32 | /// Return value of type Text.
33 | local procedure DoGenerate(ExtensionScope: Enum ExtensionScope_ANJ; Identity: Text; AppName: Text; IsHandled: Boolean): Text
34 | begin
35 | if IsHandled then
36 | exit;
37 |
38 | exit(GenerateFullFigure(ExtensionScope, Identity, AppName));
39 | end;
40 |
41 | ///
42 | /// GenerateFullFigure.
43 | ///
44 | /// Enum ExtensionScope_ANJ.
45 | /// Text.
46 | /// Text.
47 | /// Return value of type Text.
48 | local procedure GenerateFullFigure(ExtensionScope: Enum ExtensionScope_ANJ; Identity: Text; AppName: Text): Text
49 | var
50 | FigureInGraph: Interface IFigureInGraph_ANJ;
51 | begin
52 | FigureInGraph := GetGeometricFigure(ExtensionScope);
53 | RemoveDisallowedCharacters(AppName);
54 | exit(FigureInGraph.GenerateFigureText(Identity, AppName));
55 | end;
56 |
57 | ///
58 | /// RemoveDisallowedCharacters.
59 | ///
60 | /// VAR Text.
61 | local procedure RemoveDisallowedCharacters(var AppName: Text)
62 | begin
63 | AppName := AppName.Replace('-', '');
64 | AppName := AppName.Replace('(', '');
65 | AppName := AppName.Replace(')', '');
66 | AppName := AppName.Replace('{', '');
67 | AppName := AppName.Replace('}', '');
68 | AppName := AppName.Replace('[', '');
69 | AppName := AppName.Replace(']', '');
70 | end;
71 |
72 | ///
73 | /// GetGeometricFigure.
74 | ///
75 | /// Enum ExtensionScope_ANJ.
76 | /// Return variable GeometricFigure of type Enum GeometricFigure_ANJ.
77 | local procedure GetGeometricFigure(ExtensionScope: Enum ExtensionScope_ANJ) GeometricFigure: Enum GeometricFigure_ANJ
78 | var
79 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
80 | begin
81 | DependencyGraphSetup.SetLoadFields(ScopePTEFigure, ScopeGlobalFigure, ScopeDevFigure);
82 | DependencyGraphSetup.GetInstance();
83 |
84 | case ExtensionScope of
85 | ExtensionScope::PTE:
86 | GeometricFigure := DependencyGraphSetup.ScopePTEFigure;
87 | ExtensionScope::Global:
88 | GeometricFigure := DependencyGraphSetup.ScopeGlobalFigure;
89 | ExtensionScope::Dev:
90 | GeometricFigure := DependencyGraphSetup.ScopeDevFigure;
91 | end;
92 | end;
93 |
94 | ///
95 | /// OnBeforeGenerate
96 | ///
97 | ///
98 | ///
99 | ///
100 | ///
101 | [IntegrationEvent(false, false)]
102 | local procedure OnBeforeGenerate(ExtensionScope: Enum ExtensionScope_ANJ; Identity: Text; AppName: Text; var IsHandled: Boolean)
103 | begin
104 | end;
105 |
106 | ///
107 | /// OnAfterGenerate.
108 | ///
109 | /// VAR Text.
110 | [IntegrationEvent(false, false)]
111 | local procedure OnAfterGenerate(var ReturnText: Text)
112 | begin
113 | end;
114 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/Base/DependencyGraphFacadeANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "DependencyGraphFacade_ANJ" (ID 80810).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80810 DependencyGraphFacade_ANJ
6 | {
7 | Access = Public;
8 |
9 | #region codeunit 80806 NumberSequenceMgmt_ANJ
10 | ///
11 | /// InitializeNumberSequence
12 | ///
13 | procedure InitializeNumberSequence()
14 | begin
15 | NumberSequenceMgmt.Initialize();
16 | end;
17 |
18 | ///
19 | /// GetNextNumberSequence.
20 | ///
21 | /// Return value of type Text.
22 | procedure GetNextNumberSequence(): Text
23 | begin
24 | exit(NumberSequenceMgmt.GetNextNo());
25 | end;
26 | #endregion
27 |
28 | #region codeunit 80804 GenerateFigure_ANJ
29 | ///
30 | /// GenerateFigures.
31 | ///
32 | /// Enum ExtensionScope_ANJ.
33 | /// Text.
34 | /// Text.
35 | /// Return value of type Text.
36 | procedure GenerateFigures(ExtensionScope: Enum ExtensionScope_ANJ; Identity: Text; AppName: Text): Text
37 | begin
38 | exit(GenerateFigure.Generate(ExtensionScope, Identity, AppName));
39 | end;
40 | #endregion
41 |
42 | #region codeunit 80807 GenerateTables_ANJ
43 | ///
44 | /// GenerateAllTables.
45 | ///
46 | /// Boolean.
47 | procedure GenerateAllTables(HideDialog: Boolean)
48 | begin
49 | GenerateTables.Generate(HideDialog);
50 | end;
51 | #endregion
52 |
53 | #region codeunit 80805 GenerateExtensionTable_ANJ
54 | ///
55 | /// GenerateExtensionsTable.
56 | ///
57 | procedure GenerateExtensionsTable()
58 | begin
59 | GenerateExtensionTable.Generate();
60 | end;
61 | #endregion
62 |
63 | #region codeunit 80808 GenerateRelationsTable_ANJ
64 | ///
65 | /// GenerateRelationsTable.
66 | ///
67 | procedure GenerateRelationTable()
68 | begin
69 | GenerateRelationsTable.Generate();
70 | end;
71 | #endregion
72 |
73 | #region 80811 CleanTemporaryTables_ANJ
74 | ///
75 | /// CleanExtensionsTable.
76 | ///
77 | procedure CleanExtensionsTable()
78 | begin
79 | CleanTemporaryTables.Clean(true, false);
80 | end;
81 |
82 | ///
83 | /// CleanRelationsTable.
84 | ///
85 | procedure CleanRelationsTable()
86 | begin
87 | CleanTemporaryTables.Clean(false, true);
88 | end;
89 | #endregion
90 |
91 | #region codeunit 80809 MarkdownMgmt_ANJ
92 | ///
93 | /// GenerateGraph.
94 | ///
95 | procedure GenerateGraph()
96 | begin
97 | MarkdownMgmt.GenerateGraph();
98 | end;
99 |
100 | ///
101 | /// GetMarkdownText.
102 | ///
103 | /// Integer.
104 | /// Return value of type Text.
105 | procedure GetMarkdownText(FieldNo: Integer): Text
106 | begin
107 | exit(MarkdownMgmt.GetMarkdown(FieldNo));
108 | end;
109 | #endregion
110 |
111 | ///
112 | /// GetInterfaceFillProcessingTables.
113 | ///
114 | /// VAR Interface FillingProcessingTables_ANJ.
115 | procedure GetInterfaceFillProcessingTables(var FillingProcessingTables: Interface IFillingProcessingTables_ANJ)
116 | var
117 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
118 | begin
119 | DependencyGraphSetup.SetLoadFields(FillingProcessingTables);
120 | DependencyGraphSetup.GetInstance();
121 | FillingProcessingTables := DependencyGraphSetup.FillingProcessingTables;
122 | OnAfterGetFillingProcessingTables(FillingProcessingTables);
123 | end;
124 |
125 | ///
126 | /// OnAfterGetFillingProcessingTables
127 | ///
128 | ///
129 | [IntegrationEvent(false, false)]
130 | local procedure OnAfterGetFillingProcessingTables(var FillingProcessingTables: Interface IFillingProcessingTables_ANJ)
131 | begin
132 | end;
133 |
134 | var
135 | CleanTemporaryTables: Codeunit CleanTemporaryTables_ANJ;
136 | GenerateExtensionTable: Codeunit GenerateExtensionTable_ANJ;
137 | GenerateFigure: Codeunit GenerateFigure_ANJ;
138 | GenerateRelationsTable: Codeunit GenerateRelationsTable_ANJ;
139 | GenerateTables: Codeunit GenerateTables_ANJ;
140 | MarkdownMgmt: Codeunit MarkdownMgmt_ANJ;
141 | NumberSequenceMgmt: Codeunit NumberSequenceMgmt_ANJ;
142 | }
--------------------------------------------------------------------------------
/.github/workflows/Current.yaml:
--------------------------------------------------------------------------------
1 | name: ' Test Current'
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | permissions:
7 | actions: read
8 | contents: read
9 | id-token: write
10 |
11 | defaults:
12 | run:
13 | shell: powershell
14 |
15 | env:
16 | workflowDepth: 1
17 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
18 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
19 |
20 | jobs:
21 | Initialization:
22 | needs: [ ]
23 | runs-on: [ windows-latest ]
24 | outputs:
25 | projects: ${{ steps.determineProjectsToBuild.outputs.ProjectsJson }}
26 | projectDependenciesJson: ${{ steps.determineProjectsToBuild.outputs.ProjectDependenciesJson }}
27 | buildOrderJson: ${{ steps.determineProjectsToBuild.outputs.BuildOrderJson }}
28 | workflowDepth: ${{ steps.DetermineWorkflowDepth.outputs.WorkflowDepth }}
29 | artifactsRetentionDays: ${{ steps.DetermineWorkflowDepth.outputs.ArtifactsRetentionDays }}
30 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
31 | steps:
32 | - name: Dump Workflow Information
33 | uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@v7.0
34 | with:
35 | shell: powershell
36 |
37 | - name: Checkout
38 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
39 | with:
40 | lfs: true
41 |
42 | - name: Initialize the workflow
43 | id: init
44 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
45 | with:
46 | shell: powershell
47 |
48 | - name: Read settings
49 | id: ReadSettings
50 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
51 | with:
52 | shell: powershell
53 | get: useGitSubmodules,shortLivedArtifactsRetentionDays
54 |
55 | - name: Read submodules token
56 | id: ReadSubmodulesToken
57 | if: env.useGitSubmodules != 'false' && env.useGitSubmodules != ''
58 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
59 | with:
60 | shell: powershell
61 | gitHubSecrets: ${{ toJson(secrets) }}
62 | getSecrets: '-gitSubmodulesToken'
63 |
64 | - name: Checkout Submodules
65 | if: env.useGitSubmodules != 'false' && env.useGitSubmodules != ''
66 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
67 | with:
68 | lfs: true
69 | submodules: ${{ env.useGitSubmodules }}
70 | token: '${{ fromJson(steps.ReadSubmodulesToken.outputs.Secrets).gitSubmodulesToken }}'
71 |
72 | - name: Determine Workflow Depth
73 | id: DetermineWorkflowDepth
74 | run: |
75 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "WorkflowDepth=$($env:workflowDepth)"
76 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "ArtifactsRetentionDays=$($env:shortLivedArtifactsRetentionDays)"
77 |
78 | - name: Determine Projects To Build
79 | id: determineProjectsToBuild
80 | uses: microsoft/AL-Go-Actions/DetermineProjectsToBuild@v7.0
81 | with:
82 | shell: powershell
83 | maxBuildDepth: ${{ env.workflowDepth }}
84 |
85 | Build:
86 | needs: [ Initialization ]
87 | if: (!failure()) && (!cancelled()) && fromJson(needs.Initialization.outputs.buildOrderJson)[0].projectsCount > 0
88 | strategy:
89 | matrix:
90 | include: ${{ fromJson(needs.Initialization.outputs.buildOrderJson)[0].buildDimensions }}
91 | fail-fast: false
92 | name: Build ${{ matrix.projectName }} (${{ matrix.buildMode }})
93 | uses: ./.github/workflows/_BuildALGoProject.yaml
94 | secrets: inherit
95 | with:
96 | shell: ${{ matrix.githubRunnerShell }}
97 | runsOn: ${{ matrix.githubRunner }}
98 | project: ${{ matrix.project }}
99 | projectName: ${{ matrix.projectName }}
100 | buildMode: ${{ matrix.buildMode }}
101 | projectDependenciesJson: ${{ needs.Initialization.outputs.projectDependenciesJson }}
102 | secrets: 'licenseFileUrl,codeSignCertificateUrl,*codeSignCertificatePassword,keyVaultCertificateUrl,*keyVaultCertificatePassword,keyVaultClientId,gitHubPackagesContext,applicationInsightsConnectionString'
103 | artifactsRetentionDays: ${{ fromJson(needs.Initialization.outputs.artifactsRetentionDays) }}
104 | artifactsNameSuffix: 'Current'
105 |
106 | PostProcess:
107 | needs: [ Initialization, Build ]
108 | if: always()
109 | runs-on: [ windows-latest ]
110 | steps:
111 | - name: Checkout
112 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
113 |
114 | - name: Finalize the workflow
115 | id: PostProcess
116 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
117 | env:
118 | GITHUB_TOKEN: ${{ github.token }}
119 | with:
120 | shell: powershell
121 | telemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
122 | currentJobContext: ${{ toJson(job) }}
123 |
--------------------------------------------------------------------------------
/.github/workflows/NextMajor.yaml:
--------------------------------------------------------------------------------
1 | name: ' Test Next Major'
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: '0 0 1,15 * *'
7 |
8 | permissions:
9 | actions: read
10 | contents: read
11 | id-token: write
12 |
13 | defaults:
14 | run:
15 | shell: powershell
16 |
17 | env:
18 | workflowDepth: 1
19 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
20 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
21 |
22 | jobs:
23 | Initialization:
24 | needs: [ ]
25 | runs-on: [ windows-latest ]
26 | outputs:
27 | projects: ${{ steps.determineProjectsToBuild.outputs.ProjectsJson }}
28 | projectDependenciesJson: ${{ steps.determineProjectsToBuild.outputs.ProjectDependenciesJson }}
29 | buildOrderJson: ${{ steps.determineProjectsToBuild.outputs.BuildOrderJson }}
30 | workflowDepth: ${{ steps.DetermineWorkflowDepth.outputs.WorkflowDepth }}
31 | artifactsRetentionDays: ${{ steps.DetermineWorkflowDepth.outputs.ArtifactsRetentionDays }}
32 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
33 | steps:
34 | - name: Dump Workflow Information
35 | uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@v7.0
36 | with:
37 | shell: powershell
38 |
39 | - name: Checkout
40 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
41 | with:
42 | lfs: true
43 |
44 | - name: Initialize the workflow
45 | id: init
46 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
47 | with:
48 | shell: powershell
49 |
50 | - name: Read settings
51 | id: ReadSettings
52 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
53 | with:
54 | shell: powershell
55 | get: useGitSubmodules,shortLivedArtifactsRetentionDays
56 |
57 | - name: Read submodules token
58 | id: ReadSubmodulesToken
59 | if: env.useGitSubmodules != 'false' && env.useGitSubmodules != ''
60 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
61 | with:
62 | shell: powershell
63 | gitHubSecrets: ${{ toJson(secrets) }}
64 | getSecrets: '-gitSubmodulesToken'
65 |
66 | - name: Checkout Submodules
67 | if: env.useGitSubmodules != 'false' && env.useGitSubmodules != ''
68 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
69 | with:
70 | lfs: true
71 | submodules: ${{ env.useGitSubmodules }}
72 | token: '${{ fromJson(steps.ReadSubmodulesToken.outputs.Secrets).gitSubmodulesToken }}'
73 |
74 | - name: Determine Workflow Depth
75 | id: DetermineWorkflowDepth
76 | run: |
77 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "WorkflowDepth=$($env:workflowDepth)"
78 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "ArtifactsRetentionDays=$($env:shortLivedArtifactsRetentionDays)"
79 |
80 | - name: Determine Projects To Build
81 | id: determineProjectsToBuild
82 | uses: microsoft/AL-Go-Actions/DetermineProjectsToBuild@v7.0
83 | with:
84 | shell: powershell
85 | maxBuildDepth: ${{ env.workflowDepth }}
86 |
87 | Build:
88 | needs: [ Initialization ]
89 | if: (!failure()) && (!cancelled()) && fromJson(needs.Initialization.outputs.buildOrderJson)[0].projectsCount > 0
90 | strategy:
91 | matrix:
92 | include: ${{ fromJson(needs.Initialization.outputs.buildOrderJson)[0].buildDimensions }}
93 | fail-fast: false
94 | name: Build ${{ matrix.projectName }} (${{ matrix.buildMode }})
95 | uses: ./.github/workflows/_BuildALGoProject.yaml
96 | secrets: inherit
97 | with:
98 | shell: ${{ matrix.githubRunnerShell }}
99 | runsOn: ${{ matrix.githubRunner }}
100 | project: ${{ matrix.project }}
101 | projectName: ${{ matrix.projectName }}
102 | buildMode: ${{ matrix.buildMode }}
103 | projectDependenciesJson: ${{ needs.Initialization.outputs.projectDependenciesJson }}
104 | secrets: 'licenseFileUrl,codeSignCertificateUrl,*codeSignCertificatePassword,keyVaultCertificateUrl,*keyVaultCertificatePassword,keyVaultClientId,gitHubPackagesContext,applicationInsightsConnectionString'
105 | artifactsRetentionDays: ${{ fromJson(needs.Initialization.outputs.artifactsRetentionDays) }}
106 | artifactsNameSuffix: 'NextMajor'
107 |
108 | PostProcess:
109 | needs: [ Initialization, Build ]
110 | if: always()
111 | runs-on: [ windows-latest ]
112 | steps:
113 | - name: Checkout
114 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
115 |
116 | - name: Finalize the workflow
117 | id: PostProcess
118 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
119 | env:
120 | GITHUB_TOKEN: ${{ github.token }}
121 | with:
122 | shell: powershell
123 | telemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
124 | currentJobContext: ${{ toJson(job) }}
125 |
--------------------------------------------------------------------------------
/.github/workflows/NextMinor.yaml:
--------------------------------------------------------------------------------
1 | name: ' Test Next Minor'
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: '0 0 15 * *'
7 |
8 | permissions:
9 | actions: read
10 | contents: read
11 | id-token: write
12 |
13 | defaults:
14 | run:
15 | shell: powershell
16 |
17 | env:
18 | workflowDepth: 1
19 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
20 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
21 |
22 | jobs:
23 | Initialization:
24 | needs: [ ]
25 | runs-on: [ windows-latest ]
26 | outputs:
27 | projects: ${{ steps.determineProjectsToBuild.outputs.ProjectsJson }}
28 | projectDependenciesJson: ${{ steps.determineProjectsToBuild.outputs.ProjectDependenciesJson }}
29 | buildOrderJson: ${{ steps.determineProjectsToBuild.outputs.BuildOrderJson }}
30 | workflowDepth: ${{ steps.DetermineWorkflowDepth.outputs.WorkflowDepth }}
31 | artifactsRetentionDays: ${{ steps.DetermineWorkflowDepth.outputs.ArtifactsRetentionDays }}
32 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
33 | steps:
34 | - name: Dump Workflow Information
35 | uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@v7.0
36 | with:
37 | shell: powershell
38 |
39 | - name: Checkout
40 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
41 | with:
42 | lfs: true
43 |
44 | - name: Initialize the workflow
45 | id: init
46 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
47 | with:
48 | shell: powershell
49 |
50 | - name: Read settings
51 | id: ReadSettings
52 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
53 | with:
54 | shell: powershell
55 | get: useGitSubmodules,shortLivedArtifactsRetentionDays
56 |
57 | - name: Read submodules token
58 | id: ReadSubmodulesToken
59 | if: env.useGitSubmodules != 'false' && env.useGitSubmodules != ''
60 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
61 | with:
62 | shell: powershell
63 | gitHubSecrets: ${{ toJson(secrets) }}
64 | getSecrets: '-gitSubmodulesToken'
65 |
66 | - name: Checkout Submodules
67 | if: env.useGitSubmodules != 'false' && env.useGitSubmodules != ''
68 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
69 | with:
70 | lfs: true
71 | submodules: ${{ env.useGitSubmodules }}
72 | token: '${{ fromJson(steps.ReadSubmodulesToken.outputs.Secrets).gitSubmodulesToken }}'
73 |
74 | - name: Determine Workflow Depth
75 | id: DetermineWorkflowDepth
76 | run: |
77 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "WorkflowDepth=$($env:workflowDepth)"
78 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "ArtifactsRetentionDays=$($env:shortLivedArtifactsRetentionDays)"
79 |
80 | - name: Determine Projects To Build
81 | id: determineProjectsToBuild
82 | uses: microsoft/AL-Go-Actions/DetermineProjectsToBuild@v7.0
83 | with:
84 | shell: powershell
85 | maxBuildDepth: ${{ env.workflowDepth }}
86 |
87 | Build:
88 | needs: [ Initialization ]
89 | if: (!failure()) && (!cancelled()) && fromJson(needs.Initialization.outputs.buildOrderJson)[0].projectsCount > 0
90 | strategy:
91 | matrix:
92 | include: ${{ fromJson(needs.Initialization.outputs.buildOrderJson)[0].buildDimensions }}
93 | fail-fast: false
94 | name: Build ${{ matrix.projectName }} (${{ matrix.buildMode }})
95 | uses: ./.github/workflows/_BuildALGoProject.yaml
96 | secrets: inherit
97 | with:
98 | shell: ${{ matrix.githubRunnerShell }}
99 | runsOn: ${{ matrix.githubRunner }}
100 | project: ${{ matrix.project }}
101 | projectName: ${{ matrix.projectName }}
102 | buildMode: ${{ matrix.buildMode }}
103 | projectDependenciesJson: ${{ needs.Initialization.outputs.projectDependenciesJson }}
104 | secrets: 'licenseFileUrl,codeSignCertificateUrl,*codeSignCertificatePassword,keyVaultCertificateUrl,*keyVaultCertificatePassword,keyVaultClientId,gitHubPackagesContext,applicationInsightsConnectionString'
105 | artifactsRetentionDays: ${{ fromJson(needs.Initialization.outputs.artifactsRetentionDays) }}
106 | artifactsNameSuffix: 'NextMinor'
107 |
108 | PostProcess:
109 | needs: [ Initialization, Build ]
110 | if: always()
111 | runs-on: [ windows-latest ]
112 | steps:
113 | - name: Checkout
114 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
115 |
116 | - name: Finalize the workflow
117 | id: PostProcess
118 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
119 | env:
120 | GITHUB_TOKEN: ${{ github.token }}
121 | with:
122 | shell: powershell
123 | telemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
124 | currentJobContext: ${{ toJson(job) }}
125 |
--------------------------------------------------------------------------------
/Dependency-Graph/src/Setup/DependencyGraphSetupANJ.Table.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Table DependencyGraphSetup_ANJ (ID 80800).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | table 80800 DependencyGraphSetup_ANJ
6 | {
7 | Access = Public;
8 | Caption = 'Dependency Graph Setup';
9 | DataClassification = CustomerContent;
10 | DrillDownPageId = DependencyGraphSetup_ANJ;
11 | Extensible = true;
12 | LookupPageId = DependencyGraphSetup_ANJ;
13 |
14 | fields
15 | {
16 | field(1; PrimaryKey; Code[10])
17 | {
18 | AllowInCustomizations = Never;
19 | Caption = 'Primary Key';
20 | NotBlank = false;
21 | }
22 | field(2; ClientID; Text[2048])
23 | {
24 | Caption = 'Client ID';
25 | ToolTip = 'Specifies the value of the Client ID field.';
26 | }
27 | field(3; Secret; Text[2048])
28 | {
29 | Caption = 'Secret';
30 | ToolTip = 'Specifies the value of the Secret field.';
31 | }
32 | field(4; ScopePTEFigure; Enum GeometricFigure_ANJ)
33 | {
34 | Caption = 'PTE Scope';
35 | InitValue = SquareRectangle;
36 | ToolTip = 'Specifies the value of the Scope PTE Figure field.';
37 | }
38 | field(5; ScopeGlobalFigure; Enum GeometricFigure_ANJ)
39 | {
40 | Caption = 'Global Scope';
41 | InitValue = SquareRectangle;
42 | ToolTip = 'Specifies the value of the Scope Global Figure field.';
43 | }
44 | field(6; ScopeDevFigure; Enum GeometricFigure_ANJ)
45 | {
46 | Caption = 'Dev Scope';
47 | InitValue = SquareRectangle;
48 | ToolTip = 'Specifies the value of the Scope Dev Figure field.';
49 | }
50 | field(7; IncludeMicrosoftApps; Boolean)
51 | {
52 | Caption = 'Include Microsoft Apps';
53 | ToolTip = 'Specifies the value of the Include Microsoft Apps field.';
54 | }
55 | field(8; DateLastGeneration; Date)
56 | {
57 | Caption = 'Date';
58 | ToolTip = 'Specifies the value of the Date Last Generation field.';
59 | }
60 | field(9; TimeLastGeneration; Time)
61 | {
62 | Caption = 'Time';
63 | ToolTip = 'Specifies the value of the Time Last Generation field.';
64 | }
65 | field(10; Markdown; Blob)
66 | {
67 | Caption = 'Content Text';
68 | }
69 | field(11; Picture; Media)
70 | {
71 | Caption = 'Picture';
72 | }
73 | field(12; IncludeLinkText; Boolean)
74 | {
75 | Caption = 'Include Link Text';
76 | ToolTip = 'Specifies the value of the Include Link text field.';
77 | }
78 | field(13; DateLastGenerationMarkdown; Date)
79 | {
80 | Caption = 'Date';
81 | ToolTip = 'Specifies the value of the Date Last Generation field.';
82 | }
83 | field(14; TimeLastGenerationMarkdown; Time)
84 | {
85 | Caption = 'Time';
86 | ToolTip = 'Specifies the value of the Time Last Generation field.';
87 | }
88 | field(15; MarkdownMermaid; Blob)
89 | {
90 | Caption = 'Mermaid Content Text';
91 | }
92 | field(16; FillingProcessingTables; Enum FillingProcessingTables_ANJ)
93 | {
94 | Caption = 'Filling Processing Tables';
95 | InitValue = WSAndModuleDependencyInfo;
96 | ToolTip = 'Specifies the value of the Filling Processing Tables field.';
97 | }
98 | }
99 | keys
100 | {
101 | key(PK; PrimaryKey)
102 | {
103 | Clustered = true;
104 | }
105 | }
106 | fieldgroups
107 | {
108 | fieldgroup(DropDown; ScopePTEFigure, ScopeGlobalFigure, ScopeDevFigure)
109 | {
110 | }
111 | fieldgroup(Brick; ScopePTEFigure, ScopeGlobalFigure, ScopeDevFigure)
112 | {
113 | }
114 | }
115 |
116 | ///
117 | /// SetMarkdown.
118 | ///
119 | /// Text.
120 | /// Integer.
121 | internal procedure SetMarkdown(AuxText: Text; FieldNo: Integer)
122 | var
123 | AuxOutStream: OutStream;
124 | begin
125 | Clear(HasBeenRead);
126 | case FieldNo of
127 | FieldNo(Markdown):
128 | Markdown.CreateOutStream(AuxOutStream);
129 | FieldNo(MarkdownMermaid):
130 | MarkdownMermaid.CreateOutStream(AuxOutStream);
131 | end;
132 | AuxOutStream.Write(AuxText);
133 | end;
134 |
135 | ///
136 | /// GetInstance.
137 | ///
138 | procedure GetInstance()
139 | begin
140 | if HasBeenRead then
141 | exit;
142 |
143 | if not Get() then begin
144 | Init();
145 | Insert(true);
146 | end;
147 | HasBeenRead := true;
148 | end;
149 |
150 | var
151 | HasBeenRead: Boolean;
152 | }
--------------------------------------------------------------------------------
/Dependency-Graph/src/ExtensionTable/GenerateExtensionTableANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "GenerateExtensionTable_ANJ" (ID 80805).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80805 GenerateExtensionTable_ANJ
6 | {
7 | Access = Public;
8 | Permissions = tabledata Extensions_ANJ = RIM;
9 |
10 | ///
11 | /// Generate.
12 | ///
13 | internal procedure Generate()
14 | var
15 | Extensions: Record Extensions_ANJ;
16 | IsHandled: Boolean;
17 | begin
18 | OnBeforeGenerateExtensionTable(Extensions, IsHandled);
19 | DoGenerateExtensionTable(IsHandled);
20 | OnAfterGenerateExtensionTable(Extensions);
21 | end;
22 |
23 | ///
24 | /// DoGenerateExtensionTable.
25 | ///
26 | /// Boolean.
27 | local procedure DoGenerateExtensionTable(IsHandled: Boolean)
28 | var
29 | FillingProcessingTables: Interface IFillingProcessingTables_ANJ;
30 | ResponseText: Text;
31 | begin
32 | if IsHandled then
33 | exit;
34 |
35 | DependencyGraphFacade.GetInterfaceFillProcessingTables(FillingProcessingTables);
36 | ResponseText := FillingProcessingTables.GetExtensions();
37 | if ResponseText <> '' then
38 | PopulateResponse(ResponseText);
39 | end;
40 |
41 | ///
42 | /// PopulateResponse.
43 | ///
44 | /// Text.
45 | local procedure PopulateResponse(ResponseText: Text)
46 | var
47 | ResponseJsonArray: JsonArray;
48 | SingleJsonObject: JsonToken;
49 | JsonValueArry: Text;
50 | begin
51 | JsonValueArry := JSONMethods.GetJsonValue(ValueJsonArryTok, ResponseText);
52 | if not ResponseJsonArray.ReadFrom(JsonValueArry) then
53 | Error(ReadingJsonErr);
54 |
55 | DependencyGraphFacade.InitializeNumberSequence();
56 | foreach SingleJsonObject in ResponseJsonArray do
57 | InsertTableLines(SingleJsonObject);
58 | end;
59 |
60 | ///
61 | /// InsertTableLines.
62 | ///
63 | /// JsonToken.
64 | local procedure InsertTableLines(SingleJsonObject: JsonToken)
65 | var
66 | Extensions: Record Extensions_ANJ;
67 | AuxiliaryText: Text;
68 | Name: Text;
69 | begin
70 | if not SingleJsonObject.WriteTo(AuxiliaryText) then
71 | Error(ReadingJsonErr);
72 |
73 | if JSONMethods.GetJsonValue(IsInstalledTok, AuxiliaryText) = FalseTok then
74 | exit;
75 |
76 | Extensions.Init();
77 | Extensions.Validate(AppID, JSONMethods.GetJsonValue(IdTok, AuxiliaryText));
78 | Extensions.Insert(true);
79 | Name := JSONMethods.GetJsonValue(DisplayNameTok, AuxiliaryText);
80 | Extensions.Validate(Name, CopyStr(Name, 1, 2048));
81 | Extensions.Validate(DisplayName, CopyStr(Name, 1, 2048));
82 | Extensions.Validate(Publisher, CopyStr(JSONMethods.GetJsonValue(PublisherTok, AuxiliaryText), 1, 2048));
83 |
84 | case JSONMethods.GetJsonValue(PublishedAsTok, AuxiliaryText) of
85 | Format(Enum::ExtensionScope_ANJ::PTE):
86 | Extensions.Validate(PublishedAs, Enum::ExtensionScope_ANJ::PTE);
87 | Format(Enum::ExtensionScope_ANJ::Global):
88 | Extensions.Validate(PublishedAs, Enum::ExtensionScope_ANJ::Global);
89 | Format(Enum::ExtensionScope_ANJ::Dev):
90 | Extensions.Validate(PublishedAs, Enum::ExtensionScope_ANJ::Dev);
91 | end;
92 |
93 | Extensions.Validate(ShowInGraph, true);
94 | Extensions.Validate(Identity, CopyStr(DependencyGraphFacade.GetNextNumberSequence(), 1, 2048));
95 | Extensions.Modify(true);
96 | end;
97 |
98 | ///
99 | /// OnBeforeGenerateExtensionTable.
100 | ///
101 | /// Record Extensions_ANJ.
102 | /// VAR Boolean.
103 | [IntegrationEvent(false, false)]
104 | local procedure OnBeforeGenerateExtensionTable(Extensions: Record Extensions_ANJ; var IsHandled: Boolean)
105 | begin
106 | end;
107 |
108 | ///
109 | /// OnAfterGenerateExtensionTable.
110 | ///
111 | /// Record Extensions_ANJ.
112 | [IntegrationEvent(false, false)]
113 | local procedure OnAfterGenerateExtensionTable(Extensions: Record Extensions_ANJ)
114 | begin
115 | end;
116 |
117 | var
118 | DependencyGraphFacade: Codeunit DependencyGraphFacade_ANJ;
119 | JSONMethods: Codeunit JSONMethods_ANJ;
120 | DisplayNameTok: Label 'displayName', Locked = true;
121 | FalseTok: Label 'false', Locked = true;
122 | IdTok: Label 'id', Locked = true;
123 | IsInstalledTok: Label 'isInstalled', Locked = true;
124 | PublishedAsTok: Label 'publishedAs', Locked = true;
125 | PublisherTok: Label 'publisher', Locked = true;
126 | ReadingJsonErr: Label 'Error reading JSON response.';
127 | ValueJsonArryTok: Label 'value', Locked = true;
128 | }
--------------------------------------------------------------------------------
/.github/workflows/PullRequestHandler.yaml:
--------------------------------------------------------------------------------
1 | name: 'Pull Request Build'
2 |
3 | on:
4 | pull_request_target:
5 | branches: [ 'main' ]
6 |
7 | concurrency:
8 | group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
9 | cancel-in-progress: true
10 |
11 | defaults:
12 | run:
13 | shell: powershell
14 |
15 | permissions:
16 | actions: read
17 | contents: read
18 | id-token: write
19 | pull-requests: read
20 |
21 | env:
22 | workflowDepth: 1
23 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
24 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
25 |
26 | jobs:
27 | PregateCheck:
28 | if: (github.event.pull_request.base.repo.full_name != github.event.pull_request.head.repo.full_name) && (github.event_name != 'pull_request')
29 | runs-on: windows-latest
30 | steps:
31 | - uses: microsoft/AL-Go-Actions/VerifyPRChanges@v7.0
32 |
33 | Initialization:
34 | needs: [ PregateCheck ]
35 | if: (!failure() && !cancelled())
36 | runs-on: [ windows-latest ]
37 | outputs:
38 | projects: ${{ steps.determineProjectsToBuild.outputs.ProjectsJson }}
39 | projectDependenciesJson: ${{ steps.determineProjectsToBuild.outputs.ProjectDependenciesJson }}
40 | buildOrderJson: ${{ steps.determineProjectsToBuild.outputs.BuildOrderJson }}
41 | baselineWorkflowRunId: ${{ steps.determineProjectsToBuild.outputs.BaselineWorkflowRunId }}
42 | baselineWorkflowSHA: ${{ steps.determineProjectsToBuild.outputs.BaselineWorkflowSHA }}
43 | workflowDepth: ${{ steps.DetermineWorkflowDepth.outputs.WorkflowDepth }}
44 | artifactsRetentionDays: ${{ steps.DetermineWorkflowDepth.outputs.ArtifactsRetentionDays }}
45 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
46 | steps:
47 | - name: Dump Workflow Information
48 | uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@v7.0
49 | with:
50 | shell: powershell
51 |
52 | - name: Checkout
53 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
54 | with:
55 | lfs: true
56 | ref: ${{ github.event_name == 'pull_request' && github.sha || format('refs/pull/{0}/merge', github.event.pull_request.number) }}
57 |
58 | - name: Initialize the workflow
59 | id: init
60 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
61 | with:
62 | shell: powershell
63 |
64 | - name: Read settings
65 | id: ReadSettings
66 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
67 | with:
68 | shell: powershell
69 | get: shortLivedArtifactsRetentionDays
70 |
71 | - name: Determine Workflow Depth
72 | id: DetermineWorkflowDepth
73 | run: |
74 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "WorkflowDepth=$($env:workflowDepth)"
75 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "ArtifactsRetentionDays=$($env:shortLivedArtifactsRetentionDays)"
76 |
77 | - name: Determine Projects To Build
78 | id: determineProjectsToBuild
79 | uses: microsoft/AL-Go-Actions/DetermineProjectsToBuild@v7.0
80 | with:
81 | shell: powershell
82 | maxBuildDepth: ${{ env.workflowDepth }}
83 |
84 | Build:
85 | needs: [ Initialization ]
86 | if: (!failure()) && (!cancelled()) && fromJson(needs.Initialization.outputs.buildOrderJson)[0].projectsCount > 0
87 | strategy:
88 | matrix:
89 | include: ${{ fromJson(needs.Initialization.outputs.buildOrderJson)[0].buildDimensions }}
90 | fail-fast: false
91 | name: Build ${{ matrix.projectName }} (${{ matrix.buildMode }})
92 | uses: ./.github/workflows/_BuildALGoProject.yaml
93 | secrets: inherit
94 | with:
95 | shell: ${{ matrix.githubRunnerShell }}
96 | runsOn: ${{ matrix.githubRunner }}
97 | checkoutRef: ${{ github.event_name == 'pull_request' && github.sha || format('refs/pull/{0}/merge', github.event.pull_request.number) }}
98 | project: ${{ matrix.project }}
99 | projectName: ${{ matrix.projectName }}
100 | buildMode: ${{ matrix.buildMode }}
101 | projectDependenciesJson: ${{ needs.Initialization.outputs.projectDependenciesJson }}
102 | baselineWorkflowRunId: ${{ needs.Initialization.outputs.baselineWorkflowRunId }}
103 | baselineWorkflowSHA: ${{ needs.Initialization.outputs.baselineWorkflowSHA }}
104 | secrets: 'licenseFileUrl,keyVaultCertificateUrl,*keyVaultCertificatePassword,keyVaultClientId,gitHubPackagesContext,applicationInsightsConnectionString'
105 | artifactsRetentionDays: ${{ fromJson(needs.Initialization.outputs.artifactsRetentionDays) }}
106 | artifactsNameSuffix: 'PR${{ github.event.number }}'
107 | useArtifactCache: true
108 |
109 | StatusCheck:
110 | needs: [ Initialization, Build ]
111 | if: (!cancelled())
112 | runs-on: [ windows-latest ]
113 | name: Pull Request Status Check
114 | steps:
115 | - name: Pull Request Status Check
116 | id: PullRequestStatusCheck
117 | uses: microsoft/AL-Go-Actions/PullRequestStatusCheck@v7.0
118 | env:
119 | GITHUB_TOKEN: ${{ github.token }}
120 | with:
121 | shell: powershell
122 |
123 | - name: Finalize the workflow
124 | id: PostProcess
125 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
126 | if: success() || failure()
127 | env:
128 | GITHUB_TOKEN: ${{ github.token }}
129 | with:
130 | shell: powershell
131 | telemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
132 | currentJobContext: ${{ toJson(job) }}
133 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | [email](mailto:anovoa@novoadev.com).
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/.github/copilot-instructions.md:
--------------------------------------------------------------------------------
1 | # Instructions for Copilot
2 | ## Code Generation
3 | - All code must be written in **English**.
4 | - Explicitly declare **access modifiers** for procedures (`local`, `internal`, `public`), prioritizing `local` or `internal` as appropriate.
5 | - When using `Insert`, `Modify`, `Delete` or `DeleteAll`, always pass `true` as a parameter to ensure full execution of business logic. Example: `Insert(true)`, `Modify(true)`.
6 | - When using `FindSet()`, specify the parameter according to the block’s intent. If the table will be modified, use `FindSet(true)`; otherwise, use `FindSet(false)`.
7 | - When assigning a value to a field, **do not assign it directly**; use `Validate` to ensure the correct execution of related logic.
8 | - Do not hardcode **text** in messages or user interaction elements. Use `Labels` to facilitate translation file generation.
9 | - Do not declare multiple variables of the same type in a single line. Incorrect example: `MyInt1, MyInt2, MyInt3: Integer;`.
10 | - Control all graphical interface elements (`Message`, `Confirm`, etc.) using `GuiAllowed()` to prevent errors in background processes.
11 | - Use the correct suffix in `Label`-type variables according to their purpose:
12 | - `Msg` – Messages.
13 | - `Tok` – Constants such as tokens, URLs, etc.
14 | - `Err` – Errors.
15 | - `Qst` – For `StrMenu` or `Confirm`.
16 | - `Lbl` – Texts that require translation (`Label`, `Caption`).
17 | - Apply **SOLID** principles in code design:
18 | - **SRP** – Single Responsibility Principle: Each module should have only one reason to change.
19 | - **OCP** – Open/Closed Principle: Code should be open for extension (events, interfaces) but closed for direct modification.
20 | - **LSP** – Liskov Substitution Principle: Use interfaces to ensure implementations can be interchanged without altering expected behavior.
21 | - **ISP** – Interface Segregation Principle: Split large interfaces into several smaller, more specific ones.
22 | - **DIP** – Dependency Inversion Principle: Modules should depend on abstractions (interfaces, events), not on concrete implementations.
23 | - Do not use `Find(-)`, `Find(+)`, `Find(=)`, etc. Use `FindFirst()`, `FindLast()` or `FindSet()` instead.
24 | - Always use the most appropriate data type: `primitive types`, `List`, `Dictionary`, `JsonObject`, `TextBuilder`, among others.
25 | - All objects and procedures must be properly documented using **XML comments**.
26 | - Do not use `//` or `/* */` comments; all documentation must be provided through XML blocks above procedures and objects.
27 | - Avoid adding redundant or unhelpful comments.
28 | - When adding elements to pages, prioritize `AddFirst` or `AddLast` over `AddAfter` or `AddBefore`.
29 | - Ensure **referential integrity** when linking fields.
30 | - In event subscriptions, explicitly configure: `SkipOnMissingLicense = false` and `SkipOnMissingPermission = false`.
31 | - Remove unnecessary lines such as redundant calls to `Reset` or default initializations for newly created variables.
32 | - When a variable refers to an object, use the exact **same name** as the object, with no spaces, quotes, or special characters.
33 | - Consider the **cyclomatic complexity** of the code.
34 | - Simplify control structures and avoid unnecessary `begin/end` blocks.
35 | - If a function has more than six parameters, consider using structures like `Dictionary`, `Record`, or others to simplify the call.
36 | - Always declare the **exact length** for `Text` or `Code` type fields and variables.
37 | - If adding `FlowField` type fields in a table, make sure to set `Editable = false`.
38 | - Use **PascalCase** for naming objects, functions, and variables.
39 | - Do not use `Option`; create an `Enum` type instead.
40 | - When subscribing to an event that receives a table as a parameter, first check if it is **temporary**. If it is, use `exit;`.
41 | - Do not include code directly within **triggers**; create separate functions that can be called from the trigger.
42 | - In new tables, add the `DataClassification` property **only at the table level**, **never on individual fields**.
43 | - In table extensions, **do not add `DataClassification` at the table level**; this will cause an error. Add it only to specific fields.
44 | - Avoid **hardcoding** object IDs. Use the **object name** instead.
45 | - Structure the code according to its purpose. Prioritize in the following order:
46 | - Use **codeunits** for general and reusable logic.
47 | - Use **tables** for logic related to entities.
48 | - Use **pages** exclusively for user interaction.
49 | - All variable names must be **clear and descriptive**. Avoid meaningless abbreviations or single letters.
50 | - When creating a table, define the `DrillDownPageId` and `LookupPageId` properties, referencing the corresponding list-type page.
51 | - Use the condition directly in validations. Avoid constructs like `if Condition = true` or `= false`.
52 |
53 | ## Code Review
54 | - Verify that all added procedures are documented with XML blocks.
55 | - Ensure the code complies with all established generation instructions.
56 | - Make code review comments in English and respectfully insulting the developer in Spanish as a loving reminder that they can do better.
57 |
58 | ## Commit Message Generation
59 | - The message must be written in English.
60 | - Use the following format:
61 | `[Type] - [Branch Name] - [Brief Description]`
62 | Example: `Bug - EV001 - Optimización del cálculo de impuestos`
63 | - Include a clear, precise, and concise description of the changes made.
64 |
65 | ## Pull Request Title and Description Generation Instructions
66 | - Titles: Use a clear format that includes the main purpose of the changes.
67 | Example: `Fix: Error in discount calculation logic`
68 | - Descriptions: Include an overview of the changes made, explaining their impact and purpose.
69 |
--------------------------------------------------------------------------------
/.github/workflows/UpdateGitHubGoSystemFiles.yaml:
--------------------------------------------------------------------------------
1 | name: ' Update AL-Go System Files'
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | templateUrl:
7 | description: Template Repository URL (current is https://github.com/microsoft/AL-Go-PTE@main)
8 | required: false
9 | default: ''
10 | downloadLatest:
11 | description: Download latest from template repository
12 | type: boolean
13 | default: true
14 | directCommit:
15 | description: Direct Commit?
16 | type: boolean
17 | default: false
18 | includeBranches:
19 | description: Specify a comma-separated list of branches to update. Wildcards are supported. The AL-Go settings will be read for every branch. Leave empty to update the current branch only.
20 | required: false
21 | default: ''
22 | schedule:
23 | - cron: '0 0 1,15 * *'
24 |
25 | permissions:
26 | actions: read
27 | contents: read
28 | id-token: write
29 |
30 | defaults:
31 | run:
32 | shell: powershell
33 |
34 | env:
35 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
36 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
37 |
38 | jobs:
39 | Initialize:
40 | runs-on: windows-latest
41 | name: Initialize
42 | outputs:
43 | UpdateBranches: ${{ steps.GetBranches.outputs.Result }}
44 | TemplateUrl: ${{ steps.DetermineTemplateUrl.outputs.TemplateUrl }}
45 | steps:
46 | - name: Checkout
47 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
48 |
49 | - name: Read settings
50 | id: ReadSettings
51 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
52 | with:
53 | shell: powershell
54 | get: templateUrl
55 |
56 | - name: Get Workflow Multi-Run Branches
57 | id: GetBranches
58 | uses: microsoft/AL-Go-Actions/GetWorkflowMultiRunBranches@v7.0
59 | with:
60 | shell: powershell
61 | includeBranches: ${{ github.event.inputs.includeBranches }}
62 |
63 | - name: Determine Template URL
64 | id: DetermineTemplateUrl
65 | env:
66 | TemplateUrlAsInput: '${{ github.event.inputs.templateUrl }}'
67 | run: |
68 | $templateUrl = $env:templateUrl # Available from ReadSettings step
69 | if ($ENV:TemplateUrlAsInput) {
70 | # Use the input value if it is provided
71 | $templateUrl = $ENV:TemplateUrlAsInput
72 | }
73 | Write-Host "Using template URL: $templateUrl"
74 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "TemplateUrl=$templateUrl"
75 |
76 | UpdateALGoSystemFiles:
77 | name: "[${{ matrix.branch }}] Update AL-Go System Files"
78 | needs: [ Initialize ]
79 | runs-on: [ windows-latest ]
80 | strategy:
81 | matrix:
82 | branch: ${{ fromJson(needs.Initialize.outputs.UpdateBranches).branches }}
83 | fail-fast: false
84 |
85 | steps:
86 | - name: Dump Workflow Information
87 | uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@v7.0
88 | with:
89 | shell: powershell
90 |
91 | - name: Checkout
92 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
93 | with:
94 | ref: ${{ matrix.branch }}
95 |
96 | - name: Initialize the workflow
97 | id: init
98 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
99 | with:
100 | shell: powershell
101 |
102 | - name: Read settings
103 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
104 | with:
105 | shell: powershell
106 | get: commitOptions
107 |
108 | - name: Read secrets
109 | id: ReadSecrets
110 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
111 | with:
112 | shell: powershell
113 | gitHubSecrets: ${{ toJson(secrets) }}
114 | getSecrets: 'ghTokenWorkflow'
115 |
116 | - name: Calculate Commit Options
117 | env:
118 | directCommit: '${{ github.event.inputs.directCommit }}'
119 | downloadLatest: '${{ github.event.inputs.downloadLatest }}'
120 | run: |
121 | $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0
122 | if('${{ github.event_name }}' -eq 'workflow_dispatch') {
123 | Write-Host "Using inputs from workflow_dispatch event"
124 | $directCommit = $env:directCommit
125 | $downloadLatest = $env:downloadLatest
126 | }
127 | else {
128 | Write-Host "Using inputs from commitOptions setting"
129 | $commitOptions = $env:commitOptions | ConvertFrom-Json # Available from ReadSettings step
130 | $directCommit=$(-not $commitOptions.createPullRequest)
131 | $downloadLatest=$true
132 | }
133 | Add-Content -Encoding UTF8 -Path $env:GITHUB_ENV -Value "directCommit=$directCommit"
134 | Add-Content -Encoding UTF8 -Path $env:GITHUB_ENV -Value "downloadLatest=$downloadLatest"
135 |
136 | - name: Update AL-Go system files
137 | uses: microsoft/AL-Go-Actions/CheckForUpdates@v7.0
138 | with:
139 | shell: powershell
140 | token: ${{ fromJson(steps.ReadSecrets.outputs.Secrets).ghTokenWorkflow }}
141 | downloadLatest: ${{ env.downloadLatest }}
142 | update: 'Y'
143 | templateUrl: ${{ needs.Initialize.outputs.TemplateUrl }}
144 | directCommit: ${{ env.directCommit }}
145 | updateBranch: ${{ matrix.branch }}
146 |
147 | - name: Finalize the workflow
148 | if: always()
149 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
150 | env:
151 | GITHUB_TOKEN: ${{ github.token }}
152 | with:
153 | shell: powershell
154 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
155 | currentJobContext: ${{ toJson(job) }}
156 |
--------------------------------------------------------------------------------
/.AL-Go/localDevEnv.ps1:
--------------------------------------------------------------------------------
1 | #
2 | # Script for creating local development environment
3 | # Please do not modify this script as it will be auto-updated from the AL-Go Template
4 | # Recommended approach is to use as is or add a script (freddyk-devenv.ps1), which calls this script with the user specific parameters
5 | #
6 | Param(
7 | [string] $containerName = "",
8 | [ValidateSet("UserPassword", "Windows")]
9 | [string] $auth = "",
10 | [pscredential] $credential = $null,
11 | [string] $licenseFileUrl = "",
12 | [switch] $fromVSCode,
13 | [switch] $accept_insiderEula,
14 | [switch] $clean
15 | )
16 |
17 | $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0
18 |
19 | function DownloadHelperFile {
20 | param(
21 | [string] $url,
22 | [string] $folder
23 | )
24 |
25 | $prevProgressPreference = $ProgressPreference; $ProgressPreference = 'SilentlyContinue'
26 | $name = [System.IO.Path]::GetFileName($url)
27 | Write-Host "Downloading $name from $url"
28 | $path = Join-Path $folder $name
29 | Invoke-WebRequest -UseBasicParsing -uri $url -OutFile $path
30 | $ProgressPreference = $prevProgressPreference
31 | return $path
32 | }
33 |
34 | try {
35 | Clear-Host
36 | Write-Host
37 | Write-Host -ForegroundColor Yellow @'
38 | _ _ _____ ______
39 | | | | | | __ \ | ____|
40 | | | ___ ___ __ _| | | | | | _____ __ |__ _ ____ __
41 | | | / _ \ / __/ _` | | | | | |/ _ \ \ / / __| | '_ \ \ / /
42 | | |____ (_) | (__ (_| | | | |__| | __/\ V /| |____| | | \ V /
43 | |______\___/ \___\__,_|_| |_____/ \___| \_/ |______|_| |_|\_/
44 |
45 | '@
46 |
47 | $tmpFolder = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString())"
48 | New-Item -Path $tmpFolder -ItemType Directory -Force | Out-Null
49 | $GitHubHelperPath = DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v7.0/Github-Helper.psm1' -folder $tmpFolder
50 | $ALGoHelperPath = DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v7.0/AL-Go-Helper.ps1' -folder $tmpFolder
51 | DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v7.0/Packages.json' -folder $tmpFolder | Out-Null
52 |
53 | Import-Module $GitHubHelperPath
54 | . $ALGoHelperPath -local
55 |
56 | $baseFolder = GetBaseFolder -folder $PSScriptRoot
57 | $project = GetProject -baseFolder $baseFolder -projectALGoFolder $PSScriptRoot
58 |
59 | Write-Host @'
60 |
61 | This script will create a docker based local development environment for your project.
62 |
63 | NOTE: You need to have Docker installed, configured and be able to create Business Central containers for this to work.
64 | If this fails, you can setup a cloud based development environment by running cloudDevEnv.ps1
65 |
66 | All apps and test apps will be compiled and published to the environment in the development scope.
67 | The script will also modify launch.json to have a Local Sandbox configuration point to your environment.
68 |
69 | '@
70 |
71 | $settings = ReadSettings -baseFolder $baseFolder -project $project -userName $env:USERNAME -workflowName 'localDevEnv'
72 |
73 | Write-Host "Checking System Requirements"
74 | $dockerProcess = (Get-Process "dockerd" -ErrorAction Ignore)
75 | if (!($dockerProcess)) {
76 | Write-Host -ForegroundColor Red "Dockerd process not found. Docker might not be started, not installed or not running Windows Containers."
77 | }
78 | if ($settings.keyVaultName) {
79 | if (-not (Get-Module -ListAvailable -Name 'Az.KeyVault')) {
80 | Write-Host -ForegroundColor Red "A keyvault name is defined in Settings, you need to have the Az.KeyVault PowerShell module installed (use Install-Module az) or you can set the keyVaultName to an empty string in the user settings file ($($ENV:UserName).settings.json)."
81 | }
82 | }
83 |
84 | Write-Host
85 |
86 | if (-not $containerName) {
87 | $containerName = Enter-Value `
88 | -title "Container name" `
89 | -question "Please enter the name of the container to create" `
90 | -default "bcserver" `
91 | -trimCharacters @('"',"'",' ')
92 | }
93 |
94 | if (-not $auth) {
95 | $auth = Select-Value `
96 | -title "Authentication mechanism for container" `
97 | -options @{ "Windows" = "Windows Authentication"; "UserPassword" = "Username/Password authentication" } `
98 | -question "Select authentication mechanism for container" `
99 | -default "UserPassword"
100 | }
101 |
102 | if (-not $credential) {
103 | if ($auth -eq "Windows") {
104 | $credential = Get-Credential -Message "Please enter your Windows Credentials" -UserName $env:USERNAME
105 | $CurrentDomain = "LDAP://" + ([ADSI]"").distinguishedName
106 | $domain = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,$credential.UserName,$credential.GetNetworkCredential().password)
107 | if ($null -eq $domain.name) {
108 | Write-Host -ForegroundColor Red "Unable to verify your Windows Credentials, you might not be able to authenticate to your container"
109 | }
110 | }
111 | else {
112 | $credential = Get-Credential -Message "Please enter username and password for your container" -UserName "admin"
113 | }
114 | }
115 |
116 | if (-not $licenseFileUrl) {
117 | if ($settings.type -eq "AppSource App") {
118 | $description = "When developing AppSource Apps for Business Central versions prior to 22, your local development environment needs the developer licensefile with permissions to your AppSource app object IDs"
119 | $default = "none"
120 | }
121 | else {
122 | $description = "When developing PTEs, you can optionally specify a developer licensefile with permissions to object IDs of your dependant apps"
123 | $default = "none"
124 | }
125 |
126 | $licenseFileUrl = Enter-Value `
127 | -title "LicenseFileUrl" `
128 | -description $description `
129 | -question "Local path or a secure download URL to license file " `
130 | -default $default `
131 | -doNotConvertToLower `
132 | -trimCharacters @('"',"'",' ')
133 | }
134 |
135 | if ($licenseFileUrl -eq "none") {
136 | $licenseFileUrl = ""
137 | }
138 |
139 | CreateDevEnv `
140 | -kind local `
141 | -caller local `
142 | -containerName $containerName `
143 | -baseFolder $baseFolder `
144 | -project $project `
145 | -auth $auth `
146 | -credential $credential `
147 | -licenseFileUrl $licenseFileUrl `
148 | -accept_insiderEula:$accept_insiderEula `
149 | -clean:$clean
150 | }
151 | catch {
152 | Write-Host -ForegroundColor Red "Error: $($_.Exception.Message)`nStacktrace: $($_.scriptStackTrace)"
153 | }
154 | finally {
155 | if ($fromVSCode) {
156 | Read-Host "Press ENTER to close this window"
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml:
--------------------------------------------------------------------------------
1 | name: ' Create Online Dev. Environment'
2 |
3 | run-name: "Create Online Dev. Environment for [${{ github.ref_name }} / ${{ github.event.inputs.project }}]"
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | project:
9 | description: Project name if the repository is setup for multiple projects
10 | required: false
11 | default: '.'
12 | environmentName:
13 | description: Name of the online environment
14 | required: true
15 | reUseExistingEnvironment:
16 | description: Reuse environment if it exists?
17 | type: boolean
18 | default: false
19 | directCommit:
20 | description: Direct Commit?
21 | type: boolean
22 | default: false
23 | useGhTokenWorkflow:
24 | description: Use GhTokenWorkflow for PR/Commit?
25 | type: boolean
26 | default: false
27 |
28 | permissions:
29 | actions: read
30 | contents: write
31 | id-token: write
32 | pull-requests: write
33 |
34 | defaults:
35 | run:
36 | shell: powershell
37 |
38 | env:
39 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
40 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
41 |
42 | jobs:
43 | Initialization:
44 | needs: [ ]
45 | runs-on: [ windows-latest ]
46 | outputs:
47 | deviceCode: ${{ steps.authenticate.outputs.deviceCode }}
48 | githubRunner: ${{ steps.ReadSettings.outputs.GitHubRunnerJson }}
49 | githubRunnerShell: ${{ steps.ReadSettings.outputs.GitHubRunnerShell }}
50 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
51 | steps:
52 | - name: Dump Workflow Information
53 | uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@v7.0
54 | with:
55 | shell: powershell
56 |
57 | - name: Checkout
58 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
59 |
60 | - name: Initialize the workflow
61 | id: init
62 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
63 | with:
64 | shell: powershell
65 |
66 | - name: Read settings
67 | id: ReadSettings
68 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
69 | with:
70 | shell: powershell
71 |
72 | - name: Read secrets
73 | id: ReadSecrets
74 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
75 | with:
76 | shell: powershell
77 | gitHubSecrets: ${{ toJson(secrets) }}
78 | getSecrets: 'adminCenterApiCredentials'
79 |
80 | - name: Check AdminCenterApiCredentials / Initiate Device Login (open to see code)
81 | id: authenticate
82 | run: |
83 | $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0
84 | $settings = $env:Settings | ConvertFrom-Json
85 | if ('${{ fromJson(steps.ReadSecrets.outputs.Secrets).adminCenterApiCredentials }}') {
86 | Write-Host "AdminCenterApiCredentials provided in secret $($settings.adminCenterApiCredentialsSecretName)!"
87 | Add-Content -Encoding UTF8 -path $ENV:GITHUB_STEP_SUMMARY -value "Admin Center Api Credentials was provided in a secret called $($settings.adminCenterApiCredentialsSecretName). Using this information for authentication."
88 | }
89 | else {
90 | Write-Host "AdminCenterApiCredentials not provided, initiating Device Code flow"
91 | $ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1"
92 | $webClient = New-Object System.Net.WebClient
93 | $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v7.0/AL-Go-Helper.ps1', $ALGoHelperPath)
94 | . $ALGoHelperPath
95 | DownloadAndImportBcContainerHelper
96 | $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0))
97 | Add-Content -Encoding UTF8 -path $ENV:GITHUB_STEP_SUMMARY -value "AL-Go needs access to the Business Central Admin Center Api and could not locate a secret called $($settings.adminCenterApiCredentialsSecretName) (https://aka.ms/ALGoSettings#AdminCenterApiCredentialsSecretName)`n`n$($authContext.message)"
98 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "deviceCode=$($authContext.deviceCode)"
99 | }
100 |
101 | CreateDevelopmentEnvironment:
102 | needs: [ Initialization ]
103 | runs-on: ${{ fromJson(needs.Initialization.outputs.githubRunner) }}
104 | defaults:
105 | run:
106 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
107 | name: Create Development Environment
108 | env:
109 | deviceCode: ${{ needs.Initialization.outputs.deviceCode }}
110 | steps:
111 | - name: Checkout
112 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
113 |
114 | - name: Read settings
115 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
116 | with:
117 | shell: powershell
118 |
119 | - name: Read secrets
120 | id: ReadSecrets
121 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
122 | with:
123 | shell: powershell
124 | gitHubSecrets: ${{ toJson(secrets) }}
125 | getSecrets: 'adminCenterApiCredentials,TokenForPush'
126 | useGhTokenWorkflowForPush: '${{ github.event.inputs.useGhTokenWorkflow }}'
127 |
128 | - name: Set AdminCenterApiCredentials
129 | id: SetAdminCenterApiCredentials
130 | run: |
131 | if ($env:deviceCode) {
132 | $adminCenterApiCredentials = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("{""deviceCode"":""$($env:deviceCode)""}"))
133 | }
134 | else {
135 | $adminCenterApiCredentials = '${{ fromJson(steps.ReadSecrets.outputs.Secrets).adminCenterApiCredentials }}'
136 | }
137 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -value "adminCenterApiCredentials=$adminCenterApiCredentials"
138 |
139 | - name: Create Development Environment
140 | uses: microsoft/AL-Go-Actions/CreateDevelopmentEnvironment@v7.0
141 | with:
142 | shell: powershell
143 | token: ${{ steps.ReadSecrets.outputs.TokenForPush }}
144 | environmentName: ${{ github.event.inputs.environmentName }}
145 | project: ${{ github.event.inputs.project }}
146 | reUseExistingEnvironment: ${{ github.event.inputs.reUseExistingEnvironment }}
147 | directCommit: ${{ github.event.inputs.directCommit }}
148 | adminCenterApiCredentials: ${{ steps.SetAdminCenterApiCredentials.outputs.adminCenterApiCredentials }}
149 |
150 | - name: Finalize the workflow
151 | if: always()
152 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
153 | env:
154 | GITHUB_TOKEN: ${{ github.token }}
155 | with:
156 | shell: powershell
157 | telemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
158 | currentJobContext: ${{ toJson(job) }}
159 |
--------------------------------------------------------------------------------
/Dependency-Graph/src/Markdown/MarkdownMgmtANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit MarkdownMgmt_ANJ (ID 80809).
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80809 MarkdownMgmt_ANJ
6 | {
7 | Access = Public;
8 | Permissions = tabledata Relations_ANJ = RD, tabledata Extensions_ANJ = RD, tabledata DependencyGraphSetup_ANJ = RM;
9 |
10 | ///
11 | /// GenerateGraph.
12 | ///
13 | internal procedure GenerateGraph()
14 | var
15 | IsHandled: Boolean;
16 | begin
17 | OnBeforeGenerateMarkdown(IsHandled);
18 | DoGenerateMarkdown(IsHandled);
19 | OnAfterGenerateMarkdown();
20 | end;
21 |
22 | ///
23 | /// DoGenerateMarkdown.
24 | ///
25 | /// Boolean.
26 | local procedure DoGenerateMarkdown(IsHandled: Boolean)
27 | var
28 | GrapTextBuilder: TextBuilder;
29 | begin
30 | if IsHandled then
31 | exit;
32 |
33 | GrapTextBuilder.AppendLine(Header2Tok);
34 | InsertRelationships(GrapTextBuilder);
35 | InsertAppWithoutRelationships(GrapTextBuilder);
36 |
37 | UpdateSetupTable(GrapTextBuilder.ToText())
38 | end;
39 |
40 | ///
41 | /// InsertRelationships.
42 | ///
43 | /// VAR TextBuilder.
44 | local procedure InsertRelationships(var GrapTextBuilder: TextBuilder)
45 | var
46 | Relations: Record Relations_ANJ;
47 | AppIdList: List of [Guid];
48 | begin
49 | Relations.SetLoadFields(SourceAppID, DestinationAppID, LinkText);
50 | Relations.SetRange(ShowInGraph, true);
51 | if Relations.IsEmpty() then
52 | exit;
53 |
54 | if Relations.FindSet(false) then
55 | repeat
56 | GrapTextBuilder.Append(GetExtensionText(Relations.SourceAppID, AppIdList));
57 | InsertLinkText(GrapTextBuilder, Relations.LinkText);
58 | GrapTextBuilder.AppendLine(GetExtensionText(Relations.DestinationAppID, AppIdList));
59 | until Relations.Next() = 0;
60 | end;
61 |
62 | ///
63 | /// GetExtensionText.
64 | ///
65 | /// Guid.
66 | /// VAR List of [Guid].
67 | /// Return value of type Text.
68 | local procedure GetExtensionText(AppGuid: Guid; var AppIdList: List of [Guid]): Text
69 | var
70 | Extensions: Record Extensions_ANJ;
71 | begin
72 |
73 | Extensions.SetLoadFields(Identity, Figure);
74 | if not Extensions.Get(AppGuid) then
75 | exit;
76 |
77 | if AppIdList.Contains(AppGuid) then
78 | exit(Extensions.Identity);
79 |
80 | AppIdList.Add(AppGuid);
81 | exit(Extensions.Figure);
82 | end;
83 |
84 | ///
85 | /// InsertLinkText.
86 | ///
87 | /// VAR TextBuilder.
88 | /// Text.
89 | local procedure InsertLinkText(var GrapTextBuilder: TextBuilder; LinkText: Text)
90 | begin
91 | if LinkText = '' then begin
92 | GrapTextBuilder.Append(ArrowLbl);
93 | exit;
94 | end;
95 |
96 | GrapTextBuilder.Append(StrSubstNo(LinkArrowLbl, LinkText));
97 | end;
98 |
99 | ///
100 | /// InsertAppWithoutRelationships.
101 | ///
102 | /// VAR TextBuilder.
103 | local procedure InsertAppWithoutRelationships(var GrapTextBuilder: TextBuilder)
104 | var
105 | Extensions: Record Extensions_ANJ;
106 | begin
107 | Extensions.SetRange(ShowInGraph, true);
108 | Extensions.SetRange(HasStartRelationships, false);
109 | Extensions.SetRange(HasRelationships, false);
110 | if Extensions.IsEmpty() then
111 | exit;
112 |
113 | if Extensions.FindSet(false) then
114 | repeat
115 | GrapTextBuilder.AppendLine(Extensions.Figure);
116 | until Extensions.Next() = 0;
117 | end;
118 |
119 | ///
120 | /// UpdateSetupTable.
121 | ///
122 | /// Text.
123 | local procedure UpdateSetupTable(MarkDownText: Text)
124 | var
125 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
126 | GraphTextBuilder: TextBuilder;
127 | begin
128 | if MarkDownText = '' then
129 | exit;
130 |
131 | GraphTextBuilder.AppendLine(Header1Tok);
132 | GraphTextBuilder.AppendLine(MarkDownText);
133 | GraphTextBuilder.AppendLine(FooterTok);
134 |
135 | DependencyGraphSetup.GetInstance();
136 | DependencyGraphSetup.SetMarkdown(GraphTextBuilder.ToText(), DependencyGraphSetup.FieldNo(Markdown));
137 | DependencyGraphSetup.SetMarkdown(MarkDownText, DependencyGraphSetup.FieldNo(MarkdownMermaid));
138 | DependencyGraphSetup.Validate(DateLastGenerationMarkdown, Today());
139 | DependencyGraphSetup.Validate(TimeLastGenerationMarkdown, Time());
140 | DependencyGraphSetup.Modify(true);
141 | end;
142 |
143 | ///
144 | /// DownloadMarkdown.
145 | ///
146 | internal procedure DownloadMarkdown()
147 | var
148 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
149 | AuxInStream: InStream;
150 | FileName: Text;
151 | begin
152 | DependencyGraphSetup.SetLoadFields(Markdown);
153 | DependencyGraphSetup.SetAutoCalcFields(Markdown);
154 | DependencyGraphSetup.GetInstance();
155 | DependencyGraphSetup.Markdown.CreateInStream(AuxInStream);
156 |
157 | if not DependencyGraphSetup.Markdown.HasValue() then
158 | exit;
159 |
160 | FileName := FileNameLbl;
161 | DownloadFromStream(AuxInStream, '', '', '', FileName);
162 | end;
163 |
164 | ///
165 | /// GetMarkdown.
166 | ///
167 | /// Integer.
168 | /// Return value of type Text.
169 | internal procedure GetMarkdown(FieldNo: Integer): Text
170 | var
171 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
172 | AuxInStream: InStream;
173 | AuxText: Text;
174 | begin
175 | DependencyGraphSetup.SetLoadFields(Markdown, MarkdownMermaid);
176 | DependencyGraphSetup.SetAutoCalcFields(Markdown, MarkdownMermaid);
177 | DependencyGraphSetup.GetInstance();
178 | case FieldNo of
179 | DependencyGraphSetup.FieldNo(Markdown):
180 | DependencyGraphSetup.Markdown.CreateInStream(AuxInStream);
181 | DependencyGraphSetup.FieldNo(MarkdownMermaid):
182 | DependencyGraphSetup.MarkdownMermaid.CreateInStream(AuxInStream);
183 | end;
184 |
185 | AuxInStream.Read(AuxText);
186 | exit(AuxText);
187 | end;
188 |
189 | ///
190 | /// OnBeforeGenerateMarkdown.
191 | ///
192 | ///
193 | [IntegrationEvent(false, false)]
194 | local procedure OnBeforeGenerateMarkdown(var IsHandled: Boolean)
195 | begin
196 | end;
197 |
198 | ///
199 | /// OnAfterGenerateMarkdown.
200 | ///
201 | [IntegrationEvent(false, false)]
202 | local procedure OnAfterGenerateMarkdown()
203 | begin
204 | end;
205 |
206 | var
207 | ArrowLbl: Label ' --> ';
208 | FileNameLbl: Label 'DependencyGraph.md';
209 | FooterTok: Label '```', Locked = true;
210 | Header1Tok: Label '```mermaid', Locked = true;
211 | Header2Tok: Label 'graph BT', Locked = true;
212 | LinkArrowLbl: Label ' -- %1 --> ', Comment = 'Placeholder %1 for the link text';
213 | }
--------------------------------------------------------------------------------
/.github/workflows/PublishToEnvironment.yaml:
--------------------------------------------------------------------------------
1 | name: ' Publish To Environment'
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | appVersion:
7 | description: App version to deploy to environment(s) (current, prerelease, draft, latest, version number or PR_)
8 | required: false
9 | default: 'current'
10 | environmentName:
11 | description: Environment mask to receive the new version (* for all, PROD* for all environments starting with PROD)
12 | required: true
13 |
14 | permissions:
15 | actions: read
16 | contents: read
17 | id-token: write
18 |
19 | defaults:
20 | run:
21 | shell: powershell
22 |
23 | env:
24 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
25 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
26 |
27 | jobs:
28 | Initialization:
29 | needs: [ ]
30 | runs-on: [ windows-latest ]
31 | outputs:
32 | environmentsMatrixJson: ${{ steps.DetermineDeploymentEnvironments.outputs.EnvironmentsMatrixJson }}
33 | environmentCount: ${{ steps.DetermineDeploymentEnvironments.outputs.EnvironmentCount }}
34 | deploymentEnvironmentsJson: ${{ steps.DetermineDeploymentEnvironments.outputs.DeploymentEnvironmentsJson }}
35 | deviceCode: ${{ steps.Authenticate.outputs.deviceCode }}
36 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
37 | steps:
38 | - name: Dump Workflow Information
39 | uses: microsoft/AL-Go-Actions/DumpWorkflowInfo@v7.0
40 | with:
41 | shell: powershell
42 |
43 | - name: Checkout
44 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
45 |
46 | - name: Initialize the workflow
47 | id: init
48 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v7.0
49 | with:
50 | shell: powershell
51 |
52 | - name: Read settings
53 | id: ReadSettings
54 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
55 | with:
56 | shell: powershell
57 |
58 | - name: Determine Deployment Environments
59 | id: DetermineDeploymentEnvironments
60 | uses: microsoft/AL-Go-Actions/DetermineDeploymentEnvironments@v7.0
61 | env:
62 | GITHUB_TOKEN: ${{ github.token }}
63 | with:
64 | shell: powershell
65 | getEnvironments: ${{ github.event.inputs.environmentName }}
66 | type: 'Publish'
67 |
68 | - name: EnvName
69 | id: envName
70 | if: steps.DetermineDeploymentEnvironments.outputs.UnknownEnvironment == 1
71 | run: |
72 | $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0
73 | $envName = '${{ fromJson(steps.DetermineDeploymentEnvironments.outputs.environmentsMatrixJson).matrix.include[0].environment }}'.split(' ')[0]
74 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "envName=$envName"
75 |
76 | - name: Read secrets
77 | id: ReadSecrets
78 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
79 | if: steps.DetermineDeploymentEnvironments.outputs.UnknownEnvironment == 1
80 | with:
81 | shell: powershell
82 | gitHubSecrets: ${{ toJson(secrets) }}
83 | getSecrets: '${{ steps.envName.outputs.envName }}-AuthContext,${{ steps.envName.outputs.envName }}_AuthContext,AuthContext'
84 |
85 | - name: Authenticate
86 | id: Authenticate
87 | if: steps.DetermineDeploymentEnvironments.outputs.UnknownEnvironment == 1
88 | run: |
89 | $envName = '${{ steps.envName.outputs.envName }}'
90 | $secretName = ''
91 | $secrets = '${{ steps.ReadSecrets.outputs.Secrets }}' | ConvertFrom-Json
92 | $authContext = $null
93 | "$($envName)-AuthContext", "$($envName)_AuthContext", "AuthContext" | ForEach-Object {
94 | if (!($authContext)) {
95 | if ($secrets."$_") {
96 | Write-Host "Using $_ secret as AuthContext"
97 | $authContext = $secrets."$_"
98 | $secretName = $_
99 | }
100 | }
101 | }
102 | if ($authContext) {
103 | Write-Host "AuthContext provided in secret $secretName!"
104 | Add-Content -Encoding UTF8 -path $ENV:GITHUB_STEP_SUMMARY -value "AuthContext was provided in a secret called $secretName. Using this information for authentication."
105 | }
106 | else {
107 | Write-Host "No AuthContext provided for $envName, initiating Device Code flow"
108 | $ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1"
109 | $webClient = New-Object System.Net.WebClient
110 | $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v7.0/AL-Go-Helper.ps1', $ALGoHelperPath)
111 | . $ALGoHelperPath
112 | DownloadAndImportBcContainerHelper
113 | $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0))
114 | Add-Content -Encoding UTF8 -path $ENV:GITHUB_STEP_SUMMARY -value "AL-Go needs access to the Business Central Environment $('${{ steps.envName.outputs.envName }}'.Split(' ')[0]) and could not locate a secret called ${{ steps.envName.outputs.envName }}_AuthContext`n`n$($authContext.message)"
115 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "deviceCode=$($authContext.deviceCode)"
116 | }
117 |
118 | Deploy:
119 | needs: [ Initialization ]
120 | if: needs.Initialization.outputs.environmentCount > 0
121 | strategy: ${{ fromJson(needs.Initialization.outputs.environmentsMatrixJson) }}
122 | runs-on: ${{ fromJson(matrix.os) }}
123 | name: Deploy to ${{ matrix.environment }}
124 | defaults:
125 | run:
126 | shell: ${{ matrix.shell }}
127 | environment:
128 | name: ${{ matrix.environment }}
129 | url: ${{ steps.Deploy.outputs.environmentUrl }}
130 | env:
131 | deviceCode: ${{ needs.Initialization.outputs.deviceCode }}
132 | steps:
133 | - name: Checkout
134 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
135 |
136 | - name: EnvName
137 | id: envName
138 | run: |
139 | $errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0
140 | $envName = '${{ matrix.environment }}'.split(' ')[0]
141 | Add-Content -Encoding UTF8 -Path $env:GITHUB_OUTPUT -Value "envName=$envName"
142 |
143 | - name: Read settings
144 | uses: microsoft/AL-Go-Actions/ReadSettings@v7.0
145 | with:
146 | shell: ${{ matrix.shell }}
147 | get: type,powerPlatformSolutionFolder
148 |
149 | - name: Read secrets
150 | id: ReadSecrets
151 | uses: microsoft/AL-Go-Actions/ReadSecrets@v7.0
152 | with:
153 | shell: ${{ matrix.shell }}
154 | gitHubSecrets: ${{ toJson(secrets) }}
155 | getSecrets: '${{ steps.envName.outputs.envName }}-AuthContext,${{ steps.envName.outputs.envName }}_AuthContext,AuthContext'
156 |
157 | - name: Get Artifacts for deployment
158 | uses: microsoft/AL-Go-Actions/GetArtifactsForDeployment@v7.0
159 | with:
160 | shell: ${{ matrix.shell }}
161 | artifactsVersion: ${{ github.event.inputs.appVersion }}
162 | artifactsFolder: '.artifacts'
163 |
164 | - name: Deploy to Business Central
165 | id: Deploy
166 | uses: microsoft/AL-Go-Actions/Deploy@v7.0
167 | env:
168 | Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}'
169 | with:
170 | shell: ${{ matrix.shell }}
171 | environmentName: ${{ matrix.environment }}
172 | artifactsFolder: '.artifacts'
173 | type: 'Publish'
174 | deploymentEnvironmentsJson: ${{ needs.Initialization.outputs.deploymentEnvironmentsJson }}
175 | artifactsVersion: ${{ github.event.inputs.appVersion }}
176 |
177 | - name: Deploy to Power Platform
178 | if: env.type == 'PTE' && env.powerPlatformSolutionFolder != ''
179 | uses: microsoft/AL-Go-Actions/DeployPowerPlatform@v7.0
180 | env:
181 | Secrets: '${{ steps.ReadSecrets.outputs.Secrets }}'
182 | with:
183 | shell: ${{ matrix.shell }}
184 | environmentName: ${{ matrix.environment }}
185 | artifactsFolder: '.artifacts'
186 | deploymentEnvironmentsJson: ${{ needs.Initialization.outputs.deploymentEnvironmentsJson }}
187 |
188 | PostProcess:
189 | needs: [ Initialization, Deploy ]
190 | if: always()
191 | runs-on: [ windows-latest ]
192 | steps:
193 | - name: Checkout
194 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
195 |
196 | - name: Finalize the workflow
197 | id: PostProcess
198 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v7.0
199 | env:
200 | GITHUB_TOKEN: ${{ github.token }}
201 | with:
202 | shell: powershell
203 | telemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
204 | currentJobContext: ${{ toJson(job) }}
205 |
--------------------------------------------------------------------------------
/Dependency-Graph/src/Base/WSAndMDInfoImpANJ.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Codeunit "WSAndMDInfoImp_ANJ" (ID 80812) implements Interface FillingProcessingTables_ANJ.
3 | ///
4 | namespace ANJ.Tools.Graph;
5 | codeunit 80812 WSAndMDInfoImp_ANJ implements IFillingProcessingTables_ANJ
6 | {
7 | Access = Internal;
8 | Permissions = tabledata Extensions_ANJ = RD, tabledata Relations_ANJ = RD;
9 |
10 | ///
11 | /// GetExtensions.
12 | ///
13 | /// Return value of type Text.
14 | procedure GetExtensions(): Text
15 | var
16 | AccessToken: Text;
17 | begin
18 | AccessToken := GetAccessToken();
19 | if AccessToken = '' then
20 | Error(UnableToCommunicateWSErr);
21 |
22 | exit(DoGetExtensions(AccessToken));
23 | end;
24 |
25 | ///
26 | /// GetAccessToken.
27 | ///
28 | /// Return value of type Text.
29 | local procedure GetAccessToken(): Text
30 | var
31 | AuxHttpClient: HttpClient;
32 | RequestHttpContent: HttpContent;
33 | Headers: HttpHeaders;
34 | ResponseHttpResponseMessage: HttpResponseMessage;
35 | ResponseText: Text;
36 | begin
37 | RequestHttpContent.WriteFrom(GetRequestAccessTokenContent());
38 | RequestHttpContent.GetHeaders(Headers);
39 | Headers.Remove(ContentTypeTok);
40 | Headers.Add(ContentTypeTok, UrlencodedTok);
41 |
42 | if not AuxHttpClient.Post(GetAccessTokenAPIUrl(), RequestHttpContent, ResponseHttpResponseMessage) then
43 | Error(UnableToCommunicateWSErr);
44 |
45 | GetResponseMessageText(ResponseHttpResponseMessage, ResponseText);
46 | exit(JSONMethods.GetJsonValue(AccessTokenTok, ResponseText));
47 | end;
48 |
49 | ///
50 | /// GetRequestAccessTokenContent.
51 | ///
52 | /// Return value of type Text.
53 | local procedure GetRequestAccessTokenContent(): Text
54 | var
55 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
56 | ContentTextBuilder: TextBuilder;
57 | begin
58 | DependencyGraphSetup.SetLoadFields(ClientID, Secret);
59 | DependencyGraphSetup.GetInstance();
60 |
61 | ContentTextBuilder.AppendLine(GrantTypeTok);
62 | ContentTextBuilder.AppendLine(StrSubstNo(ClientIdTok, DependencyGraphSetup.ClientID));
63 | ContentTextBuilder.AppendLine(StrSubstNo(ClientSecretTok, DependencyGraphSetup.Secret));
64 | ContentTextBuilder.AppendLine(ScopeTok);
65 | exit(ContentTextBuilder.ToText());
66 | end;
67 |
68 | ///
69 | /// GetAccessTokenAPIUrl.
70 | ///
71 | /// Return value of type Text.
72 | local procedure GetAccessTokenAPIUrl(): Text
73 | begin
74 | exit(StrSubstNo(AccessTokenUrlTok, GetTenantId()));
75 | end;
76 |
77 | ///
78 | /// GetTenantId.
79 | ///
80 | /// Return value of type Text.
81 | local procedure GetTenantId(): Text
82 | begin
83 | exit(AzureADTenant.GetAadTenantId());
84 | end;
85 |
86 | ///
87 | /// DoGetExtensions.
88 | ///
89 | /// Text.
90 | /// Return value of type Text.
91 | local procedure DoGetExtensions(AccessToken: Text): Text
92 | var
93 | AuxHttpClient: HttpClient;
94 | ResponseHttpResponseMessage: HttpResponseMessage;
95 | ResponseText: Text;
96 | begin
97 | AuxHttpClient.DefaultRequestHeaders().Add(AuthorizationTok, StrSubstNo(AuthorizationValueTok, AccessToken));
98 |
99 | if not AuxHttpClient.Get(GetExtensionsAPIUrl(), ResponseHttpResponseMessage) then
100 | Error(UnableToCommunicateWSErr);
101 |
102 | GetResponseMessageText(ResponseHttpResponseMessage, ResponseText);
103 | exit(ResponseText);
104 | end;
105 |
106 | ///
107 | /// GetExtensionsAPIUrl.
108 | ///
109 | /// Return value of type Text.
110 | local procedure GetExtensionsAPIUrl(): Text
111 | var
112 | DependencyGraphSetup: Record DependencyGraphSetup_ANJ;
113 | ExtensionsUrl: Text;
114 | begin
115 | DependencyGraphSetup.SetLoadFields(IncludeMicrosoftApps);
116 | DependencyGraphSetup.GetInstance();
117 |
118 | ExtensionsUrl := StrSubstNo(ExtensionsUrlTok, GetEnvironmentName(), GetCompanyId());
119 | if not DependencyGraphSetup.IncludeMicrosoftApps then
120 | ExtensionsUrl += FilterMSAppsTok + '''Microsoft''';
121 | // Hardcode of value 'Microsoft' because the single-quote escaping gives error when generating translations with the third-party extension.
122 |
123 | exit(ExtensionsUrl);
124 | end;
125 |
126 | ///
127 | /// GetEnvironmentName.
128 | ///
129 | /// Return value of type Text.
130 | local procedure GetEnvironmentName(): Text
131 | var
132 | EnvironmentInformation: Codeunit System.Environment."Environment Information";
133 | begin
134 | exit(EnvironmentInformation.GetEnvironmentName());
135 | end;
136 |
137 | ///
138 | /// GetCompanyId.
139 | ///
140 | /// Return value of type Boolean.
141 | local procedure GetCompanyId(): Text
142 | var
143 | Company: Record System.Environment.Company;
144 | begin
145 | Company.SetLoadFields(Id);
146 | if not Company.Get(CompanyName()) then
147 | exit;
148 |
149 | exit(Format(Company.Id, 0, 4).ToLower());
150 | end;
151 |
152 | ///
153 | /// GetResponseMessageText.
154 | ///
155 | /// VAR HttpResponseMessage.
156 | /// VAR Text.
157 | local procedure GetResponseMessageText(var ResponseHttpResponseMessage: HttpResponseMessage; var ResponseText: Text)
158 | begin
159 | ResponseHttpResponseMessage.Content().ReadAs(ResponseText);
160 | if not ResponseHttpResponseMessage.IsSuccessStatusCode() then
161 | Error(WSStatusCodeErr, ResponseHttpResponseMessage.HttpStatusCode(), ResponseText);
162 | end;
163 |
164 | ///
165 | /// GetRelations.
166 | ///
167 | /// Return value of type Text.
168 | procedure GetRelations() JsonText: Text
169 | var
170 | Extensions: Record Extensions_ANJ;
171 | RelationsArry: JsonArray;
172 | begin
173 | Extensions.SetLoadFields(AppID);
174 | Extensions.SetRange(ShowInGraph, true);
175 | if Extensions.IsEmpty() then
176 | exit;
177 |
178 | if Extensions.FindSet(false) then
179 | repeat
180 | CheckDependencies(Extensions.AppID, RelationsArry);
181 | until Extensions.Next() = 0;
182 | RelationsArry.WriteTo(JsonText);
183 | end;
184 |
185 | ///
186 | /// CheckDependencies.
187 | ///
188 | /// Guid.
189 | /// VAR JsonArray.
190 | local procedure CheckDependencies(AppId: Guid; var RelationsArry: JsonArray)
191 | var
192 | DestinationAppID: Guid;
193 | ModuleDependencyInfoList: List of [ModuleDependencyInfo];
194 | SingleModuleDependencyInfo: ModuleDependencyInfo;
195 | AuxModuleInfo: ModuleInfo;
196 | begin
197 | if not NavApp.GetModuleInfo(AppId, AuxModuleInfo) then
198 | exit;
199 |
200 | ModuleDependencyInfoList := AuxModuleInfo.Dependencies;
201 |
202 | foreach SingleModuleDependencyInfo in ModuleDependencyInfoList do begin
203 | DestinationAppID := SingleModuleDependencyInfo.Id;
204 | if CheckDestinationAppIDShowInGraph(DestinationAppID) then
205 | AddNewRelationToJsonArry(RelationsArry, AppId, DestinationAppID);
206 | end;
207 | end;
208 |
209 | ///
210 | /// AddNewRelationToJsonArry.
211 | ///
212 | /// VAR JsonArray.
213 | /// Guid.
214 | /// Guid.
215 | local procedure AddNewRelationToJsonArry(var RelationsArry: JsonArray; SourceAppID: Guid; DestinationAppID: Guid)
216 | var
217 | RelationJsonObject: JsonObject;
218 | begin
219 | RelationJsonObject.Add(SourceAppIDTok, SourceAppID);
220 | RelationJsonObject.Add(DestinationAppIDTok, DestinationAppID);
221 |
222 | RelationsArry.Add(RelationJsonObject);
223 | end;
224 |
225 | ///
226 | /// CheckDestinationAppIDShowInGraph.
227 | ///
228 | /// Guid.
229 | /// Return value of type Boolean.
230 | local procedure CheckDestinationAppIDShowInGraph(AppID: Guid): Boolean
231 | var
232 | Extensions: Record Extensions_ANJ;
233 | begin
234 | Extensions.SetRange(AppID, AppID);
235 | Extensions.SetRange(ShowInGraph, true);
236 | exit(not Extensions.IsEmpty());
237 | end;
238 |
239 | var
240 | JSONMethods: Codeunit JSONMethods_ANJ;
241 | AzureADTenant: Codeunit System.Azure.Identity."Azure AD Tenant";
242 | AccessTokenTok: Label 'access_token', Locked = true;
243 | AccessTokenUrlTok: Label 'https://login.microsoftonline.com/%1/oauth2/v2.0/token', Locked = true;
244 | AuthorizationTok: Label 'Authorization', Locked = true;
245 | AuthorizationValueTok: Label 'Bearer %1', Locked = true;
246 | ClientIdTok: Label '&client_id=%1', Locked = true;
247 | ClientSecretTok: Label '&client_secret=%1', Locked = true;
248 | ContentTypeTok: Label 'Content-Type', Locked = true;
249 | DestinationAppIDTok: Label 'DestinationAppID', Locked = true;
250 | ExtensionsUrlTok: Label 'https://api.businesscentral.dynamics.com/v2.0/%1/api/microsoft/automation/v2.0/companies(%2)/extensions', Locked = true;
251 | FilterMSAppsTok: Label '?$filter=publisher ne ', Locked = true;
252 | GrantTypeTok: Label 'grant_type=client_credentials', Locked = true;
253 | ScopeTok: Label '&scope=https://api.businesscentral.dynamics.com/.default', Locked = true;
254 | SourceAppIDTok: Label 'SourceAppID', Locked = true;
255 | UnableToCommunicateWSErr: Label 'Unable to communicate with the web service.';
256 | UrlencodedTok: Label 'application/x-www-form-urlencoded', Locked = true;
257 | WSStatusCodeErr: Label 'Error - Status code: %1 Description: %2.', Comment = 'Placeholder %1: Status code, Placeholder %2: Description.';
258 | }
--------------------------------------------------------------------------------