diff --git a/404.html b/404.html index 6874477..93fc391 100644 --- a/404.html +++ b/404.html @@ -916,6 +916,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -968,11 +989,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1306,6 +1327,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1327,6 +1369,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/CLI/index.html b/CLI/index.html index 908d474..9985109 100644 --- a/CLI/index.html +++ b/CLI/index.html @@ -937,6 +937,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -989,11 +1010,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1327,6 +1348,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1348,6 +1390,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/CLI/microflow-transformation/index.html b/CLI/microflow-transformation/index.html index e1f84ad..4e3198f 100644 --- a/CLI/microflow-transformation/index.html +++ b/CLI/microflow-transformation/index.html @@ -996,6 +996,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -1048,11 +1069,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1386,6 +1407,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1407,6 +1449,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/about/index.html b/about/index.html index 3ac2e06..ef50c2c 100644 --- a/about/index.html +++ b/about/index.html @@ -923,6 +923,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -975,11 +996,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1313,6 +1334,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1334,6 +1376,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/assets/videos/2024-10-24-mx-meetup-part1.mp4 b/assets/videos/2024-10-24-mx-meetup-part1.mp4 new file mode 100644 index 0000000..1b01da7 Binary files /dev/null and b/assets/videos/2024-10-24-mx-meetup-part1.mp4 differ diff --git a/assets/videos/2024-10-24-mx-meetup-part2.mp4 b/assets/videos/2024-10-24-mx-meetup-part2.mp4 new file mode 100644 index 0000000..72e5e01 Binary files /dev/null and b/assets/videos/2024-10-24-mx-meetup-part2.mp4 differ diff --git a/assets/videos/2024-10-24-mx-meetup-part3.mp4 b/assets/videos/2024-10-24-mx-meetup-part3.mp4 new file mode 100644 index 0000000..c3da68a Binary files /dev/null and b/assets/videos/2024-10-24-mx-meetup-part3.mp4 differ diff --git a/code-of-conduct/index.html b/code-of-conduct/index.html index 0f74d11..f226213 100644 --- a/code-of-conduct/index.html +++ b/code-of-conduct/index.html @@ -925,6 +925,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -977,11 +998,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1315,6 +1336,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1336,6 +1378,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/events/2024-10-24-mendix-meetup/index.html b/events/2024-10-24-mendix-meetup/index.html new file mode 100644 index 0000000..631c188 --- /dev/null +++ b/events/2024-10-24-mendix-meetup/index.html @@ -0,0 +1,1856 @@ + + + + + + + + + + + + + + + + + + + + + + + Mendix Meetup 2024-10-24 - MxLint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + Skip to content + + +
    +
    + +
    + + + + + + +
    + + +
    + +
    + + + + + + +
    +
    + + + +
    +
    +
    + + + + + +
    +
    +
    + + + + + + + +
    +
    + + + + + + + +

    Mendix Meetup 2024-10-24

    +

    Agenda

    +
      +
    • Quality Driven Development Initiative – Xiwen Cheng (10min)
    • +
    • ABN AMRO shares a story about balancing low-code, low-ops & low risk and why they support the MxLint initiative - Bart Zantingh (20min)
    • +
    • MxLint – Xiwen Cheng (25min)
    • +
    +

    Speakers

    +
      +
    • Bart Zantingh, Lead Mendix Developer, ABN AMRO
    • +
    • Xiwen (Steven) Cheng, Cloud Architect, CINAQ
    • +
    +

    Video recordings

    +

    Quality Driven Development Initiative

    + + +

    ABN AMRO shares a story about balancing low-code, low-ops & low risk and why they support the MxLint initiative

    + + +

    MxLint

    + + + + + + + + + + + + + + +
    +
    + + + +
    + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/events/index.html b/events/index.html new file mode 100644 index 0000000..8588529 --- /dev/null +++ b/events/index.html @@ -0,0 +1,1670 @@ + + + + + + + + + + + + + + + + + + + + + + + Introduction - MxLint + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + Skip to content + + +
    +
    + +
    + + + + + + +
    + + +
    + +
    + + + + + + +
    +
    + + + +
    +
    +
    + + + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    +
    + + + +
    +
    + + + + + + + +

    Introduction

    +

    This section gives an overview of all social events held in the past.

    + + + + + + + + + + + + + +
    +
    + + + +
    + +
    + + + +
    +
    +
    +
    + +
    + + + + + + + + + + \ No newline at end of file diff --git a/features/index.html b/features/index.html index ac73a21..47a08a3 100644 --- a/features/index.html +++ b/features/index.html @@ -935,6 +935,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -987,11 +1008,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1325,6 +1346,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1346,6 +1388,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/index.html b/index.html index abc7a41..07c6968 100644 --- a/index.html +++ b/index.html @@ -999,6 +999,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -1051,11 +1072,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1389,6 +1410,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1410,6 +1452,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/mendix-documents/domain-model/index.html b/mendix-documents/domain-model/index.html index 187ebe5..e98e5d5 100644 --- a/mendix-documents/domain-model/index.html +++ b/mendix-documents/domain-model/index.html @@ -976,6 +976,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -1028,11 +1049,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1366,6 +1387,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1387,6 +1429,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/mendix-documents/index.html b/mendix-documents/index.html index 52d1940..402a657 100644 --- a/mendix-documents/index.html +++ b/mendix-documents/index.html @@ -937,6 +937,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -989,11 +1010,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1327,6 +1348,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1348,6 +1390,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/mendix-studio-pro-extension/configuration/index.html b/mendix-studio-pro-extension/configuration/index.html index faa3a75..f049d90 100644 --- a/mendix-studio-pro-extension/configuration/index.html +++ b/mendix-studio-pro-extension/configuration/index.html @@ -976,6 +976,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -1028,11 +1049,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1366,6 +1387,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1387,6 +1429,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/mendix-studio-pro-extension/development/index.html b/mendix-studio-pro-extension/development/index.html index 5370318..0f94fc0 100644 --- a/mendix-studio-pro-extension/development/index.html +++ b/mendix-studio-pro-extension/development/index.html @@ -1042,6 +1042,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -1094,11 +1115,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1432,6 +1453,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1453,6 +1495,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/mendix-studio-pro-extension/index.html b/mendix-studio-pro-extension/index.html index 816963f..8980016 100644 --- a/mendix-studio-pro-extension/index.html +++ b/mendix-studio-pro-extension/index.html @@ -976,6 +976,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -1028,11 +1049,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1366,6 +1387,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1387,6 +1429,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/mendix-studio-pro-extension/installation/index.html b/mendix-studio-pro-extension/installation/index.html index 662f306..179205d 100644 --- a/mendix-studio-pro-extension/installation/index.html +++ b/mendix-studio-pro-extension/installation/index.html @@ -1009,6 +1009,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -1061,11 +1082,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1399,6 +1420,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1420,6 +1462,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/pipeline-integration/index.html b/pipeline-integration/index.html index c5ff676..9ffd557 100644 --- a/pipeline-integration/index.html +++ b/pipeline-integration/index.html @@ -937,6 +937,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -989,11 +1010,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1327,6 +1348,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1348,6 +1390,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/pipeline-integration/setup/index.html b/pipeline-integration/setup/index.html index 42268a0..8881b0b 100644 --- a/pipeline-integration/setup/index.html +++ b/pipeline-integration/setup/index.html @@ -985,6 +985,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -1037,11 +1058,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1375,6 +1396,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1396,6 +1438,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/quickstart/index.html b/quickstart/index.html index ce1c127..c6d1cfb 100644 --- a/quickstart/index.html +++ b/quickstart/index.html @@ -935,6 +935,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -987,11 +1008,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1325,6 +1346,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1346,6 +1388,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/rules/001_project_settings/001_0001_anonymous_disabled/index.html b/rules/001_project_settings/001_0001_anonymous_disabled/index.html index 725dd45..4612f0a 100644 --- a/rules/001_project_settings/001_0001_anonymous_disabled/index.html +++ b/rules/001_project_settings/001_0001_anonymous_disabled/index.html @@ -1014,6 +1014,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -1066,11 +1087,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,6 +1425,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/rules/001_project_settings/001_0002_demo_users_disabled/index.html b/rules/001_project_settings/001_0002_demo_users_disabled/index.html index 64a5f04..d5c8dbf 100644 --- a/rules/001_project_settings/001_0002_demo_users_disabled/index.html +++ b/rules/001_project_settings/001_0002_demo_users_disabled/index.html @@ -1014,6 +1014,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -1066,11 +1087,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,6 +1425,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/rules/001_project_settings/001_0003_security_checks/index.html b/rules/001_project_settings/001_0003_security_checks/index.html index 3d258d2..b66af7d 100644 --- a/rules/001_project_settings/001_0003_security_checks/index.html +++ b/rules/001_project_settings/001_0003_security_checks/index.html @@ -1014,6 +1014,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -1066,11 +1087,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,6 +1425,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/rules/001_project_settings/001_0004_strong_password/index.html b/rules/001_project_settings/001_0004_strong_password/index.html index 1af952d..f1451e6 100644 --- a/rules/001_project_settings/001_0004_strong_password/index.html +++ b/rules/001_project_settings/001_0004_strong_password/index.html @@ -12,7 +12,7 @@ - + @@ -1014,6 +1014,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -1066,11 +1087,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,6 +1425,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • @@ -1670,13 +1798,13 @@

    Test cases

    -
    +
  • +
  • - + -
  • - - - - - - - - - - - - - - - - - -
  • - - - - - - - - - - + +
  • + + + + + + + + + + + + + + + +
  • + + + + + + + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • @@ -1506,9 +1634,9 @@ @@ -981,11 +1002,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,6 +1425,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • @@ -1672,7 +1800,7 @@

    Test cases

    @@ -981,11 +1002,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,6 +1425,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/rules/002_domain_model/002_0004_inherit_from_non_system/index.html b/rules/002_domain_model/002_0004_inherit_from_non_system/index.html index 53c11a9..c743562 100644 --- a/rules/002_domain_model/002_0004_inherit_from_non_system/index.html +++ b/rules/002_domain_model/002_0004_inherit_from_non_system/index.html @@ -927,6 +927,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -981,11 +1002,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,6 +1425,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/rules/002_domain_model/002_0005_avoid_system_entity_association/index.html b/rules/002_domain_model/002_0005_avoid_system_entity_association/index.html index c5624ff..5e7fd17 100644 --- a/rules/002_domain_model/002_0005_avoid_system_entity_association/index.html +++ b/rules/002_domain_model/002_0005_avoid_system_entity_association/index.html @@ -927,6 +927,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -981,11 +1002,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,6 +1425,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/index.html b/rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/index.html index 5c11bfb..d31c9f7 100644 --- a/rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/index.html +++ b/rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/index.html @@ -927,6 +927,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -981,11 +1002,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,6 +1425,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/rules/002_domain_model/002_0007_avoid_using_validation_rules/index.html b/rules/002_domain_model/002_0007_avoid_using_validation_rules/index.html index 209f105..c677c55 100644 --- a/rules/002_domain_model/002_0007_avoid_using_validation_rules/index.html +++ b/rules/002_domain_model/002_0007_avoid_using_validation_rules/index.html @@ -927,6 +927,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -981,11 +1002,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,6 +1425,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/rules/003_modules/003_0001_number_of_modules/index.html b/rules/003_modules/003_0001_number_of_modules/index.html index b35d165..ea8954b 100644 --- a/rules/003_modules/003_0001_number_of_modules/index.html +++ b/rules/003_modules/003_0001_number_of_modules/index.html @@ -927,6 +927,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -979,11 +1000,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,6 +1425,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/rules/004_pages/004_0001_inline_style_property_used/index.html b/rules/004_pages/004_0001_inline_style_property_used/index.html index 7fa250c..5163d6b 100644 --- a/rules/004_pages/004_0001_inline_style_property_used/index.html +++ b/rules/004_pages/004_0001_inline_style_property_used/index.html @@ -927,6 +927,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -979,11 +1000,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,6 +1425,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1425,6 +1467,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • @@ -1575,7 +1703,7 @@

    Metadata

    authors:
     - Xiwen Cheng <x@cinaq.com>
     category: Maintainability
    -input: '*/**/*$Page.yaml'
    +input: '**/*$Page.yaml'
     rulename: InlineStylePropertyUsed
     rulenumber: '004_0001'
     scope: package
    diff --git a/rules/005_microflows/005_0001_empty_string_check_not_complete/index.html b/rules/005_microflows/005_0001_empty_string_check_not_complete/index.html
    index 9efc631..07c21d9 100644
    --- a/rules/005_microflows/005_0001_empty_string_check_not_complete/index.html
    +++ b/rules/005_microflows/005_0001_empty_string_check_not_complete/index.html
    @@ -12,7 +12,7 @@
             
           
           
    -        
    +        
           
           
           
    @@ -927,6 +927,27 @@
     
                   
                 
    +              
    +                
    +  
    +  
    +  
    +  
    +    
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -979,11 +1000,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1404,12 +1425,119 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + + + + +
  • + + + + + + + + + + + + + + + + +
  • + + + + + + + + + + @@ -1070,11 +1091,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1408,6 +1429,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1429,6 +1471,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/rules/index.html b/rules/index.html index fe688da..e592ae4 100644 --- a/rules/index.html +++ b/rules/index.html @@ -937,6 +937,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -989,11 +1010,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1327,6 +1348,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1348,6 +1390,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +
  • diff --git a/search/search_index.json b/search/search_index.json index 66b2585..c6aaf7f 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":"

    MxLint (Formerly Mendix-CLI) is a set of tools to help you build high quality Mendix apps. This is inspired by the success of projects like PyLint and other linting systems which provide a way to enforce code standards, best practices and guidelines in your code.

    Your browser does not support the video tag."},{"location":"#mission","title":"Mission","text":"
    • Become the standard for linting Mendix apps.
    • Provide a way to enforce best practices and guidelines in your code.
    • Provide a way to share knowledge and experience between developers.
    "},{"location":"#ecosystem","title":"Ecosystem","text":"

    Mxlint is a collection of tools and projects to help you build high quality Mendix apps.

    • MxLint CLI: The main tool of the MxLint ecosystem. It is used to lint your Mendix project. Can be used directly as CLI tool or as part of a CI/CD pipeline.
    • MxLint Extension: An extension for Mendix Studio Pro to help you lint your Mendix project during development.
    • MxLint Rules: A place to find and share your rules with the community.
    • MxLint Docs: This documentation you are reading right now. It also extracts documentation from the rules to provide you with a better experience in a single place.
    "},{"location":"#bugs-and-contribution","title":"Bugs and Contribution","text":"

    We welcome all to report bugs or even better contribute to MxLint. This can be done creating issues or Pull request towards the repositories listed above.

    "},{"location":"#philosophy","title":"Philosophy","text":"

    MxLint is built on the philosophy that the best way to build Mendix apps is to follow the best practices and guidelines that are outlined in the Mendix Best practices documentation and most importantly based on the community experience. The collective experience of the developers in the Mendix community is distilled into rules that are then enforced with formal rules.

    We also believe knowledge should be shared and that by sharing our experiences we can all learn from each other. Therefore we are aiming for this project to be a community driven project where we can all learn from each other. A project owned by the community and for the community. To live up to this goal we decided to make MxLint an open source project and we welcome contributions from the community.

    "},{"location":"about/","title":"About","text":"

    MxLint is an initiative of CINAQ. We believe the Mendix community can benefit from a set of tools to help them build high quality Mendix apps. Where knowledge is shared with everyone.

    "},{"location":"about/#licensing","title":"Licensing","text":"

    All MxLint related repositories are licensed under the AGPL-3.0 license. This means you have the right to use, modify, and distribute the software, as long as you comply with the terms of the license.

    "},{"location":"about/#contributing","title":"Contributing","text":"

    We welcome contributions to MxLint from the community. If you have an idea for a rule or a feature, please open an issue or submit a pull request. We are also happy to help you with your contribution.

    "},{"location":"code-of-conduct/","title":"Code of Conduct","text":""},{"location":"code-of-conduct/#our-pledge","title":"Our Pledge","text":"

    We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.

    We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.

    "},{"location":"code-of-conduct/#our-standards","title":"Our Standards","text":"

    Examples of behavior that contributes to a positive environment for our community include:

    • Demonstrating empathy and kindness toward other people
    • Being respectful of differing opinions, viewpoints, and experiences
    • Giving and gracefully accepting constructive feedback
    • Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
    • Focusing on what is best not just for us as individuals, but for the overall community

    Examples of unacceptable behavior include:

    • The use of sexualized language or imagery, and sexual attention or advances of any kind
    • Trolling, insulting or derogatory comments, and personal or political attacks
    • Public or private harassment
    • Publishing others' private information, such as a physical or email address, without their explicit permission
    • Other conduct which could reasonably be considered inappropriate in a professional setting
    "},{"location":"code-of-conduct/#enforcement-responsibilities","title":"Enforcement Responsibilities","text":"

    Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.

    "},{"location":"code-of-conduct/#scope","title":"Scope","text":"

    This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online

    "},{"location":"features/","title":"Features","text":"
    • Convert mendix MPR format to Yaml files
    • Check Mendix model against predefined rule-set
    "},{"location":"quickstart/","title":"Quickstart","text":"

    MxLint is very versatile. Try out with one of the following guides:

    • Mendix Studio Pro Extension
    • Pipeline Integration

    The CLI can be used or integrated in many different ways also. Feel free to share with us if your use case is not yet documented.

    "},{"location":"terminologies/","title":"Terminologies","text":""},{"location":"terminologies/#rule","title":"Rule","text":"

    A rule is a single unit of linting that checks for a specific issue in your Mendix project.

    "},{"location":"terminologies/#ruleset","title":"Ruleset","text":"

    A ruleset is a collection of rules that are used to lint a specific part of your Mendix project.

    "},{"location":"terminologies/#linting","title":"Linting","text":"

    Linting is the process of running a ruleset against a Mendix project to check for issues.

    "},{"location":"terminologies/#policy","title":"Policy","text":"

    In the context MxLint, policy is the same as a rule. You will see the terms policy and rule used interchangeably.

    "},{"location":"use-cases/","title":"Use cases","text":"

    Here we attempt to show you what you can accomplish with MxLint. It has many purposes thanks to the fact is works lightning fast and versatile.

    "},{"location":"use-cases/#textual-revision-diff-ing","title":"Textual revision diff-ing","text":"

    To compare model changes normally you need Mendix Studio Pro. With MxLint you can use any existing Merge request workflow to review model changes. Albeit in a textual instead of visual way:

    Above use case is best when using the extension with your project in Mendix Studio Pro

    "},{"location":"use-cases/#get-feedback-on-best-practices-in-studio-pro","title":"Get feedback on best practices in Studio Pro","text":"

    With the new recent extensibility support in Mendix Studio pro, MxLint is closer than ever to you. With the extension, you get almost real-time feedback on your project quality. It does not use AI or LLM but old-school, objective and deterministic rules.

    Start here with the extension

    "},{"location":"use-cases/#enforce-best-practices-in-pipelines","title":"Enforce best practices in pipelines","text":"

    In order to guard quality and maintainability of Mendix apps as a platform owner, you can build quality gates into your pipelines. This way you ensure minimum quality criterias are met as part of you CI/CD workflows.

    Learn how this works in pipelines

    "},{"location":"use-cases/#organizational-custom-best-practices","title":"Organizational custom best practices","text":"

    As MxLint is Open source, the possibilities are endless. This means you are free and able to write your own custom rules.

    "},{"location":"use-cases/#others","title":"Others","text":"

    Do you use MxLint in other ways? Let us know.

    "},{"location":"CLI/","title":"Introduction","text":""},{"location":"CLI/microflow-transformation/","title":"Microflow transformation","text":""},{"location":"CLI/microflow-transformation/#microflow-transformation","title":"Microflow Transformation","text":"

    This document outlines the thought process and approach on how to transform a program (or function if you want) from a graph like format to as-linear-as-possible textual representation.

    "},{"location":"CLI/microflow-transformation/#mendix-microflows-are-not-dags","title":"Mendix Microflows are not DAG's","text":"

    Mendix Microflows are in abstract form a graph structure. For those warry of graph-theory, at first sight most graphs are Directed Acyclic Graph (DAG). Because there is a starting point and multiple end states. There is a minor detail: it's possible to create loops using Exclusive Split and Exclusive Merge actions. These are comparable to defining labels and goto in more classical programming languages.

    "},{"location":"CLI/microflow-transformation/#goto","title":"Goto","text":""},{"location":"mendix-documents/","title":"Introduction","text":"

    This section gives an overview of different Mendix documents that are relevant to MxLint.

    You can use these documents to understand the structure of different types of Mendix documents. This will help you write rules for MxLint.

    "},{"location":"mendix-documents/domain-model/","title":"Domain Model","text":""},{"location":"mendix-documents/domain-model/#example","title":"Example","text":"
    $Type: DomainModels$DomainModel\nAnnotations:\n- $Type: DomainModels$Annotation\n  Caption: \"This Domain model defines the data structure of this module. \\r\\n\\r\\nAdd\n    new entities and associations to define your database tables and their relations.\n    \\r\\nWe automatically provision the underlying database for you.\\r\\n\\r\\nTo define\n    in-memory data, you can turn off the 'persistable' property on the entity.\\r\\n\\r\\nMore\n    info: https://docs.mendix.com/refguide/domain-model\"\n  ExportLevel: Hidden\n  Width: 440\nAssociations: null\nCrossAssociations: null\nDocumentation: \"\"\nEntities:\n- $Type: DomainModels$EntityImpl\n  AccessRules:\n  - $Type: DomainModels$AccessRule\n    AllowCreate: false\n    AllowDelete: false\n    AllowedModuleRoles:\n    - MyFirstModule.User\n    DefaultMemberAccessRights: ReadOnly\n    Documentation: \"\"\n    MemberAccesses:\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.Image.PublicThumbnailPath\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.Image.EnableCaching\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.FileDocument.FileID\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.FileDocument.Name\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.FileDocument.DeleteAfterDownload\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.FileDocument.Contents\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.FileDocument.HasContents\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.FileDocument.Size\n    XPathConstraint: \"\"\n    XPathConstraintCaption: \"\"\n  Attributes: null\n  Documentation: \"\"\n  Events: null\n  ExportLevel: Hidden\n  Indexes: null\n  MaybeGeneralization:\n    $Type: DomainModels$Generalization\n    Generalization: System.Image\n  Name: Photo\n  Source: null\n  ValidationRules: null\n- $Type: DomainModels$EntityImpl\n  AccessRules:\n  - $Type: DomainModels$AccessRule\n    AllowCreate: false\n    AllowDelete: false\n    AllowedModuleRoles:\n    - MyFirstModule.User\n    DefaultMemberAccessRights: ReadOnly\n    Documentation: \"\"\n    MemberAccesses:\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: MyFirstModule.Bike.Name\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: MyFirstModule.Bike.PurchaseDate\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: MyFirstModule.Bike.VA_age\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: MyFirstModule.Bike.Year\n    XPathConstraint: \"\"\n    XPathConstraintCaption: \"\"\n  Attributes:\n  - $Type: DomainModels$Attribute\n    Documentation: \"\"\n    ExportLevel: Hidden\n    Name: Name\n    NewType:\n      $Type: DomainModels$StringAttributeType\n      Length: 200\n    Value:\n      $Type: DomainModels$StoredValue\n      DefaultValue: \"\"\n  - $Type: DomainModels$Attribute\n    Documentation: \"\"\n    ExportLevel: Hidden\n    Name: PurchaseDate\n    NewType:\n      $Type: DomainModels$DateTimeAttributeType\n      LocalizeDate: true\n    Value:\n      $Type: DomainModels$StoredValue\n      DefaultValue: \"\"\n  - $Type: DomainModels$Attribute\n    Documentation: \"\"\n    ExportLevel: Hidden\n    Name: VA_age\n    NewType:\n      $Type: DomainModels$IntegerAttributeType\n    Value:\n      $Type: DomainModels$CalculatedValue\n      Microflow: MyFirstModule.VA_Age\n      PassEntity: true\n  - $Type: DomainModels$Attribute\n    Documentation: \"\"\n    ExportLevel: Hidden\n    Name: Year\n    NewType:\n      $Type: DomainModels$IntegerAttributeType\n    Value:\n      $Type: DomainModels$StoredValue\n      DefaultValue: \"2010\"\n  Documentation: \"\"\n  Events: null\n  ExportLevel: Hidden\n  Indexes: null\n  MaybeGeneralization:\n    $Type: DomainModels$NoGeneralization\n    HasChangedByAttr: false\n    HasChangedDateAttr: false\n    HasCreatedDateAttr: false\n    HasOwnerAttr: false\n    Persistable: true\n  Name: Bike\n  Source: null\n  ValidationRules: null\n
    "},{"location":"mendix-studio-pro-extension/","title":"Introduction","text":"

    MxLint can be used directly inside of the Mendix Studio Pro. This method gives Mendix Developers quick feedback on how well they are doing in terms of following the best practices.

    "},{"location":"mendix-studio-pro-extension/#known-limitations","title":"Known limitations","text":"
    • Scanning takes a few seconds to complete and it depends on the size of the Mendix project and the number of rules
    • Currently very limited configuration possible
    • Probaly many more...
    "},{"location":"mendix-studio-pro-extension/configuration/","title":"Configuration","text":"

    Currently there are no customization possibilities.

    Well... there is one...

    "},{"location":"mendix-studio-pro-extension/configuration/#custom-rules","title":"Custom rules","text":"

    You can follow the instructions to create a custom rule. Custom rules can be added into $project/.mendix-cache/rules/ directory. These will be picked up automatically by the MxLint Extension.

    "},{"location":"mendix-studio-pro-extension/development/","title":"Development","text":""},{"location":"mendix-studio-pro-extension/development/#librariesreferences","title":"Libraries/References","text":"

    We try to keep the number of dependencies to a minimum. But sometimes it is handy to re-use existing projects.

    • MxLintExtension source code
    • Pico CSS to help with styling.
    "},{"location":"mendix-studio-pro-extension/development/#osx-macos","title":"OSX (MacOS)","text":"
    • Visual Studio Code
    • Mendix Studio Pro 10.12.2
    • dotNet core SDK vscode
    • C# dev kit VSCode extension (inside of VSCode)
    "},{"location":"mendix-studio-pro-extension/development/#build","title":"Build","text":"
    make\n
    "},{"location":"mendix-studio-pro-extension/development/#test","title":"Test","text":"
    • Open Mendix Studio Pro with --enable-extension-development flag: /Applications/Studio\\ Pro\\ 10.12.2.41995-Beta.app/Contents/MacOS/studiopro --enable-extension-development
    • Open local project located in resources/App
    "},{"location":"mendix-studio-pro-extension/development/#windows","title":"Windows","text":"
    • Visual Studio 2022
    • Mendix Studio Pro 10.12.2

    Open Powershell as Administrator and run the following command:

    Set-ExecutionPolicy -ExecutionPolicy RemoteSigned\n
    "},{"location":"mendix-studio-pro-extension/development/#build_1","title":"Build","text":"
    • Run the script to build:
      .\\dev\\build-windows.ps1\n
    "},{"location":"mendix-studio-pro-extension/development/#test_1","title":"Test","text":"
    • Open studiopro.exe with --enable-extension-development flag.
    • Open local project located in resources/App
    "},{"location":"mendix-studio-pro-extension/installation/","title":"Installation","text":""},{"location":"mendix-studio-pro-extension/installation/#prerequisites","title":"Prerequisites","text":"
    • Mendix Studio Pro 10.12.x or later (Windows only)
    • an existing Mendix project
    • Download a release

    The following two steps are needed for now because MxLint Extension is still in Beta. One day you will not need to do these anymore as an end-user.

    "},{"location":"mendix-studio-pro-extension/installation/#allow-unsigned-code","title":"Allow unsigned code","text":"

    MxLint-CLI is not signed yet. The extension automatically downloads the CLI and other dependencies. To allow CLI to run you must

    • Open Powershell as Administrator and then run:
      Set-ExecutionPolicy -ExecutionPolicy RemoteSigned\n
    • After that reboot your machine

    Above should only be needed once.

    "},{"location":"mendix-studio-pro-extension/installation/#enable-developer-extensions-in-studio-pro","title":"Enable Developer extensions in Studio Pro","text":"

    Mendix Studio needs to be started with a special flag. To enable this:

    • Create a shortcut to your Mendix Studio Pro if you don't have it on your desktop already
    • Right click on the shortcut and choose Properties
    • In the Target, append the following string --enable-extension-development after the double quote. Make sure to have a space after this right most double quote.

    Now your Studio pro is ready to use Extensions.

    "},{"location":"mendix-studio-pro-extension/installation/#steps","title":"Steps","text":"

    The following must be done for each project you want to add this extension:

    • Create a directory called extensions inside of your project directory
    • Unpack the zipfile into the extensions directory. You will see a directory at $project/extensions/MxLintExtension
    • Open Mendix Studio pro with extension development flag
    • Open your app
    "},{"location":"pipeline-integration/","title":"Introduction","text":"

    While the Mendix Studio Pro extension enables you to get immediate feedback on your project quality as a developer, MxLint can also be used as governance tool.

    This can be achieved by for instance adding MxLint to your (existing) Continuous Integration pipelines. You can also choose to scan against a more extensive set of rules than locally on developer machines.

    The most important benefit of Pipeline integrations is to enable governance on a platform level. This ensures independent teams are following company standards and best practices centrally managed.

    "},{"location":"pipeline-integration/setup/","title":"Setup","text":"

    TODO

    "},{"location":"pipeline-integration/setup/#gitlab","title":"Gitlab","text":""},{"location":"pipeline-integration/setup/#azure-devops","title":"Azure DevOps","text":""},{"location":"rules/","title":"Introduction","text":"

    Linting rules are written in Rego and are used to lint Mendix projects.

    "},{"location":"rules/create/","title":"Create Rule","text":""},{"location":"rules/create/#rego-introduction","title":"Rego Introduction","text":"

    Rules are expressed with the help of the powerful OPA Rego language. Rego is a declarative language that is purpose-built for expressing policies over complex hierarchical data structures. Rego is designed to be easy to read and write, even for non-programmers. Rego is a safe language that is decidable and has a small trusted computing base. Rego is also designed to be easy to integrate with other systems.

    "},{"location":"rules/create/#policy-rule","title":"Policy rule","text":"

    To create a new policy, you need to create a new Rego file in the policies directory. The file name should be in the format XXX_YYY.rego where XXX is the policy number and YYY is the policy name. For example, 001_0001_anonymous_disabled.rego.

    The policy file should contain the following structure:

    001_0001_anonymous_disabled.rego
    # METADATA\n# scope: package\n# title: Business apps must always require login\n# description: No anonymous means every user must have valid login session or credentials\n# authors:\n# - Xiwen Cheng <x@cinaq.com>\n# custom:\n#  category: Security\n#  rulename: AnonymousDisabled\n#  severity: HIGH\n#  rulenumber: 001_0001\n#  remediation: Disable anonymous/guest access in Project Security\n#  input: Security$ProjectSecurity.yaml\npackage app.mendix.project_settings.anonymous_disabled\nimport rego.v1\nannotation := rego.metadata.chain()[1].annotations\n\ndefault allow := false\nallow if count(errors) == 0\n\nerrors contains error if {\n    input.EnableGuestAccess == true\n    error := sprintf(\"[%v, %v, %v] %v\",\n        [\n            annotation.custom.severity,\n            annotation.custom.category,\n            annotation.custom.rulenumber,\n            annotation.title,\n        ]\n    )\n}\n
    • METADATA provide information about the policy.
    • package statement is used to define the policy package.
    • allow statement is used to define the conditions under which the policy is allowed.
    • errors statement is used to define the errors that are returned if the policy is not allowed.
    • input states which files are used as input for the policy. This can be a single file or an expression like */DomainModels$DomainModel.yaml to match multiple files.
    "},{"location":"rules/create/#policy-testing","title":"Policy testing","text":"

    The best way to create a new policy is to copy an existing policy and modify it to suit your needs. There is also an accompanying test file for each policy that you can use to test your policy. The test file should be in the same directory as the policy file and should be named XXX_YYY_test.rego. For example, 001_0001_anonymous_disabled_test.rego.

    package app.mendix.project_settings.anonymous_disabled\nimport rego.v1\n\n# Test cases\ntest_allow if {\n    allow with input as {\"EnableGuestAccess\": false}\n}\ntest_no_allow if {\n    not allow with input as {\"EnableGuestAccess\": true}\n}\n

    To test your policy, run the following command:

    $ ./run-policy-tests.sh\npolicies/001_project_settings/001_0001_anonymous_disabled_test.rego:\ndata.app.mendix.project_settings.anonymous_disabled.test_allow: PASS (3.031209ms)\ndata.app.mendix.project_settings.anonymous_disabled.test_no_allow: PASS (413.375\u00b5s)\n\npolicies/001_project_settings/001_0002_demo_users_disabled_test.rego:\ndata.app.mendix.project_settings.demo_users_disabled.test_allow: PASS (105.541\u00b5s)\ndata.app.mendix.project_settings.demo_users_disabled.test_no_allow: PASS (200.5\u00b5s)\n\npolicies/001_project_settings/001_0003_security_checks_test.rego:\ndata.app.mendix.project_settings.security_checks.test_allow: PASS (111.584\u00b5s)\ndata.app.mendix.project_settings.security_checks.test_no_allow_1: PASS (842.667\u00b5s)\ndata.app.mendix.project_settings.security_checks.test_no_allow_2: PASS (206.458\u00b5s)\n\npolicies/001_project_settings/001_0004_strong_password_test.rego:\ndata.app.mendix.project_settings.strong_password.test_allow: PASS (148.792\u00b5s)\ndata.app.mendix.project_settings.strong_password.test_no_allow_password_length: PASS (538.959\u00b5s)\ndata.app.mendix.project_settings.strong_password.test_no_allow_simple: PASS (286.916\u00b5s)\n\npolicies/002_domain_model/002_0001_number_of_entities_test.rego:\ndata.app.mendix.domain_model.number_of_entities.test_no_entities: PASS (134\u00b5s)\ndata.app.mendix.domain_model.number_of_entities.test_1_entity: PASS (194.666\u00b5s)\ndata.app.mendix.domain_model.number_of_entities.test_2_entities: PASS (187.334\u00b5s)\ndata.app.mendix.domain_model.number_of_entities.test_20_entities: PASS (1.375709ms)\n\npolicies/002_domain_model/002_0002_number_of_attributes_test.rego:\ndata.app.mendix.domain_model.number_of_attributes.test_no_entities: PASS (263.5\u00b5s)\ndata.app.mendix.domain_model.number_of_attributes.test_1_entity_1_attribute: PASS (519.416\u00b5s)\ndata.app.mendix.domain_model.number_of_attributes.test_2_entities: PASS (303.458\u00b5s)\ndata.app.mendix.domain_model.number_of_attributes.test_3_entities_1_empty: PASS (342.958\u00b5s)\ndata.app.mendix.domain_model.number_of_attributes.test_1_entity_40_attributes_not_allowed: PASS (1.294166ms)\ndata.app.mendix.domain_model.number_of_attributes.test_2_entity_40_attributes_1_empty_not_allowed: PASS (2.156042ms)\n--------------------------------------------------------------------------------\nPASS: 20/20\n

    The test rego files contain examples so that it's easy to validate your policy actually works for different scenarios with purpose-crafted input data. The test script will run all the test files in the policies directory and output the results.

    Example input could be inspected in the modelsource directory. The modelsource directory contains the exported Mendix model in Yaml format. The modelsource directory is created when you run the export-model command.

    "},{"location":"rules/create/#resources","title":"Resources","text":"
    • Rego language reference
    • Rego playground
    • Rego testing
    • Rego best practices
    • Style guide
    "},{"location":"rules/create/#help","title":"Help","text":"

    We understand Rego is not the easiest language to use. However, it is the perfect match due to its expressiveness. If you need help creating a new policy, please reach out to us at support@cinaq.com.

    "},{"location":"rules/001_project_settings/001_0001_anonymous_disabled/","title":"001_0001 - AnonymousDisabled","text":""},{"location":"rules/001_project_settings/001_0001_anonymous_disabled/#business-apps-must-always-require-login","title":"Business apps must always require login","text":"

    Disable anonymous/guest access in Project Security

    "},{"location":"rules/001_project_settings/001_0001_anonymous_disabled/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Security\ninput: Security$ProjectSecurity.yaml\nrulename: AnonymousDisabled\nrulenumber: '001_0001'\nscope: package\nseverity: HIGH\n
    "},{"location":"rules/001_project_settings/001_0001_anonymous_disabled/#description","title":"Description","text":"

    No anonymous means every user must have valid login session or credentials

    "},{"location":"rules/001_project_settings/001_0001_anonymous_disabled/#remediation","title":"Remediation","text":"

    Disable anonymous/guest access in Project Security

    "},{"location":"rules/001_project_settings/001_0001_anonymous_disabled/#test-cases","title":"Test cases","text":"
    package app.mendix.project_settings.anonymous_disabled\nimport rego.v1\n\n# Test cases\ntest_allow if {\n    allow with input as {\"EnableGuestAccess\": false}\n}\ntest_no_allow if {\n    not allow with input as {\"EnableGuestAccess\": true}\n}\n
    "},{"location":"rules/001_project_settings/001_0002_demo_users_disabled/","title":"001_0002 - DemoUsersDisabled","text":""},{"location":"rules/001_project_settings/001_0002_demo_users_disabled/#business-apps-should-disable-demo-users","title":"Business apps should disable demo users","text":"

    Disable demo users in Project Security

    "},{"location":"rules/001_project_settings/001_0002_demo_users_disabled/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Security\ninput: Security$ProjectSecurity.yaml\nrulename: DemoUsersDisabled\nrulenumber: '001_0002'\nscope: package\nseverity: HIGH\n
    "},{"location":"rules/001_project_settings/001_0002_demo_users_disabled/#description","title":"Description","text":"

    No demo users

    "},{"location":"rules/001_project_settings/001_0002_demo_users_disabled/#remediation","title":"Remediation","text":"

    Disable demo users in Project Security

    "},{"location":"rules/001_project_settings/001_0002_demo_users_disabled/#test-cases","title":"Test cases","text":"
    package app.mendix.project_settings.demo_users_disabled\nimport rego.v1\n\n# Test cases\ntest_allow if {\n    allow with input as {\"EnableDemoUsers\": false}\n}\ntest_no_allow if {\n    not allow with input as {\"EnableDemoUsers\": true}\n}\n
    "},{"location":"rules/001_project_settings/001_0003_security_checks/","title":"001_0003 - SecurityChecks","text":""},{"location":"rules/001_project_settings/001_0003_security_checks/#ensure-security-rules-are-active","title":"Ensure security rules are active","text":"

    Set Security check to production in Project Security

    "},{"location":"rules/001_project_settings/001_0003_security_checks/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Security\ninput: Security$ProjectSecurity.yaml\nrulename: SecurityChecks\nrulenumber: '001_0003'\nscope: package\nseverity: HIGH\n
    "},{"location":"rules/001_project_settings/001_0003_security_checks/#description","title":"Description","text":"

    Any serious app needs entity access security configured

    "},{"location":"rules/001_project_settings/001_0003_security_checks/#remediation","title":"Remediation","text":"

    Set Security check to production in Project Security

    "},{"location":"rules/001_project_settings/001_0003_security_checks/#test-cases","title":"Test cases","text":"
    package app.mendix.project_settings.security_checks\nimport rego.v1\n\n# Test cases\ntest_allow if {\n    allow with input as {\n        \"CheckSecurity\": true,\n        \"SecurityLevel\": \"CheckEverything\",\n    }\n}\ntest_no_allow_1 if {\n    not allow with input as {\n        \"CheckSecurity\": false,\n        \"SecurityLevel\": \"CheckEverything\",\n    }\n}\ntest_no_allow_2 if {\n    not allow with input as {\n        \"CheckSecurity\": true,\n        \"SecurityLevel\": \"unknown\",\n    }\n}\n
    "},{"location":"rules/001_project_settings/001_0004_strong_password/","title":"001_0004 - StrongPasswordPolicy","text":""},{"location":"rules/001_project_settings/001_0004_strong_password/#strong-password-policy","title":"Strong password policy","text":"

    Ensure minimum password length of at least 8 characters and must use all character classes.

    "},{"location":"rules/001_project_settings/001_0004_strong_password/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Security\ninput: Security$ProjectSecurity.yaml\npriority: 5\nrulename: StrongPasswordPolicy\nrulenumber: '001_0004'\nscope: package\nseverity: HIGH\n
    "},{"location":"rules/001_project_settings/001_0004_strong_password/#description","title":"Description","text":"

    Bruteforce is quite common. Ensure passwords are very strong.

    "},{"location":"rules/001_project_settings/001_0004_strong_password/#remediation","title":"Remediation","text":"

    Ensure minimum password length of at least 8 characters and must use all character classes.

    "},{"location":"rules/001_project_settings/001_0004_strong_password/#test-cases","title":"Test cases","text":"
    package app.mendix.project_settings.strong_password\nimport rego.v1\n\n# Test cases\ntest_allow if {\n    allow with input as {\n        \"PasswordPolicySettings\": {\n            \"MinimumLength\": 9,\n            \"RequireDigit\": true,\n            \"RequireSymbol\": true,\n            \"RequireMixedCase\": true,\n        }\n    }\n}\n\ntest_no_allow_password_length if {\n    not allow with input as {\n        \"PasswordPolicySettings\": {\n            \"MinimumLength\": 3,\n            \"RequireDigit\": true,\n            \"RequireSymbol\": true,\n            \"RequireMixedCase\": true,\n        }\n    }\n}\n\ntest_no_allow_simple if {\n    not allow with input as {\n        \"PasswordPolicySettings\": {\n            \"MinimumLength\": 3,\n            \"RequireDigit\": false,\n            \"RequireSymbol\": true,\n            \"RequireMixedCase\": false,\n        }\n    }\n}\n
    "},{"location":"rules/002_domain_model/002_0001_number_of_entities/","title":"002_0001 - NumberOfEntities","text":""},{"location":"rules/002_domain_model/002_0001_number_of_entities/#no-more-than-15-persistent-entities-within-one-domain-model","title":"No more than 15 persistent entities within one domain model","text":"

    Split domain model into multiple modules.

    "},{"location":"rules/002_domain_model/002_0001_number_of_entities/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Maintainability\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: NumberOfEntities\nrulenumber: '002_0001'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/002_domain_model/002_0001_number_of_entities/#description","title":"Description","text":"

    The bigger the domain models, the harder they will be to maintain. It adds complexity to your security model as well. The smaller the modules, the easier to reuse.

    "},{"location":"rules/002_domain_model/002_0001_number_of_entities/#remediation","title":"Remediation","text":"

    Split domain model into multiple modules.

    "},{"location":"rules/002_domain_model/002_0001_number_of_entities/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.number_of_entities\nimport rego.v1\n\n\n# Test data\nentity_attr_0 = {\n    \"Name\": \"Entity1\",\n}\n\n\ntwenty := numbers.range(1, 20)\nentities_20 = [ \n    { \"Name\": entity_attr_0.Name }  | n := twenty[_]\n]\n\n\n# Test cases\ntest_no_entities if {\n    allow with input as {\"Entities\": null}\n}\n\ntest_1_entity if {\n    allow with input as {\"Entities\": [entity_attr_0]}\n}\n\ntest_2_entities if {\n    allow with input as {\"Entities\": [entity_attr_0, entity_attr_0]}\n}\n\ntest_20_entities if {\n    not allow with input as {\"Entities\": entities_20}\n}\n
    "},{"location":"rules/002_domain_model/002_0002_number_of_attributes/","title":"002_0002 - NumberOfAttributes","text":""},{"location":"rules/002_domain_model/002_0002_number_of_attributes/#no-more-that-35-attributes-in-an-entity","title":"No more that 35 attributes in an entity","text":"

    Normalize your datamodel. Split your object into multiple objects. If the attributes really belong to each other in a one-to-one relation, just draw a one-to-one relation between the objects.

    "},{"location":"rules/002_domain_model/002_0002_number_of_attributes/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Maintainability\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: NumberOfAttributes\nrulenumber: '002_0002'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/002_domain_model/002_0002_number_of_attributes/#description","title":"Description","text":"

    The bigger the entities, the slower your application will become when handling the data. This is because Mendix is using SELECT * queries a lot and will retrieve a lot of unnecessary data.

    "},{"location":"rules/002_domain_model/002_0002_number_of_attributes/#remediation","title":"Remediation","text":"

    Normalize your datamodel. Split your object into multiple objects. If the attributes really belong to each other in a one-to-one relation, just draw a one-to-one relation between the objects.

    "},{"location":"rules/002_domain_model/002_0002_number_of_attributes/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.number_of_attributes\nimport rego.v1\n\n\n# Test data\nattribute1 = {\n    \"Name\": \"Attribute1\"\n}\n\nentity_attr_0 = {\n    \"Name\": \"Entity1\",\n    \"Attributes\": null,\n}\n\nentity_attr_1 = {\n    \"Name\": \"Entity1\",\n    \"Attributes\": [\n        attribute1\n    ]\n}\n\nforty := numbers.range(1, 40)\nattributes_40 = [ \n    { \"Name\": attribute1.Name }  | n := forty[_]\n]\n\nentity_1_attr_40 = {\n    \"Name\": \"Entity1\",\n    \"Attributes\": attributes_40,\n}\n\n\n# Test cases\ntest_no_entities if {\n    allow with input as {\"Entities\": null}\n}\n\ntest_1_entity_1_attribute if {\n    allow with input as {\"Entities\": [entity_attr_1]}\n}\n\ntest_2_entities if {\n    allow with input as {\"Entities\": [entity_attr_1, entity_attr_1]}\n}\n\ntest_3_entities_1_empty if {\n    allow with input as {\"Entities\": [entity_attr_1, entity_attr_1, entity_attr_0]}\n}\n\ntest_1_entity_40_attributes_not_allowed if {\n    not allow with input as {\"Entities\": [entity_1_attr_40]}\n}\n\ntest_2_entity_40_attributes_1_empty_not_allowed if {\n    not allow with input as {\"Entities\": [entity_1_attr_40, entity_1_attr_40, entity_attr_0]}\n}\n
    "},{"location":"rules/002_domain_model/002_0003_inherit_from_administration_account/","title":"002_0003 - AvioidInheritanceFromAdministrationAccount","text":""},{"location":"rules/002_domain_model/002_0003_inherit_from_administration_account/#inherit-from-administrationaccount","title":"Inherit from Administration.Account","text":"

    Inherit from system.user instead or adapt Administration.Account so it fits your needs.

    "},{"location":"rules/002_domain_model/002_0003_inherit_from_administration_account/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Performance\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: AvioidInheritanceFromAdministrationAccount\nrulenumber: '002_0003'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/002_domain_model/002_0003_inherit_from_administration_account/#description","title":"Description","text":"

    There is no need to inherit from administration.account. Administration.account may simply be extended, this is not a system module. Avoid unnecessary inheritance as this has a negative effect on performance.

    "},{"location":"rules/002_domain_model/002_0003_inherit_from_administration_account/#remediation","title":"Remediation","text":"

    Inherit from system.user instead or adapt Administration.Account so it fits your needs.

    "},{"location":"rules/002_domain_model/002_0003_inherit_from_administration_account/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.inherit_from_administration_account\nimport rego.v1\n\n\n# Test data\nentity_negative = {\n    \"Name\": \"Entity1\",\n    \"MaybeGeneralization\": {\n        \"Type\": \"DomainModels$Generalization\",\n        \"Generalization\": \"System.FileDocument\"\n    }\n}\n\nentity_positive = {\n    \"Name\": \"Entity2\",\n    \"MaybeGeneralization\": {\n        \"Type\": \"DomainModels$Generalization\",\n        \"Generalization\": \"Administration.Account\"\n    }\n}\n\n\nentities_mixed = [entity_negative, entity_positive]\n\n\n# Test cases\ntest_no_entities if {\n    allow with input as {\"Entities\": null}\n}\n\ntest_entity_negative if {\n    allow with input as {\"Entities\": [entity_negative]}\n}\n\ntest_entity_positive if {\n    not allow with input as {\"Entities\": [entity_positive]}\n}\n\ntest_entities_mixed if {\n    not allow with input as {\"Entities\": entities_mixed}\n}\n
    "},{"location":"rules/002_domain_model/002_0004_inherit_from_non_system/","title":"002_0004 - AvoidInheritanceFromNonSystem","text":""},{"location":"rules/002_domain_model/002_0004_inherit_from_non_system/#inherit-from-non-system-module-is-discouraged","title":"Inherit from non System module is discouraged","text":"

    Instead of inheritance, just use separate objects which are associated to the main object. As an alternative, you can add the child\u2019s attributes to the super entity and add an ObjectType enumeration.

    "},{"location":"rules/002_domain_model/002_0004_inherit_from_non_system/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Performance\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: AvoidInheritanceFromNonSystem\nrulenumber: '002_0004'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/002_domain_model/002_0004_inherit_from_non_system/#description","title":"Description","text":"

    Inheritance, except from system module, is strongly discouraged because of the negative performance side effects.

    "},{"location":"rules/002_domain_model/002_0004_inherit_from_non_system/#remediation","title":"Remediation","text":"

    Instead of inheritance, just use separate objects which are associated to the main object. As an alternative, you can add the child\u2019s attributes to the super entity and add an ObjectType enumeration.

    "},{"location":"rules/002_domain_model/002_0004_inherit_from_non_system/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.inherit_from_non_system\nimport rego.v1\n\n\n# Test data\nentity_negative = {\n    \"Name\": \"Entity1\",\n    \"MaybeGeneralization\": {\n        \"Type\": \"DomainModels$Generalization\",\n        \"Generalization\": \"System.FileDocument\"\n    }\n}\n\nentity_positive = {\n    \"Name\": \"Entity2\",\n    \"MaybeGeneralization\": {\n        \"Type\": \"DomainModels$Generalization\",\n        \"Generalization\": \"Administration.Account\"\n    }\n}\n\n\nentities_mixed = [entity_negative, entity_positive]\n\n\n# Test cases\ntest_no_entities if {\n    allow with input as {\"Entities\": null}\n}\n\ntest_entity_negative if {\n    allow with input as {\"Entities\": [entity_negative]}\n}\n\ntest_entity_positive if {\n    not allow with input as {\"Entities\": [entity_positive]}\n}\n\ntest_entities_mixed if {\n    not allow with input as {\"Entities\": entities_mixed}\n}\n
    "},{"location":"rules/002_domain_model/002_0005_avoid_system_entity_association/","title":"002_0005 - AvoidSystemEntityAssociation","text":""},{"location":"rules/002_domain_model/002_0005_avoid_system_entity_association/#avoid-using-system-storage-objects-directly","title":"Avoid using system storage objects directly","text":"

    Remove direct associations with the System Domain Model. Use inheritance instead (i.e. Generalization in the entity properties).

    "},{"location":"rules/002_domain_model/002_0005_avoid_system_entity_association/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Security\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: AvoidSystemEntityAssociation\nrulenumber: '002_0005'\nscope: package\nseverity: HIGH\n
    "},{"location":"rules/002_domain_model/002_0005_avoid_system_entity_association/#description","title":"Description","text":"

    Always inherit for filedocuments and images. Never implement direct assocations to the System Domain Model, because of limits on the configuration of security.

    "},{"location":"rules/002_domain_model/002_0005_avoid_system_entity_association/#remediation","title":"Remediation","text":"

    Remove direct associations with the System Domain Model. Use inheritance instead (i.e. Generalization in the entity properties).

    "},{"location":"rules/002_domain_model/002_0005_avoid_system_entity_association/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.avoid_system_entity_association\nimport rego.v1\n\n\n# Test data\nnegative = {\n    \"Name\": \"HELLO_THERE1\",\n    \"Child\": \"SomeModule.FileDocument\",\n}\n\npositive = {\n    \"Name\": \"HELLO_THERE2\",\n    \"Child\": \"System.FileDocument\",\n}\n\n\n# Test cases\n\ntest_no_cross_associations if {\n    allow with input as {\"CrossAssociations\": null}\n}\n\ntest_negative if {\n    allow with input as {\"CrossAssociations\": [negative]}\n}\n\ntest_positive if {\n    not allow with input as {\"CrossAssociations\": [positive]}\n}\n\ntest_mixed if {\n    not allow with input as {\"CrossAssociations\": [negative, positive]}\n}\n
    "},{"location":"rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/","title":"002_0006 - AvoidTooManyVirtualAttributes","text":""},{"location":"rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/#too-many-microflow-attributes-virtual-attributes-inside-of-an-entity","title":"Too many Microflow attributes (virtual attributes) inside of an entity","text":"

    Optimize the number of virtual attributes inside of an entity. Reduce to 10 or less.

    "},{"location":"rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Performance\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: AvoidTooManyVirtualAttributes\nrulenumber: '002_0006'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/#description","title":"Description","text":"

    Too many Microflow attributes (virtual attributes) inside of an entity will cause performance issues.

    "},{"location":"rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/#remediation","title":"Remediation","text":"

    Optimize the number of virtual attributes inside of an entity. Reduce to 10 or less.

    "},{"location":"rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.avoid_too_many_virtual_attributes\n\nimport rego.v1\n\n\n# Test data\n\n\nattr_0 := {\n          \"$Type\": \"DomainModels$Attribute\",\n          \"Name\": \"VA_age\",\n          \"Value\": {\n            \"$Type\": \"DomainModels$CalculatedValue\"\n          }\n}\n\n\ntwenty := numbers.range(1, 20)\nattr_20 = [ \n    { \"Name\": attr_0.Name, \"Value\": attr_0.Value }  | n := twenty[_]\n]\n\npositive := {\n  \"Entities\": [\n    {\n      \"$Type\": \"DomainModels$EntityImpl\",\n      \"Attributes\": [\n        {\n          \"$Type\": \"DomainModels$Attribute\",\n          \"Name\": \"VA_age\",\n          \"Value\": {\n            \"$Type\": \"DomainModels$CalculatedValue\"\n          }\n        },\n        {\n          \"$Type\": \"DomainModels$Attribute\",\n          \"Name\": \"Year\",\n          \"Value\": {\n            \"$Type\": \"DomainModels$StoredValue\"\n          }\n        }\n      ],\n      \"Name\": \"Bike\"\n    }\n  ]\n}\n\nnegative := {\n  \"Entities\": [\n    {\n      \"$Type\": \"DomainModels$EntityImpl\",\n      \"Attributes\": attr_20,\n      \"Name\": \"Bike\"\n    }\n  ]\n}\n\n# Test cases\n\ntest_positive if {\n    allow with input as positive\n}\n\ntest_negative if {\n    not allow with input as negative\n}\n
    "},{"location":"rules/002_domain_model/002_0007_avoid_using_validation_rules/","title":"002_0007 - AvoidUsingValidationRules","text":""},{"location":"rules/002_domain_model/002_0007_avoid_using_validation_rules/#avoid-using-validation-rules-for-domain-model","title":"Avoid using validation rules for domain model.","text":"

    Remove datamodel validation rules.

    "},{"location":"rules/002_domain_model/002_0007_avoid_using_validation_rules/#metadata","title":"Metadata","text":"
    authors:\n- Viktor Berlov <viktor@cinaq.com>\ncategory: Maintainability\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: AvoidUsingValidationRules\nrulenumber: '002_0007'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/002_domain_model/002_0007_avoid_using_validation_rules/#description","title":"Description","text":"

    Validation rules on domain model level will give the users unexpected errors.

    "},{"location":"rules/002_domain_model/002_0007_avoid_using_validation_rules/#remediation","title":"Remediation","text":"

    Remove datamodel validation rules.

    "},{"location":"rules/002_domain_model/002_0007_avoid_using_validation_rules/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.avoid_using_validation_rules\n\nimport rego.v1\n\n\n# Test data\n\npositive := {\n  \"Entities\": [\n    {\n      \"ValidationRules\": [],\n      \"Name\": \"Bike\"\n    }\n  ]\n}\n\nnegative := {\n  \"Entities\": [\n    {\n      \"ValidationRules\": [\n        {\n          \"$Type\": \"DomainModels$ValidationRule\",\n          \"Attribute\": \"MyFirstModule.Bike.Name\",\n          \"Message\": {\n            \"$Type\": \"Texts$Text\",\n            \"Items\": [\n              {\n                \"$Type\": \"Texts$Translation\",\n                \"LanguageCode\": \"en_US\",\n                \"Text\": \"Not a good name\"\n              }\n            ]\n          },\n          \"RuleInfo\": {\n            \"$Type\": \"DomainModels$EqualsToRuleInfo\",\n            \"EqualsToAttribute\": \"\",\n            \"UseValue\": true,\n            \"Value\": \"admin\"\n          }\n        }\n      ],\n      \"Name\": \"Bike\"\n    }\n  ]\n}\n\n# Test cases\n\ntest_positive if {\n    allow with input as positive\n}\n\ntest_negative if {\n    not allow with input as negative\n}\n
    "},{"location":"rules/003_modules/003_0001_number_of_modules/","title":"003_0001 - NumberOfModules","text":""},{"location":"rules/003_modules/003_0001_number_of_modules/#more-than-20-modules-in-project","title":"More than 20 modules in project","text":"

    Consider a multi-app stategy to avoid creating one big (unmaintainable) monstrous application.

    "},{"location":"rules/003_modules/003_0001_number_of_modules/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Maintainability\ninput: Metadata.yaml\nrulename: NumberOfModules\nrulenumber: '003_0001'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/003_modules/003_0001_number_of_modules/#description","title":"Description","text":"

    The bigger the application, the harder to maintain.

    "},{"location":"rules/003_modules/003_0001_number_of_modules/#remediation","title":"Remediation","text":"

    Consider a multi-app stategy to avoid creating one big (unmaintainable) monstrous application.

    "},{"location":"rules/003_modules/003_0001_number_of_modules/#test-cases","title":"Test cases","text":"
    package app.mendix.modules.number_of_modules\nimport rego.v1\n\n\n# Test data\nmodule = {\n    \"Name\": \"Module\",\n    \"Attributes\": {\n        \"FromAppStore\": false,\n    }\n}\n\n\nthirty := numbers.range(1, 30)\nmodules_30 = [ \n    module | n := thirty[_]\n]\n\n\n# Test cases\ntest_empty if {\n    allow with input as {\"Modules\": null}\n}\n\ntest_1_module if {\n    allow with input as {\"Modules\": [module]}\n}\n\ntest_2_modules if {\n    allow with input as {\"Modules\": [module, module]}\n}\n\ntest_30_modules if {\n    not allow with input as {\"Modules\": modules_30}\n}\n
    "},{"location":"rules/004_pages/004_0001_inline_style_property_used/","title":"004_0001 - InlineStylePropertyUsed","text":""},{"location":"rules/004_pages/004_0001_inline_style_property_used/#inline-style-property-used","title":"Inline style property used","text":"

    Use generic classes instead, defined by the theme.

    "},{"location":"rules/004_pages/004_0001_inline_style_property_used/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Maintainability\ninput: '*/**/*$Page.yaml'\nrulename: InlineStylePropertyUsed\nrulenumber: '004_0001'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/004_pages/004_0001_inline_style_property_used/#description","title":"Description","text":"

    Avoid using the style property, because this will make the life of your UI designer a lot more complicated. It will be harder to overrule styles from CSS file level.

    "},{"location":"rules/004_pages/004_0001_inline_style_property_used/#remediation","title":"Remediation","text":"

    Use generic classes instead, defined by the theme.

    "},{"location":"rules/004_pages/004_0001_inline_style_property_used/#test-cases","title":"Test cases","text":"
    package app.mendix.pages.inline_style_property_used\nimport rego.v1\n\n\n# Test data\nform_simple = {\n    \"$Type\": \"Forms$Page\",\n    \"Name\": \"Page1\",\n    \"Appearance\": {\n        \"$Type\": \"Forms$Appearance\",\n        \"Class\": \"\",\n        \"DesignProperties\": null,\n        \"DynamicClasses\": \"\",\n        \"Style\": \"\",\n    },\n}\nform_simple_negative = {\n    \"$Type\": \"Forms$Page\",\n    \"Name\": \"Page1\",\n    \"Appearance\": {\n        \"$Type\": \"Forms$Appearance\",\n        \"Class\": \"\",\n        \"DesignProperties\": null,\n        \"DynamicClasses\": \"\",\n        \"Style\": \"color: red;\",\n    },\n}\n\nform_nested = {\n    \"Name\": \"Page1\",\n    \"FormCall\": {\n        \"Arguments\": [\n            {\n                \"Widgets\": [\n                    {\n                        \"$Type\": \"Forms$LayoutGrid\",\n                        \"Name\": \"layoutGrid2\",\n                        \"Rows\": [\n                            {\n                                \"$Type\": \"Forms$LayoutGridRow\",\n                                \"Columns\": [\n                                    {\n                                        \"$Type\": \"Forms$LayoutGridColumn\",\n                                        \"Appearance\": {\n                                            \"$Type\": \"Forms$Appearance\",\n                                            \"Class\": \"\",\n                                            \"DesignProperties\": null,\n                                            \"DynamicClasses\": \"\",\n                                            \"Style\": \"\",\n                                        }\n                                    },\n                                ],\n                            },\n                        ],\n                    },\n                ],\n            },\n        ],\n    },\n}\n\nform_nested_negative = {\n    \"Name\": \"Page1\",\n    \"FormCall\": {\n        \"Arguments\": [\n            {\n                \"Widgets\": [\n                    {\n                        \"$Type\": \"Forms$LayoutGrid\",\n                        \"Name\": \"layoutGrid2\",\n                        \"Rows\": [\n                            {\n                                \"$Type\": \"Forms$LayoutGridRow\",\n                                \"Columns\": [\n                                    {\n                                        \"$Type\": \"Forms$LayoutGridColumn\",\n                                        \"Appearance\": {\n                                            \"$Type\": \"Forms$Appearance\",\n                                            \"Class\": \"\",\n                                            \"DesignProperties\": null,\n                                            \"DynamicClasses\": \"\",\n                                            \"Style\": \"color: orange;\",\n                                        }\n                                    },\n                                ],\n                            },\n                        ],\n                    },\n                ],\n            },\n        ],\n    },\n}\n\n\n\n# Test cases\ntest_simple if {\n    allow with input as form_simple\n}\n\ntest_simple_negative if {\n    not allow with input as form_simple_negative\n}\n\ntest_nested if {\n    allow with input as form_nested\n}\n\ntest_nested_negative if {\n    not allow with input as form_nested_negative\n}\n
    "},{"location":"rules/005_microflows/005_0001_empty_string_check_not_complete/","title":"005_0001 - EmptyStringCheckNotComplete","text":""},{"location":"rules/005_microflows/005_0001_empty_string_check_not_complete/#empty-string-check-not-complete","title":"Empty String check not complete","text":"

    Always check a string for empty based on != empty and != \"\". The first one equals database NULL value, the latter one indicates a truncated string.

    "},{"location":"rules/005_microflows/005_0001_empty_string_check_not_complete/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Error\ninput: '*/**/*$Microflow.yaml'\nrulename: EmptyStringCheckNotComplete\nrulenumber: '005_0001'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/005_microflows/005_0001_empty_string_check_not_complete/#description","title":"Description","text":"

    Technically, there is a difference between empty and \"\". Make sure to check them both.

    "},{"location":"rules/005_microflows/005_0001_empty_string_check_not_complete/#remediation","title":"Remediation","text":"

    Always check a string for empty based on != empty and != \"\". The first one equals database NULL value, the latter one indicates a truncated string.

    "},{"location":"rules/005_microflows/005_0001_empty_string_check_not_complete/#test-cases","title":"Test cases","text":"
    package app.mendix.microflows.empty_string_check_not_complete\nimport rego.v1\n\n\n# Test data\nmicroflow_good = {\n    \"$Type\": \"Microflow$Page\",\n    \"Name\": \"mf1\",\n    \"ObjectCollection\": {\n        \"$Type\": \"Microflows$MicroflowObjectCollection\",\n        \"Objects\": [\n            {\n                \"$Type\": \"Microflows$ExclusiveSplit\",\n                \"SplitCondition\": {\n                    \"$Type\": \"Microflows$ExpressionSplitCondition\",\n                    \"Expression\": \"$Variable != empty and $Variable != ''\",\n                },\n            },\n        ],\n    },\n}\n\nmicroflow_bad = {\n    \"$Type\": \"Microflow$Page\",\n    \"Name\": \"mf1\",\n    \"ObjectCollection\": {\n        \"$Type\": \"Microflows$MicroflowObjectCollection\",\n        \"Objects\": [\n            {\n                \"$Type\": \"Microflows$ExclusiveSplit\",\n                \"SplitCondition\": {\n                    \"$Type\": \"Microflows$ExpressionSplitCondition\",\n                    \"Expression\": \"$Variable != ''\",\n                },\n            },\n        ],\n    },\n}\n\n# Test cases\ntest_simple if {\n    allow with input as microflow_good\n}\n\ntest_simple_negative if {\n    not allow with input as microflow_bad\n}\n
    "}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":"

    MxLint (Formerly Mendix-CLI) is a set of tools to help you build high quality Mendix apps. This is inspired by the success of projects like PyLint and other linting systems which provide a way to enforce code standards, best practices and guidelines in your code.

    Your browser does not support the video tag."},{"location":"#mission","title":"Mission","text":"
    • Become the standard for linting Mendix apps.
    • Provide a way to enforce best practices and guidelines in your code.
    • Provide a way to share knowledge and experience between developers.
    "},{"location":"#ecosystem","title":"Ecosystem","text":"

    Mxlint is a collection of tools and projects to help you build high quality Mendix apps.

    • MxLint CLI: The main tool of the MxLint ecosystem. It is used to lint your Mendix project. Can be used directly as CLI tool or as part of a CI/CD pipeline.
    • MxLint Extension: An extension for Mendix Studio Pro to help you lint your Mendix project during development.
    • MxLint Rules: A place to find and share your rules with the community.
    • MxLint Docs: This documentation you are reading right now. It also extracts documentation from the rules to provide you with a better experience in a single place.
    "},{"location":"#bugs-and-contribution","title":"Bugs and Contribution","text":"

    We welcome all to report bugs or even better contribute to MxLint. This can be done creating issues or Pull request towards the repositories listed above.

    "},{"location":"#philosophy","title":"Philosophy","text":"

    MxLint is built on the philosophy that the best way to build Mendix apps is to follow the best practices and guidelines that are outlined in the Mendix Best practices documentation and most importantly based on the community experience. The collective experience of the developers in the Mendix community is distilled into rules that are then enforced with formal rules.

    We also believe knowledge should be shared and that by sharing our experiences we can all learn from each other. Therefore we are aiming for this project to be a community driven project where we can all learn from each other. A project owned by the community and for the community. To live up to this goal we decided to make MxLint an open source project and we welcome contributions from the community.

    "},{"location":"about/","title":"About","text":"

    MxLint is an initiative of CINAQ. We believe the Mendix community can benefit from a set of tools to help them build high quality Mendix apps. Where knowledge is shared with everyone.

    "},{"location":"about/#licensing","title":"Licensing","text":"

    All MxLint related repositories are licensed under the AGPL-3.0 license. This means you have the right to use, modify, and distribute the software, as long as you comply with the terms of the license.

    "},{"location":"about/#contributing","title":"Contributing","text":"

    We welcome contributions to MxLint from the community. If you have an idea for a rule or a feature, please open an issue or submit a pull request. We are also happy to help you with your contribution.

    "},{"location":"code-of-conduct/","title":"Code of Conduct","text":""},{"location":"code-of-conduct/#our-pledge","title":"Our Pledge","text":"

    We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.

    We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.

    "},{"location":"code-of-conduct/#our-standards","title":"Our Standards","text":"

    Examples of behavior that contributes to a positive environment for our community include:

    • Demonstrating empathy and kindness toward other people
    • Being respectful of differing opinions, viewpoints, and experiences
    • Giving and gracefully accepting constructive feedback
    • Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
    • Focusing on what is best not just for us as individuals, but for the overall community

    Examples of unacceptable behavior include:

    • The use of sexualized language or imagery, and sexual attention or advances of any kind
    • Trolling, insulting or derogatory comments, and personal or political attacks
    • Public or private harassment
    • Publishing others' private information, such as a physical or email address, without their explicit permission
    • Other conduct which could reasonably be considered inappropriate in a professional setting
    "},{"location":"code-of-conduct/#enforcement-responsibilities","title":"Enforcement Responsibilities","text":"

    Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.

    "},{"location":"code-of-conduct/#scope","title":"Scope","text":"

    This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online

    "},{"location":"features/","title":"Features","text":"
    • Convert mendix MPR format to Yaml files
    • Check Mendix model against predefined rule-set
    "},{"location":"quickstart/","title":"Quickstart","text":"

    MxLint is very versatile. Try out with one of the following guides:

    • Mendix Studio Pro Extension
    • Pipeline Integration

    The CLI can be used or integrated in many different ways also. Feel free to share with us if your use case is not yet documented.

    "},{"location":"terminologies/","title":"Terminologies","text":""},{"location":"terminologies/#rule","title":"Rule","text":"

    A rule is a single unit of linting that checks for a specific issue in your Mendix project.

    "},{"location":"terminologies/#ruleset","title":"Ruleset","text":"

    A ruleset is a collection of rules that are used to lint a specific part of your Mendix project.

    "},{"location":"terminologies/#linting","title":"Linting","text":"

    Linting is the process of running a ruleset against a Mendix project to check for issues.

    "},{"location":"terminologies/#policy","title":"Policy","text":"

    In the context MxLint, policy is the same as a rule. You will see the terms policy and rule used interchangeably.

    "},{"location":"use-cases/","title":"Use cases","text":"

    Here we attempt to show you what you can accomplish with MxLint. It has many purposes thanks to the fact is works lightning fast and versatile.

    "},{"location":"use-cases/#textual-revision-diff-ing","title":"Textual revision diff-ing","text":"

    To compare model changes normally you need Mendix Studio Pro. With MxLint you can use any existing Merge request workflow to review model changes. Albeit in a textual instead of visual way:

    Above use case is best when using the extension with your project in Mendix Studio Pro

    "},{"location":"use-cases/#get-feedback-on-best-practices-in-studio-pro","title":"Get feedback on best practices in Studio Pro","text":"

    With the new recent extensibility support in Mendix Studio pro, MxLint is closer than ever to you. With the extension, you get almost real-time feedback on your project quality. It does not use AI or LLM but old-school, objective and deterministic rules.

    Start here with the extension

    "},{"location":"use-cases/#enforce-best-practices-in-pipelines","title":"Enforce best practices in pipelines","text":"

    In order to guard quality and maintainability of Mendix apps as a platform owner, you can build quality gates into your pipelines. This way you ensure minimum quality criterias are met as part of you CI/CD workflows.

    Learn how this works in pipelines

    "},{"location":"use-cases/#organizational-custom-best-practices","title":"Organizational custom best practices","text":"

    As MxLint is Open source, the possibilities are endless. This means you are free and able to write your own custom rules.

    "},{"location":"use-cases/#others","title":"Others","text":"

    Do you use MxLint in other ways? Let us know.

    "},{"location":"CLI/","title":"Introduction","text":""},{"location":"CLI/microflow-transformation/","title":"Microflow transformation","text":""},{"location":"CLI/microflow-transformation/#microflow-transformation","title":"Microflow Transformation","text":"

    This document outlines the thought process and approach on how to transform a program (or function if you want) from a graph like format to as-linear-as-possible textual representation.

    "},{"location":"CLI/microflow-transformation/#mendix-microflows-are-not-dags","title":"Mendix Microflows are not DAG's","text":"

    Mendix Microflows are in abstract form a graph structure. For those warry of graph-theory, at first sight most graphs are Directed Acyclic Graph (DAG). Because there is a starting point and multiple end states. There is a minor detail: it's possible to create loops using Exclusive Split and Exclusive Merge actions. These are comparable to defining labels and goto in more classical programming languages.

    "},{"location":"CLI/microflow-transformation/#goto","title":"Goto","text":""},{"location":"events/","title":"Introduction","text":"

    This section gives an overview of all social events held in the past.

    "},{"location":"events/2024-10-24-mendix-meetup/","title":"Mendix Meetup 2024-10-24","text":""},{"location":"events/2024-10-24-mendix-meetup/#agenda","title":"Agenda","text":"
    • Quality Driven Development Initiative \u2013 Xiwen Cheng (10min)
    • ABN AMRO shares a story about balancing low-code, low-ops & low risk and why they support the MxLint initiative - Bart Zantingh (20min)
    • MxLint \u2013 Xiwen Cheng (25min)
    "},{"location":"events/2024-10-24-mendix-meetup/#speakers","title":"Speakers","text":"
    • Bart Zantingh, Lead Mendix Developer, ABN AMRO
    • Xiwen (Steven) Cheng, Cloud Architect, CINAQ
    "},{"location":"events/2024-10-24-mendix-meetup/#video-recordings","title":"Video recordings","text":""},{"location":"events/2024-10-24-mendix-meetup/#quality-driven-development-initiative","title":"Quality Driven Development Initiative","text":"Your browser does not support the video tag."},{"location":"events/2024-10-24-mendix-meetup/#abn-amro-shares-a-story-about-balancing-low-code-low-ops-low-risk-and-why-they-support-the-mxlint-initiative","title":"ABN AMRO shares a story about balancing low-code, low-ops & low risk and why they support the MxLint initiative","text":"Your browser does not support the video tag."},{"location":"events/2024-10-24-mendix-meetup/#mxlint","title":"MxLint","text":"Your browser does not support the video tag."},{"location":"mendix-documents/","title":"Introduction","text":"

    This section gives an overview of different Mendix documents that are relevant to MxLint.

    You can use these documents to understand the structure of different types of Mendix documents. This will help you write rules for MxLint.

    "},{"location":"mendix-documents/domain-model/","title":"Domain Model","text":""},{"location":"mendix-documents/domain-model/#example","title":"Example","text":"
    $Type: DomainModels$DomainModel\nAnnotations:\n- $Type: DomainModels$Annotation\n  Caption: \"This Domain model defines the data structure of this module. \\r\\n\\r\\nAdd\n    new entities and associations to define your database tables and their relations.\n    \\r\\nWe automatically provision the underlying database for you.\\r\\n\\r\\nTo define\n    in-memory data, you can turn off the 'persistable' property on the entity.\\r\\n\\r\\nMore\n    info: https://docs.mendix.com/refguide/domain-model\"\n  ExportLevel: Hidden\n  Width: 440\nAssociations: null\nCrossAssociations: null\nDocumentation: \"\"\nEntities:\n- $Type: DomainModels$EntityImpl\n  AccessRules:\n  - $Type: DomainModels$AccessRule\n    AllowCreate: false\n    AllowDelete: false\n    AllowedModuleRoles:\n    - MyFirstModule.User\n    DefaultMemberAccessRights: ReadOnly\n    Documentation: \"\"\n    MemberAccesses:\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.Image.PublicThumbnailPath\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.Image.EnableCaching\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.FileDocument.FileID\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.FileDocument.Name\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.FileDocument.DeleteAfterDownload\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.FileDocument.Contents\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.FileDocument.HasContents\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: System.FileDocument.Size\n    XPathConstraint: \"\"\n    XPathConstraintCaption: \"\"\n  Attributes: null\n  Documentation: \"\"\n  Events: null\n  ExportLevel: Hidden\n  Indexes: null\n  MaybeGeneralization:\n    $Type: DomainModels$Generalization\n    Generalization: System.Image\n  Name: Photo\n  Source: null\n  ValidationRules: null\n- $Type: DomainModels$EntityImpl\n  AccessRules:\n  - $Type: DomainModels$AccessRule\n    AllowCreate: false\n    AllowDelete: false\n    AllowedModuleRoles:\n    - MyFirstModule.User\n    DefaultMemberAccessRights: ReadOnly\n    Documentation: \"\"\n    MemberAccesses:\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: MyFirstModule.Bike.Name\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: MyFirstModule.Bike.PurchaseDate\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: MyFirstModule.Bike.VA_age\n    - $Type: DomainModels$MemberAccess\n      AccessRights: ReadOnly\n      Association: \"\"\n      Attribute: MyFirstModule.Bike.Year\n    XPathConstraint: \"\"\n    XPathConstraintCaption: \"\"\n  Attributes:\n  - $Type: DomainModels$Attribute\n    Documentation: \"\"\n    ExportLevel: Hidden\n    Name: Name\n    NewType:\n      $Type: DomainModels$StringAttributeType\n      Length: 200\n    Value:\n      $Type: DomainModels$StoredValue\n      DefaultValue: \"\"\n  - $Type: DomainModels$Attribute\n    Documentation: \"\"\n    ExportLevel: Hidden\n    Name: PurchaseDate\n    NewType:\n      $Type: DomainModels$DateTimeAttributeType\n      LocalizeDate: true\n    Value:\n      $Type: DomainModels$StoredValue\n      DefaultValue: \"\"\n  - $Type: DomainModels$Attribute\n    Documentation: \"\"\n    ExportLevel: Hidden\n    Name: VA_age\n    NewType:\n      $Type: DomainModels$IntegerAttributeType\n    Value:\n      $Type: DomainModels$CalculatedValue\n      Microflow: MyFirstModule.VA_Age\n      PassEntity: true\n  - $Type: DomainModels$Attribute\n    Documentation: \"\"\n    ExportLevel: Hidden\n    Name: Year\n    NewType:\n      $Type: DomainModels$IntegerAttributeType\n    Value:\n      $Type: DomainModels$StoredValue\n      DefaultValue: \"2010\"\n  Documentation: \"\"\n  Events: null\n  ExportLevel: Hidden\n  Indexes: null\n  MaybeGeneralization:\n    $Type: DomainModels$NoGeneralization\n    HasChangedByAttr: false\n    HasChangedDateAttr: false\n    HasCreatedDateAttr: false\n    HasOwnerAttr: false\n    Persistable: true\n  Name: Bike\n  Source: null\n  ValidationRules: null\n
    "},{"location":"mendix-studio-pro-extension/","title":"Introduction","text":"

    MxLint can be used directly inside of the Mendix Studio Pro. This method gives Mendix Developers quick feedback on how well they are doing in terms of following the best practices.

    "},{"location":"mendix-studio-pro-extension/#known-limitations","title":"Known limitations","text":"
    • Scanning takes a few seconds to complete and it depends on the size of the Mendix project and the number of rules
    • Currently very limited configuration possible
    • Probaly many more...
    "},{"location":"mendix-studio-pro-extension/configuration/","title":"Configuration","text":"

    Currently there are no customization possibilities.

    Well... there is one...

    "},{"location":"mendix-studio-pro-extension/configuration/#custom-rules","title":"Custom rules","text":"

    You can follow the instructions to create a custom rule. Custom rules can be added into $project/.mendix-cache/rules/ directory. These will be picked up automatically by the MxLint Extension.

    "},{"location":"mendix-studio-pro-extension/development/","title":"Development","text":""},{"location":"mendix-studio-pro-extension/development/#librariesreferences","title":"Libraries/References","text":"

    We try to keep the number of dependencies to a minimum. But sometimes it is handy to re-use existing projects.

    • MxLintExtension source code
    • Pico CSS to help with styling.
    "},{"location":"mendix-studio-pro-extension/development/#osx-macos","title":"OSX (MacOS)","text":"
    • Visual Studio Code
    • Mendix Studio Pro 10.12.2
    • dotNet core SDK vscode
    • C# dev kit VSCode extension (inside of VSCode)
    "},{"location":"mendix-studio-pro-extension/development/#build","title":"Build","text":"
    make\n
    "},{"location":"mendix-studio-pro-extension/development/#test","title":"Test","text":"
    • Open Mendix Studio Pro with --enable-extension-development flag: /Applications/Studio\\ Pro\\ 10.12.2.41995-Beta.app/Contents/MacOS/studiopro --enable-extension-development
    • Open local project located in resources/App
    "},{"location":"mendix-studio-pro-extension/development/#windows","title":"Windows","text":"
    • Visual Studio 2022
    • Mendix Studio Pro 10.12.2

    Open Powershell as Administrator and run the following command:

    Set-ExecutionPolicy -ExecutionPolicy RemoteSigned\n
    "},{"location":"mendix-studio-pro-extension/development/#build_1","title":"Build","text":"
    • Run the script to build:
      .\\dev\\build-windows.ps1\n
    "},{"location":"mendix-studio-pro-extension/development/#test_1","title":"Test","text":"
    • Open studiopro.exe with --enable-extension-development flag.
    • Open local project located in resources/App
    "},{"location":"mendix-studio-pro-extension/installation/","title":"Installation","text":""},{"location":"mendix-studio-pro-extension/installation/#prerequisites","title":"Prerequisites","text":"
    • Mendix Studio Pro 10.12.x or later (Windows only)
    • an existing Mendix project
    • Download a release

    The following two steps are needed for now because MxLint Extension is still in Beta. One day you will not need to do these anymore as an end-user.

    "},{"location":"mendix-studio-pro-extension/installation/#allow-unsigned-code","title":"Allow unsigned code","text":"

    MxLint-CLI is not signed yet. The extension automatically downloads the CLI and other dependencies. To allow CLI to run you must

    • Open Powershell as Administrator and then run:
      Set-ExecutionPolicy -ExecutionPolicy RemoteSigned\n
    • After that reboot your machine

    Above should only be needed once.

    "},{"location":"mendix-studio-pro-extension/installation/#enable-developer-extensions-in-studio-pro","title":"Enable Developer extensions in Studio Pro","text":"

    Mendix Studio needs to be started with a special flag. To enable this:

    • Create a shortcut to your Mendix Studio Pro if you don't have it on your desktop already
    • Right click on the shortcut and choose Properties
    • In the Target, append the following string --enable-extension-development after the double quote. Make sure to have a space after this right most double quote.

    Now your Studio pro is ready to use Extensions.

    "},{"location":"mendix-studio-pro-extension/installation/#steps","title":"Steps","text":"

    The following must be done for each project you want to add this extension:

    • Create a directory called extensions inside of your project directory
    • Unpack the zipfile into the extensions directory. You will see a directory at $project/extensions/MxLintExtension
    • Open Mendix Studio pro with extension development flag
    • Open your app
    "},{"location":"pipeline-integration/","title":"Introduction","text":"

    While the Mendix Studio Pro extension enables you to get immediate feedback on your project quality as a developer, MxLint can also be used as governance tool.

    This can be achieved by for instance adding MxLint to your (existing) Continuous Integration pipelines. You can also choose to scan against a more extensive set of rules than locally on developer machines.

    The most important benefit of Pipeline integrations is to enable governance on a platform level. This ensures independent teams are following company standards and best practices centrally managed.

    "},{"location":"pipeline-integration/setup/","title":"Setup","text":"

    TODO

    "},{"location":"pipeline-integration/setup/#gitlab","title":"Gitlab","text":""},{"location":"pipeline-integration/setup/#azure-devops","title":"Azure DevOps","text":""},{"location":"rules/","title":"Introduction","text":"

    Linting rules are written in Rego and are used to lint Mendix projects.

    "},{"location":"rules/create/","title":"Create Rule","text":""},{"location":"rules/create/#rego-introduction","title":"Rego Introduction","text":"

    Rules are expressed with the help of the powerful OPA Rego language. Rego is a declarative language that is purpose-built for expressing policies over complex hierarchical data structures. Rego is designed to be easy to read and write, even for non-programmers. Rego is a safe language that is decidable and has a small trusted computing base. Rego is also designed to be easy to integrate with other systems.

    "},{"location":"rules/create/#policy-rule","title":"Policy rule","text":"

    To create a new policy, you need to create a new Rego file in the policies directory. The file name should be in the format XXX_YYY.rego where XXX is the policy number and YYY is the policy name. For example, 001_0001_anonymous_disabled.rego.

    The policy file should contain the following structure:

    001_0001_anonymous_disabled.rego
    # METADATA\n# scope: package\n# title: Business apps must always require login\n# description: No anonymous means every user must have valid login session or credentials\n# authors:\n# - Xiwen Cheng <x@cinaq.com>\n# custom:\n#  category: Security\n#  rulename: AnonymousDisabled\n#  severity: HIGH\n#  rulenumber: 001_0001\n#  remediation: Disable anonymous/guest access in Project Security\n#  input: Security$ProjectSecurity.yaml\npackage app.mendix.project_settings.anonymous_disabled\nimport rego.v1\nannotation := rego.metadata.chain()[1].annotations\n\ndefault allow := false\nallow if count(errors) == 0\n\nerrors contains error if {\n    input.EnableGuestAccess == true\n    error := sprintf(\"[%v, %v, %v] %v\",\n        [\n            annotation.custom.severity,\n            annotation.custom.category,\n            annotation.custom.rulenumber,\n            annotation.title,\n        ]\n    )\n}\n
    • METADATA provide information about the policy.
    • package statement is used to define the policy package.
    • allow statement is used to define the conditions under which the policy is allowed.
    • errors statement is used to define the errors that are returned if the policy is not allowed.
    • input states which files are used as input for the policy. This can be a single file or an expression like */DomainModels$DomainModel.yaml to match multiple files.
    "},{"location":"rules/create/#policy-testing","title":"Policy testing","text":"

    The best way to create a new policy is to copy an existing policy and modify it to suit your needs. There is also an accompanying test file for each policy that you can use to test your policy. The test file should be in the same directory as the policy file and should be named XXX_YYY_test.rego. For example, 001_0001_anonymous_disabled_test.rego.

    package app.mendix.project_settings.anonymous_disabled\nimport rego.v1\n\n# Test cases\ntest_allow if {\n    allow with input as {\"EnableGuestAccess\": false}\n}\ntest_no_allow if {\n    not allow with input as {\"EnableGuestAccess\": true}\n}\n

    To test your policy, run the following command:

    $ ./run-policy-tests.sh\npolicies/001_project_settings/001_0001_anonymous_disabled_test.rego:\ndata.app.mendix.project_settings.anonymous_disabled.test_allow: PASS (3.031209ms)\ndata.app.mendix.project_settings.anonymous_disabled.test_no_allow: PASS (413.375\u00b5s)\n\npolicies/001_project_settings/001_0002_demo_users_disabled_test.rego:\ndata.app.mendix.project_settings.demo_users_disabled.test_allow: PASS (105.541\u00b5s)\ndata.app.mendix.project_settings.demo_users_disabled.test_no_allow: PASS (200.5\u00b5s)\n\npolicies/001_project_settings/001_0003_security_checks_test.rego:\ndata.app.mendix.project_settings.security_checks.test_allow: PASS (111.584\u00b5s)\ndata.app.mendix.project_settings.security_checks.test_no_allow_1: PASS (842.667\u00b5s)\ndata.app.mendix.project_settings.security_checks.test_no_allow_2: PASS (206.458\u00b5s)\n\npolicies/001_project_settings/001_0004_strong_password_test.rego:\ndata.app.mendix.project_settings.strong_password.test_allow: PASS (148.792\u00b5s)\ndata.app.mendix.project_settings.strong_password.test_no_allow_password_length: PASS (538.959\u00b5s)\ndata.app.mendix.project_settings.strong_password.test_no_allow_simple: PASS (286.916\u00b5s)\n\npolicies/002_domain_model/002_0001_number_of_entities_test.rego:\ndata.app.mendix.domain_model.number_of_entities.test_no_entities: PASS (134\u00b5s)\ndata.app.mendix.domain_model.number_of_entities.test_1_entity: PASS (194.666\u00b5s)\ndata.app.mendix.domain_model.number_of_entities.test_2_entities: PASS (187.334\u00b5s)\ndata.app.mendix.domain_model.number_of_entities.test_20_entities: PASS (1.375709ms)\n\npolicies/002_domain_model/002_0002_number_of_attributes_test.rego:\ndata.app.mendix.domain_model.number_of_attributes.test_no_entities: PASS (263.5\u00b5s)\ndata.app.mendix.domain_model.number_of_attributes.test_1_entity_1_attribute: PASS (519.416\u00b5s)\ndata.app.mendix.domain_model.number_of_attributes.test_2_entities: PASS (303.458\u00b5s)\ndata.app.mendix.domain_model.number_of_attributes.test_3_entities_1_empty: PASS (342.958\u00b5s)\ndata.app.mendix.domain_model.number_of_attributes.test_1_entity_40_attributes_not_allowed: PASS (1.294166ms)\ndata.app.mendix.domain_model.number_of_attributes.test_2_entity_40_attributes_1_empty_not_allowed: PASS (2.156042ms)\n--------------------------------------------------------------------------------\nPASS: 20/20\n

    The test rego files contain examples so that it's easy to validate your policy actually works for different scenarios with purpose-crafted input data. The test script will run all the test files in the policies directory and output the results.

    Example input could be inspected in the modelsource directory. The modelsource directory contains the exported Mendix model in Yaml format. The modelsource directory is created when you run the export-model command.

    "},{"location":"rules/create/#resources","title":"Resources","text":"
    • Rego language reference
    • Rego playground
    • Rego testing
    • Rego best practices
    • Style guide
    "},{"location":"rules/create/#help","title":"Help","text":"

    We understand Rego is not the easiest language to use. However, it is the perfect match due to its expressiveness. If you need help creating a new policy, please reach out to us at support@cinaq.com.

    "},{"location":"rules/001_project_settings/001_0001_anonymous_disabled/","title":"001_0001 - AnonymousDisabled","text":""},{"location":"rules/001_project_settings/001_0001_anonymous_disabled/#business-apps-must-always-require-login","title":"Business apps must always require login","text":"

    Disable anonymous/guest access in Project Security

    "},{"location":"rules/001_project_settings/001_0001_anonymous_disabled/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Security\ninput: Security$ProjectSecurity.yaml\nrulename: AnonymousDisabled\nrulenumber: '001_0001'\nscope: package\nseverity: HIGH\n
    "},{"location":"rules/001_project_settings/001_0001_anonymous_disabled/#description","title":"Description","text":"

    No anonymous means every user must have valid login session or credentials

    "},{"location":"rules/001_project_settings/001_0001_anonymous_disabled/#remediation","title":"Remediation","text":"

    Disable anonymous/guest access in Project Security

    "},{"location":"rules/001_project_settings/001_0001_anonymous_disabled/#test-cases","title":"Test cases","text":"
    package app.mendix.project_settings.anonymous_disabled\nimport rego.v1\n\n# Test cases\ntest_allow if {\n    allow with input as {\"EnableGuestAccess\": false}\n}\ntest_no_allow if {\n    not allow with input as {\"EnableGuestAccess\": true}\n}\n
    "},{"location":"rules/001_project_settings/001_0002_demo_users_disabled/","title":"001_0002 - DemoUsersDisabled","text":""},{"location":"rules/001_project_settings/001_0002_demo_users_disabled/#business-apps-should-disable-demo-users","title":"Business apps should disable demo users","text":"

    Disable demo users in Project Security

    "},{"location":"rules/001_project_settings/001_0002_demo_users_disabled/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Security\ninput: Security$ProjectSecurity.yaml\nrulename: DemoUsersDisabled\nrulenumber: '001_0002'\nscope: package\nseverity: HIGH\n
    "},{"location":"rules/001_project_settings/001_0002_demo_users_disabled/#description","title":"Description","text":"

    No demo users

    "},{"location":"rules/001_project_settings/001_0002_demo_users_disabled/#remediation","title":"Remediation","text":"

    Disable demo users in Project Security

    "},{"location":"rules/001_project_settings/001_0002_demo_users_disabled/#test-cases","title":"Test cases","text":"
    package app.mendix.project_settings.demo_users_disabled\nimport rego.v1\n\n# Test cases\ntest_allow if {\n    allow with input as {\"EnableDemoUsers\": false}\n}\ntest_no_allow if {\n    not allow with input as {\"EnableDemoUsers\": true}\n}\n
    "},{"location":"rules/001_project_settings/001_0003_security_checks/","title":"001_0003 - SecurityChecks","text":""},{"location":"rules/001_project_settings/001_0003_security_checks/#ensure-security-rules-are-active","title":"Ensure security rules are active","text":"

    Set Security check to production in Project Security

    "},{"location":"rules/001_project_settings/001_0003_security_checks/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Security\ninput: Security$ProjectSecurity.yaml\nrulename: SecurityChecks\nrulenumber: '001_0003'\nscope: package\nseverity: HIGH\n
    "},{"location":"rules/001_project_settings/001_0003_security_checks/#description","title":"Description","text":"

    Any serious app needs entity access security configured

    "},{"location":"rules/001_project_settings/001_0003_security_checks/#remediation","title":"Remediation","text":"

    Set Security check to production in Project Security

    "},{"location":"rules/001_project_settings/001_0003_security_checks/#test-cases","title":"Test cases","text":"
    package app.mendix.project_settings.security_checks\nimport rego.v1\n\n# Test cases\ntest_allow if {\n    allow with input as {\n        \"CheckSecurity\": true,\n        \"SecurityLevel\": \"CheckEverything\",\n    }\n}\ntest_no_allow_1 if {\n    not allow with input as {\n        \"CheckSecurity\": false,\n        \"SecurityLevel\": \"CheckEverything\",\n    }\n}\ntest_no_allow_2 if {\n    not allow with input as {\n        \"CheckSecurity\": true,\n        \"SecurityLevel\": \"unknown\",\n    }\n}\n
    "},{"location":"rules/001_project_settings/001_0004_strong_password/","title":"001_0004 - StrongPasswordPolicy","text":""},{"location":"rules/001_project_settings/001_0004_strong_password/#strong-password-policy","title":"Strong password policy","text":"

    Ensure minimum password length of at least 8 characters and must use all character classes.

    "},{"location":"rules/001_project_settings/001_0004_strong_password/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Security\ninput: Security$ProjectSecurity.yaml\npriority: 5\nrulename: StrongPasswordPolicy\nrulenumber: '001_0004'\nscope: package\nseverity: HIGH\n
    "},{"location":"rules/001_project_settings/001_0004_strong_password/#description","title":"Description","text":"

    Bruteforce is quite common. Ensure passwords are very strong.

    "},{"location":"rules/001_project_settings/001_0004_strong_password/#remediation","title":"Remediation","text":"

    Ensure minimum password length of at least 8 characters and must use all character classes.

    "},{"location":"rules/001_project_settings/001_0004_strong_password/#test-cases","title":"Test cases","text":"
    package app.mendix.project_settings.strong_password\nimport rego.v1\n\n# Test cases\ntest_allow if {\n    allow with input as {\n        \"PasswordPolicySettings\": {\n            \"MinimumLength\": 9,\n            \"RequireDigit\": true,\n            \"RequireSymbol\": true,\n            \"RequireMixedCase\": true,\n        }\n    }\n}\n\ntest_no_allow_password_length if {\n    not allow with input as {\n        \"PasswordPolicySettings\": {\n            \"MinimumLength\": 3,\n            \"RequireDigit\": true,\n            \"RequireSymbol\": true,\n            \"RequireMixedCase\": true,\n        }\n    }\n}\n\ntest_no_allow_simple if {\n    not allow with input as {\n        \"PasswordPolicySettings\": {\n            \"MinimumLength\": 3,\n            \"RequireDigit\": false,\n            \"RequireSymbol\": true,\n            \"RequireMixedCase\": false,\n        }\n    }\n}\n
    "},{"location":"rules/001_project_settings/001_0005_mxadmin_userid/","title":"001_0005 - MxAdminNotUsed","text":""},{"location":"rules/001_project_settings/001_0005_mxadmin_userid/#mendix-admin-userid-not-allowed-to-be-mxadmin","title":"Mendix Admin userid not allowed to be MxAdmin","text":"

    Rename the mendix admin userid

    "},{"location":"rules/001_project_settings/001_0005_mxadmin_userid/#metadata","title":"Metadata","text":"
    authors:\n- Andre Luijkx\ncategory: Security\ninput: Security$ProjectSecurity.yaml\nrulename: MxAdminNotUsed\nrulenumber: '001_0005'\nscope: package\nseverity: HIGH\n
    "},{"location":"rules/001_project_settings/001_0005_mxadmin_userid/#description","title":"Description","text":"

    Check the mendix admin userid and should not be default value (MxAdmin)

    "},{"location":"rules/001_project_settings/001_0005_mxadmin_userid/#remediation","title":"Remediation","text":"

    Rename the mendix admin userid

    "},{"location":"rules/001_project_settings/001_0005_mxadmin_userid/#test-cases","title":"Test cases","text":"
    package app.mendix.project_settings.mxadmin_userid\nimport rego.v1\n\n# Test cases\ntest_allow if {\n    allow with input as {\"AdminUserName\": \"SystemAdmin\"}\n}\ntest_no_allow if {\n    not allow with input as {\"AdminUserName\": \"MxAdmin\"}\n}\n
    "},{"location":"rules/002_domain_model/002_0001_number_of_persistent_entities/","title":"002_0001 - NumberOfPersistantEntities","text":""},{"location":"rules/002_domain_model/002_0001_number_of_persistent_entities/#no-more-than-15-persistent-entities-within-one-domain-model","title":"No more than 15 persistent entities within one domain model","text":"

    Split domain model into multiple modules.

    "},{"location":"rules/002_domain_model/002_0001_number_of_persistent_entities/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\n- Andre Luijkx\ncategory: Maintainability\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: NumberOfPersistantEntities\nrulenumber: '002_0001'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/002_domain_model/002_0001_number_of_persistent_entities/#description","title":"Description","text":"

    The bigger the domain models, the harder they will be to maintain. It adds complexity to your security model as well. The smaller the modules, the easier to reuse.

    "},{"location":"rules/002_domain_model/002_0001_number_of_persistent_entities/#remediation","title":"Remediation","text":"

    Split domain model into multiple modules.

    "},{"location":"rules/002_domain_model/002_0001_number_of_persistent_entities/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.number_of_persistent_entities\nimport rego.v1\n\n\n# Test data\nentity_attr_0 = {\n    \"Name\": \"Entity1\",\n}\n\nnpe_entity_attr_0 = {\n    \"Name\": \"Entity1\",\n    \"MaybeGeneralization\": {\n        \"Persistable\": false,\n    }\n}\n\n\ntwenty := numbers.range(1, 20)\nentities_20 = [\n    { \"Name\": entity_attr_0.Name }  | n := twenty[_]\n]\n\nnpe_entities_20 = [\n    npe_entity_attr_0 | n := twenty[_]\n]\n\n# Test cases\ntest_no_entities if {\n    allow with input as {\"Entities\": null}\n}\n\ntest_1_entity if {\n    allow with input as {\"Entities\": [entity_attr_0]}\n}\n\ntest_2_entities if {\n    allow with input as {\"Entities\": [entity_attr_0, entity_attr_0]}\n}\n\ntest_2_entities_npe if {\n    allow with input as {\"Entities\": [entity_attr_0, npe_entity_attr_0]}\n}\n\ntest_20_entities if {\n    not allow with input as {\"Entities\": entities_20}\n}\n\ntest_20_npe_entities if {\n    allow with input as {\"Entities\": npe_entities_20 }\n}\n
    "},{"location":"rules/002_domain_model/002_0002_number_of_attributes/","title":"002_0002 - NumberOfAttributes","text":""},{"location":"rules/002_domain_model/002_0002_number_of_attributes/#no-more-that-35-attributes-in-an-entity","title":"No more that 35 attributes in an entity","text":"

    Normalize your datamodel. Split your object into multiple objects. If the attributes really belong to each other in a one-to-one relation, just draw a one-to-one relation between the objects.

    "},{"location":"rules/002_domain_model/002_0002_number_of_attributes/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Maintainability\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: NumberOfAttributes\nrulenumber: '002_0002'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/002_domain_model/002_0002_number_of_attributes/#description","title":"Description","text":"

    The bigger the entities, the slower your application will become when handling the data. This is because Mendix is using SELECT * queries a lot and will retrieve a lot of unnecessary data.

    "},{"location":"rules/002_domain_model/002_0002_number_of_attributes/#remediation","title":"Remediation","text":"

    Normalize your datamodel. Split your object into multiple objects. If the attributes really belong to each other in a one-to-one relation, just draw a one-to-one relation between the objects.

    "},{"location":"rules/002_domain_model/002_0002_number_of_attributes/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.number_of_attributes\nimport rego.v1\n\n\n# Test data\nattribute1 = {\n    \"Name\": \"Attribute1\"\n}\n\nentity_attr_0 = {\n    \"Name\": \"Entity1\",\n    \"Attributes\": null,\n}\n\nentity_attr_1 = {\n    \"Name\": \"Entity1\",\n    \"Attributes\": [\n        attribute1\n    ]\n}\n\nforty := numbers.range(1, 40)\nattributes_40 = [ \n    { \"Name\": attribute1.Name }  | n := forty[_]\n]\n\nentity_1_attr_40 = {\n    \"Name\": \"Entity1\",\n    \"Attributes\": attributes_40,\n}\n\n\n# Test cases\ntest_no_entities if {\n    allow with input as {\"Entities\": null}\n}\n\ntest_1_entity_1_attribute if {\n    allow with input as {\"Entities\": [entity_attr_1]}\n}\n\ntest_2_entities if {\n    allow with input as {\"Entities\": [entity_attr_1, entity_attr_1]}\n}\n\ntest_3_entities_1_empty if {\n    allow with input as {\"Entities\": [entity_attr_1, entity_attr_1, entity_attr_0]}\n}\n\ntest_1_entity_40_attributes_not_allowed if {\n    not allow with input as {\"Entities\": [entity_1_attr_40]}\n}\n\ntest_2_entity_40_attributes_1_empty_not_allowed if {\n    not allow with input as {\"Entities\": [entity_1_attr_40, entity_1_attr_40, entity_attr_0]}\n}\n
    "},{"location":"rules/002_domain_model/002_0003_inherit_from_administration_account/","title":"002_0003 - AvioidInheritanceFromAdministrationAccount","text":""},{"location":"rules/002_domain_model/002_0003_inherit_from_administration_account/#inherit-from-administrationaccount","title":"Inherit from Administration.Account","text":"

    Inherit from system.user instead or adapt Administration.Account so it fits your needs.

    "},{"location":"rules/002_domain_model/002_0003_inherit_from_administration_account/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Performance\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: AvioidInheritanceFromAdministrationAccount\nrulenumber: '002_0003'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/002_domain_model/002_0003_inherit_from_administration_account/#description","title":"Description","text":"

    There is no need to inherit from administration.account. Administration.account may simply be extended, this is not a system module. Avoid unnecessary inheritance as this has a negative effect on performance.

    "},{"location":"rules/002_domain_model/002_0003_inherit_from_administration_account/#remediation","title":"Remediation","text":"

    Inherit from system.user instead or adapt Administration.Account so it fits your needs.

    "},{"location":"rules/002_domain_model/002_0003_inherit_from_administration_account/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.inherit_from_administration_account\nimport rego.v1\n\n\n# Test data\nentity_negative = {\n    \"Name\": \"Entity1\",\n    \"MaybeGeneralization\": {\n        \"Type\": \"DomainModels$Generalization\",\n        \"Generalization\": \"System.FileDocument\"\n    }\n}\n\nentity_positive = {\n    \"Name\": \"Entity2\",\n    \"MaybeGeneralization\": {\n        \"Type\": \"DomainModels$Generalization\",\n        \"Generalization\": \"Administration.Account\"\n    }\n}\n\n\nentities_mixed = [entity_negative, entity_positive]\n\n\n# Test cases\ntest_no_entities if {\n    allow with input as {\"Entities\": null}\n}\n\ntest_entity_negative if {\n    allow with input as {\"Entities\": [entity_negative]}\n}\n\ntest_entity_positive if {\n    not allow with input as {\"Entities\": [entity_positive]}\n}\n\ntest_entities_mixed if {\n    not allow with input as {\"Entities\": entities_mixed}\n}\n
    "},{"location":"rules/002_domain_model/002_0004_inherit_from_non_system/","title":"002_0004 - AvoidInheritanceFromNonSystem","text":""},{"location":"rules/002_domain_model/002_0004_inherit_from_non_system/#inherit-from-non-system-module-is-discouraged","title":"Inherit from non System module is discouraged","text":"

    Instead of inheritance, just use separate objects which are associated to the main object. As an alternative, you can add the child\u2019s attributes to the super entity and add an ObjectType enumeration.

    "},{"location":"rules/002_domain_model/002_0004_inherit_from_non_system/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Performance\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: AvoidInheritanceFromNonSystem\nrulenumber: '002_0004'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/002_domain_model/002_0004_inherit_from_non_system/#description","title":"Description","text":"

    Inheritance, except from system module, is strongly discouraged because of the negative performance side effects.

    "},{"location":"rules/002_domain_model/002_0004_inherit_from_non_system/#remediation","title":"Remediation","text":"

    Instead of inheritance, just use separate objects which are associated to the main object. As an alternative, you can add the child\u2019s attributes to the super entity and add an ObjectType enumeration.

    "},{"location":"rules/002_domain_model/002_0004_inherit_from_non_system/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.inherit_from_non_system\nimport rego.v1\n\n\n# Test data\nentity_negative = {\n    \"Name\": \"Entity1\",\n    \"MaybeGeneralization\": {\n        \"Type\": \"DomainModels$Generalization\",\n        \"Generalization\": \"System.FileDocument\"\n    }\n}\n\nentity_positive = {\n    \"Name\": \"Entity2\",\n    \"MaybeGeneralization\": {\n        \"Type\": \"DomainModels$Generalization\",\n        \"Generalization\": \"Administration.Account\"\n    }\n}\n\n\nentities_mixed = [entity_negative, entity_positive]\n\n\n# Test cases\ntest_no_entities if {\n    allow with input as {\"Entities\": null}\n}\n\ntest_entity_negative if {\n    allow with input as {\"Entities\": [entity_negative]}\n}\n\ntest_entity_positive if {\n    not allow with input as {\"Entities\": [entity_positive]}\n}\n\ntest_entities_mixed if {\n    not allow with input as {\"Entities\": entities_mixed}\n}\n
    "},{"location":"rules/002_domain_model/002_0005_avoid_system_entity_association/","title":"002_0005 - AvoidSystemEntityAssociation","text":""},{"location":"rules/002_domain_model/002_0005_avoid_system_entity_association/#avoid-using-system-storage-objects-directly","title":"Avoid using system storage objects directly","text":"

    Remove direct associations with the System Domain Model. Use inheritance instead (i.e. Generalization in the entity properties).

    "},{"location":"rules/002_domain_model/002_0005_avoid_system_entity_association/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Security\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: AvoidSystemEntityAssociation\nrulenumber: '002_0005'\nscope: package\nseverity: HIGH\n
    "},{"location":"rules/002_domain_model/002_0005_avoid_system_entity_association/#description","title":"Description","text":"

    Always inherit for filedocuments and images. Never implement direct assocations to the System Domain Model, because of limits on the configuration of security.

    "},{"location":"rules/002_domain_model/002_0005_avoid_system_entity_association/#remediation","title":"Remediation","text":"

    Remove direct associations with the System Domain Model. Use inheritance instead (i.e. Generalization in the entity properties).

    "},{"location":"rules/002_domain_model/002_0005_avoid_system_entity_association/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.avoid_system_entity_association\nimport rego.v1\n\n\n# Test data\nnegative = {\n    \"Name\": \"HELLO_THERE1\",\n    \"Child\": \"SomeModule.FileDocument\",\n}\n\npositive = {\n    \"Name\": \"HELLO_THERE2\",\n    \"Child\": \"System.FileDocument\",\n}\n\n\n# Test cases\n\ntest_no_cross_associations if {\n    allow with input as {\"CrossAssociations\": null}\n}\n\ntest_negative if {\n    allow with input as {\"CrossAssociations\": [negative]}\n}\n\ntest_positive if {\n    not allow with input as {\"CrossAssociations\": [positive]}\n}\n\ntest_mixed if {\n    not allow with input as {\"CrossAssociations\": [negative, positive]}\n}\n
    "},{"location":"rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/","title":"002_0006 - AvoidTooManyVirtualAttributes","text":""},{"location":"rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/#too-many-microflow-attributes-virtual-attributes-inside-of-an-entity","title":"Too many Microflow attributes (virtual attributes) inside of an entity","text":"

    Optimize the number of virtual attributes inside of an entity. Reduce to 10 or less.

    "},{"location":"rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Performance\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: AvoidTooManyVirtualAttributes\nrulenumber: '002_0006'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/#description","title":"Description","text":"

    Too many Microflow attributes (virtual attributes) inside of an entity will cause performance issues.

    "},{"location":"rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/#remediation","title":"Remediation","text":"

    Optimize the number of virtual attributes inside of an entity. Reduce to 10 or less.

    "},{"location":"rules/002_domain_model/002_0006_avoid_too_many_virtual_attributes/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.avoid_too_many_virtual_attributes\n\nimport rego.v1\n\n\n# Test data\n\n\nattr_0 := {\n          \"$Type\": \"DomainModels$Attribute\",\n          \"Name\": \"VA_age\",\n          \"Value\": {\n            \"$Type\": \"DomainModels$CalculatedValue\"\n          }\n}\n\n\ntwenty := numbers.range(1, 20)\nattr_20 = [ \n    { \"Name\": attr_0.Name, \"Value\": attr_0.Value }  | n := twenty[_]\n]\n\npositive := {\n  \"Entities\": [\n    {\n      \"$Type\": \"DomainModels$EntityImpl\",\n      \"Attributes\": [\n        {\n          \"$Type\": \"DomainModels$Attribute\",\n          \"Name\": \"VA_age\",\n          \"Value\": {\n            \"$Type\": \"DomainModels$CalculatedValue\"\n          }\n        },\n        {\n          \"$Type\": \"DomainModels$Attribute\",\n          \"Name\": \"Year\",\n          \"Value\": {\n            \"$Type\": \"DomainModels$StoredValue\"\n          }\n        }\n      ],\n      \"Name\": \"Bike\"\n    }\n  ]\n}\n\nnegative := {\n  \"Entities\": [\n    {\n      \"$Type\": \"DomainModels$EntityImpl\",\n      \"Attributes\": attr_20,\n      \"Name\": \"Bike\"\n    }\n  ]\n}\n\n# Test cases\n\ntest_positive if {\n    allow with input as positive\n}\n\ntest_negative if {\n    not allow with input as negative\n}\n
    "},{"location":"rules/002_domain_model/002_0007_avoid_using_validation_rules/","title":"002_0007 - AvoidUsingValidationRules","text":""},{"location":"rules/002_domain_model/002_0007_avoid_using_validation_rules/#avoid-using-validation-rules-for-domain-model","title":"Avoid using validation rules for domain model.","text":"

    Remove datamodel validation rules.

    "},{"location":"rules/002_domain_model/002_0007_avoid_using_validation_rules/#metadata","title":"Metadata","text":"
    authors:\n- Viktor Berlov <viktor@cinaq.com>\ncategory: Maintainability\ninput: '*/DomainModels$DomainModel.yaml'\nrulename: AvoidUsingValidationRules\nrulenumber: '002_0007'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/002_domain_model/002_0007_avoid_using_validation_rules/#description","title":"Description","text":"

    Validation rules on domain model level will give the users unexpected errors.

    "},{"location":"rules/002_domain_model/002_0007_avoid_using_validation_rules/#remediation","title":"Remediation","text":"

    Remove datamodel validation rules.

    "},{"location":"rules/002_domain_model/002_0007_avoid_using_validation_rules/#test-cases","title":"Test cases","text":"
    package app.mendix.domain_model.avoid_using_validation_rules\n\nimport rego.v1\n\n\n# Test data\n\npositive := {\n  \"Entities\": [\n    {\n      \"ValidationRules\": [],\n      \"Name\": \"Bike\"\n    }\n  ]\n}\n\nnegative := {\n  \"Entities\": [\n    {\n      \"ValidationRules\": [\n        {\n          \"$Type\": \"DomainModels$ValidationRule\",\n          \"Attribute\": \"MyFirstModule.Bike.Name\",\n          \"Message\": {\n            \"$Type\": \"Texts$Text\",\n            \"Items\": [\n              {\n                \"$Type\": \"Texts$Translation\",\n                \"LanguageCode\": \"en_US\",\n                \"Text\": \"Not a good name\"\n              }\n            ]\n          },\n          \"RuleInfo\": {\n            \"$Type\": \"DomainModels$EqualsToRuleInfo\",\n            \"EqualsToAttribute\": \"\",\n            \"UseValue\": true,\n            \"Value\": \"admin\"\n          }\n        }\n      ],\n      \"Name\": \"Bike\"\n    }\n  ]\n}\n\n# Test cases\n\ntest_positive if {\n    allow with input as positive\n}\n\ntest_negative if {\n    not allow with input as negative\n}\n
    "},{"location":"rules/003_modules/003_0001_number_of_modules/","title":"003_0001 - NumberOfModules","text":""},{"location":"rules/003_modules/003_0001_number_of_modules/#more-than-20-modules-in-project","title":"More than 20 modules in project","text":"

    Consider a multi-app stategy to avoid creating one big (unmaintainable) monstrous application.

    "},{"location":"rules/003_modules/003_0001_number_of_modules/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Maintainability\ninput: Metadata.yaml\nrulename: NumberOfModules\nrulenumber: '003_0001'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/003_modules/003_0001_number_of_modules/#description","title":"Description","text":"

    The bigger the application, the harder to maintain.

    "},{"location":"rules/003_modules/003_0001_number_of_modules/#remediation","title":"Remediation","text":"

    Consider a multi-app stategy to avoid creating one big (unmaintainable) monstrous application.

    "},{"location":"rules/003_modules/003_0001_number_of_modules/#test-cases","title":"Test cases","text":"
    package app.mendix.modules.number_of_modules\nimport rego.v1\n\n\n# Test data\nmodule = {\n    \"Name\": \"Module\",\n    \"Attributes\": {\n        \"FromAppStore\": false,\n    }\n}\n\n\nthirty := numbers.range(1, 30)\nmodules_30 = [ \n    module | n := thirty[_]\n]\n\n\n# Test cases\ntest_empty if {\n    allow with input as {\"Modules\": null}\n}\n\ntest_1_module if {\n    allow with input as {\"Modules\": [module]}\n}\n\ntest_2_modules if {\n    allow with input as {\"Modules\": [module, module]}\n}\n\ntest_30_modules if {\n    not allow with input as {\"Modules\": modules_30}\n}\n
    "},{"location":"rules/004_pages/004_0001_inline_style_property_used/","title":"004_0001 - InlineStylePropertyUsed","text":""},{"location":"rules/004_pages/004_0001_inline_style_property_used/#inline-style-property-used","title":"Inline style property used","text":"

    Use generic classes instead, defined by the theme.

    "},{"location":"rules/004_pages/004_0001_inline_style_property_used/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Maintainability\ninput: '**/*$Page.yaml'\nrulename: InlineStylePropertyUsed\nrulenumber: '004_0001'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/004_pages/004_0001_inline_style_property_used/#description","title":"Description","text":"

    Avoid using the style property, because this will make the life of your UI designer a lot more complicated. It will be harder to overrule styles from CSS file level.

    "},{"location":"rules/004_pages/004_0001_inline_style_property_used/#remediation","title":"Remediation","text":"

    Use generic classes instead, defined by the theme.

    "},{"location":"rules/004_pages/004_0001_inline_style_property_used/#test-cases","title":"Test cases","text":"
    package app.mendix.pages.inline_style_property_used\nimport rego.v1\n\n\n# Test data\nform_simple = {\n    \"$Type\": \"Forms$Page\",\n    \"Name\": \"Page1\",\n    \"Appearance\": {\n        \"$Type\": \"Forms$Appearance\",\n        \"Class\": \"\",\n        \"DesignProperties\": null,\n        \"DynamicClasses\": \"\",\n        \"Style\": \"\",\n    },\n}\nform_simple_negative = {\n    \"$Type\": \"Forms$Page\",\n    \"Name\": \"Page1\",\n    \"Appearance\": {\n        \"$Type\": \"Forms$Appearance\",\n        \"Class\": \"\",\n        \"DesignProperties\": null,\n        \"DynamicClasses\": \"\",\n        \"Style\": \"color: red;\",\n    },\n}\n\nform_nested = {\n    \"Name\": \"Page1\",\n    \"FormCall\": {\n        \"Arguments\": [\n            {\n                \"Widgets\": [\n                    {\n                        \"$Type\": \"Forms$LayoutGrid\",\n                        \"Name\": \"layoutGrid2\",\n                        \"Rows\": [\n                            {\n                                \"$Type\": \"Forms$LayoutGridRow\",\n                                \"Columns\": [\n                                    {\n                                        \"$Type\": \"Forms$LayoutGridColumn\",\n                                        \"Appearance\": {\n                                            \"$Type\": \"Forms$Appearance\",\n                                            \"Class\": \"\",\n                                            \"DesignProperties\": null,\n                                            \"DynamicClasses\": \"\",\n                                            \"Style\": \"\",\n                                        }\n                                    },\n                                ],\n                            },\n                        ],\n                    },\n                ],\n            },\n        ],\n    },\n}\n\nform_nested_negative = {\n    \"Name\": \"Page1\",\n    \"FormCall\": {\n        \"Arguments\": [\n            {\n                \"Widgets\": [\n                    {\n                        \"$Type\": \"Forms$LayoutGrid\",\n                        \"Name\": \"layoutGrid2\",\n                        \"Rows\": [\n                            {\n                                \"$Type\": \"Forms$LayoutGridRow\",\n                                \"Columns\": [\n                                    {\n                                        \"$Type\": \"Forms$LayoutGridColumn\",\n                                        \"Appearance\": {\n                                            \"$Type\": \"Forms$Appearance\",\n                                            \"Class\": \"\",\n                                            \"DesignProperties\": null,\n                                            \"DynamicClasses\": \"\",\n                                            \"Style\": \"color: orange;\",\n                                        }\n                                    },\n                                ],\n                            },\n                        ],\n                    },\n                ],\n            },\n        ],\n    },\n}\n\n\n\n# Test cases\ntest_simple if {\n    allow with input as form_simple\n}\n\ntest_simple_negative if {\n    not allow with input as form_simple_negative\n}\n\ntest_nested if {\n    allow with input as form_nested\n}\n\ntest_nested_negative if {\n    not allow with input as form_nested_negative\n}\n
    "},{"location":"rules/005_microflows/005_0001_empty_string_check_not_complete/","title":"005_0001 - EmptyStringCheckNotComplete","text":""},{"location":"rules/005_microflows/005_0001_empty_string_check_not_complete/#empty-string-check-not-complete","title":"Empty String check not complete","text":"

    Always check a string for empty based on != empty and != \"\". The first one equals database NULL value, the latter one indicates a truncated string.

    "},{"location":"rules/005_microflows/005_0001_empty_string_check_not_complete/#metadata","title":"Metadata","text":"
    authors:\n- Xiwen Cheng <x@cinaq.com>\ncategory: Error\ninput: '**/*$Microflow.yaml'\nrulename: EmptyStringCheckNotComplete\nrulenumber: '005_0001'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/005_microflows/005_0001_empty_string_check_not_complete/#description","title":"Description","text":"

    Technically, there is a difference between empty and \"\". Make sure to check them both.

    "},{"location":"rules/005_microflows/005_0001_empty_string_check_not_complete/#remediation","title":"Remediation","text":"

    Always check a string for empty based on != empty and != \"\". The first one equals database NULL value, the latter one indicates a truncated string.

    "},{"location":"rules/005_microflows/005_0001_empty_string_check_not_complete/#test-cases","title":"Test cases","text":"
    package app.mendix.microflows.empty_string_check_not_complete\nimport rego.v1\n\n\n# Test data\nmicroflow_good = {\n    \"$Type\": \"Microflow$Page\",\n    \"Name\": \"mf1\",\n    \"ObjectCollection\": {\n        \"$Type\": \"Microflows$MicroflowObjectCollection\",\n        \"Objects\": [\n            {\n                \"$Type\": \"Microflows$ExclusiveSplit\",\n                \"SplitCondition\": {\n                    \"$Type\": \"Microflows$ExpressionSplitCondition\",\n                    \"Expression\": \"$Variable != empty and $Variable != ''\",\n                },\n            },\n        ],\n    },\n}\n\nmicroflow_bad = {\n    \"$Type\": \"Microflow$Page\",\n    \"Name\": \"mf1\",\n    \"ObjectCollection\": {\n        \"$Type\": \"Microflows$MicroflowObjectCollection\",\n        \"Objects\": [\n            {\n                \"$Type\": \"Microflows$ExclusiveSplit\",\n                \"SplitCondition\": {\n                    \"$Type\": \"Microflows$ExpressionSplitCondition\",\n                    \"Expression\": \"$Variable != ''\",\n                },\n            },\n        ],\n    },\n}\n\n# Test cases\ntest_simple if {\n    allow with input as microflow_good\n}\n\ntest_simple_negative if {\n    not allow with input as microflow_bad\n}\n
    "},{"location":"rules/005_microflows/005_0002_commit_actions_with_a_loop/","title":"005_0002 - AvoidCommitInLoop","text":""},{"location":"rules/005_microflows/005_0002_commit_actions_with_a_loop/#commit-actions-with-a-loop","title":"Commit actions with a loop","text":"

    Consider committing objects outside the loop. Within the loop, add them to a list.

    "},{"location":"rules/005_microflows/005_0002_commit_actions_with_a_loop/#metadata","title":"Metadata","text":"
    authors:\n- Viktor Berlov <viktor@cinaq.com>\ncategory: Microflows\ninput: '**/*$Microflow.yaml'\nrulename: AvoidCommitInLoop\nrulenumber: '005_0002'\nscope: package\nseverity: MEDIUM\n
    "},{"location":"rules/005_microflows/005_0002_commit_actions_with_a_loop/#description","title":"Description","text":"

    Commiting objects within a loop will fire a SQL Update query for each iteration.

    "},{"location":"rules/005_microflows/005_0002_commit_actions_with_a_loop/#remediation","title":"Remediation","text":"

    Consider committing objects outside the loop. Within the loop, add them to a list.

    "},{"location":"rules/005_microflows/005_0002_commit_actions_with_a_loop/#test-cases","title":"Test cases","text":"
    package app.mendix.microflows.commit_actions_with_a_loop_test\n\nimport data.app.mendix.microflows.commit_actions_with_a_loop\nimport rego.v1\n\n# Test data\nloop_commit_good_empty_objects := {\n    \"$Type\": \"Microflow$Microflow\",\n    \"Name\": \"MicroflowForLoop\",\n    \"MainFunction\": [{\"Attributes\": {\n        \"$Type\": \"Microflows$LoopedActivity\",\n        \"ObjectCollection\": {\n            \"$Type\": \"Microflows$MicroflowObjectCollection\",\n            \"Objects\": null,\n        },\n    }}],\n}\n\nloop_commit_good_objects := {\n    \"$Type\": \"Microflow$Microflow\",\n    \"Name\": \"MicroflowForLoop\",\n    \"MainFunction\": [{\"Attributes\": {\n        \"$Type\": \"Microflows$LoopedActivity\",\n        \"ObjectCollection\": {\n            \"$Type\": \"Microflows$MicroflowObjectCollection\",\n            \"Objects\": [{\n                \"$Type\": \"Microflows$ActionActivity\",\n                \"Action\": {\n                    \"$Type\": \"Microflows$ChangeAction\",\n                    \"Commit\": \"No\",\n                },\n            }],\n        },\n    }}],\n}\n\nloop_commit_bad_commit_action := {\n    \"$Type\": \"Microflow$Microflow\",\n    \"Name\": \"MicroflowForLoop\",\n    \"MainFunction\": [{\"Attributes\": {\n        \"$Type\": \"Microflows$LoopedActivity\",\n        \"ObjectCollection\": {\n            \"$Type\": \"Microflows$MicroflowObjectCollection\",\n            \"Objects\": [{\n                \"$Type\": \"Microflows$ActionActivity\",\n                \"Action\": {\"$Type\": \"Microflows$CommitAction\"},\n            }],\n        },\n    }}],\n}\n\nloop_commit_bad_change_action := {\n    \"$Type\": \"Microflow$Microflow\",\n    \"Name\": \"MicroflowForLoop\",\n    \"MainFunction\": [{\"Attributes\": {\n        \"$Type\": \"Microflows$LoopedActivity\",\n        \"ObjectCollection\": {\n            \"$Type\": \"Microflows$MicroflowObjectCollection\",\n            \"Objects\": [{\n                \"$Type\": \"Microflows$ActionActivity\",\n                \"Action\": {\n                    \"$Type\": \"Microflows$ChangeAction\",\n                    \"Commit\": \"Yes\",\n                },\n            }],\n        },\n    }}],\n}\n\nloop_commit_bad_all := {\n    \"$Type\": \"Microflow$Microflow\",\n    \"Name\": \"MicroflowForLoop\",\n    \"MainFunction\": [{\"Attributes\": {\n        \"$Type\": \"Microflows$LoopedActivity\",\n        \"ObjectCollection\": {\n            \"$Type\": \"Microflows$MicroflowObjectCollection\",\n            \"Objects\": [\n                {\n                    \"$Type\": \"Microflows$ActionActivity\",\n                    \"Action\": {\"$Type\": \"Microflows$CommitAction\"},\n                },\n                {\n                    \"$Type\": \"Microflows$ActionActivity\",\n                    \"Action\": {\n                        \"$Type\": \"Microflows$ChangeAction\",\n                        \"Commit\": \"Yes\",\n                    },\n                },\n            ],\n        },\n    }}],\n}\n\n# Test cases\ntest_loop_commit_good if {\n    commit_actions_with_a_loop.allow with input as loop_commit_good_empty_objects\n    commit_actions_with_a_loop.allow with input as loop_commit_good_objects\n}\n\ntest_loop_commit_bad if {\n    not commit_actions_with_a_loop.allow with input as loop_commit_bad_commit_action\n    not commit_actions_with_a_loop.allow with input as loop_commit_bad_change_action\n    not commit_actions_with_a_loop.allow with input as loop_commit_bad_all\n}\n
    "}]} \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index da74375..cb26231 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ diff --git a/terminologies/index.html b/terminologies/index.html index 13537f0..63f2a34 100644 --- a/terminologies/index.html +++ b/terminologies/index.html @@ -9,7 +9,7 @@ - + @@ -925,6 +925,27 @@ + + + + + + +
  • + + + + + 001_0005 - MxAdminNotUsed + + + + +
  • + + + + @@ -977,11 +998,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1315,6 +1336,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1334,6 +1376,92 @@ + + + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + @@ -1584,7 +1712,7 @@

    Policy

    @@ -1062,11 +1083,11 @@
  • - + - 002_0001 - NumberOfEntities + 002_0001 - NumberOfPersistantEntities @@ -1400,6 +1421,27 @@ + + + + + + +
  • + + + + + 005_0002 - AvoidCommitInLoop + + + + +
  • + + + + @@ -1421,6 +1463,92 @@ + + + + + +
  • + + + + + + + + + + + +
  • + + + + + + + + +