A Mendix Introduction au SDK — Partie 2
Il s'agit du deuxième article de blog d'une courte série illustrant comment le Mendix Les SDK de plate-forme/modèle peuvent vous donner un accès programmatique au modèle de votre application sans utiliser Mendix Studio ou Mendix Studio Pro.
Créer des applications et construire le modèle
Dans l' premier article de blog sur le SDK J'ai montré comment vous pouvez utiliser un script TypeScript et NodeJS pour accéder à un Mendix modèle d'application et exporter les informations du modèle dans un script JavaScript. Maintenant, je vais créer un nouveau Mendix application utilisant un script SDK de plate-forme/modèle, créez un nouveau module dans l'application et placez certains éléments dans ce module.

Une nouvelle application
Pour un blog, le cas d'utilisation doit être assez simple, je vais donc créer une application qui imite un scénario de saisie de commande très simpliste. L'application comportera simplement les éléments suivants :
- Une entité de commande avec un numéro automatique pour l'ID de commande, une valeur de commande, un statut de commande et un nom de client, qui peut également contenir un document (un PDF peut-être) de la commande reçue.
- Une entité de ligne de commande avec un ID de ligne de commande, un nom de produit et une valeur de ligne.
- Une association avec suppression en cascade entre les entités de commande et de ligne de commande.
- Une entité non persistante qui peut être utilisée pour rechercher des commandes dont la valeur est comprise entre les valeurs maximales et minimales saisies par l'utilisateur.
- Une énumération représentant l'état de la commande.
- Sécurité de niveau production configurée avec un rôle d'administrateur et un rôle d'utilisateur normal. J'ai rendu les entités persistantes accessibles en écriture par l'administrateur mais en lecture seule pour les utilisateurs normaux, juste pour illustrer comment cela se fait.
Le script construira tout cela à partir de zéro et permettra également de contrôler le Mendix version et modèle d'application de démarrage qui sont utilisés comme base pour l'application créée.
Je pense que nous pourrons développer cette application dans les prochains articles de blog.
Donc pour commencer : toutes les activités se déroulent dans une fonction asynchrone appelée main(). Cela nous permet d'utiliser des modificateurs d'attente pour simplifier le code.
Créer l'application
C'est simple et nous avons juste besoin d'un nom pour l'application et d'un résumé facultatif (description) et d'un identifiant de modèle. Si vous ne spécifiez pas l'ID de modèle, le modèle par défaut pour la dernière version disponible Mendix version sera utilisée.
Je vais utiliser le modèle d'application Web vierge pour Mendix 9.18.1.
Vous pouvez trouver l'ID de modèle pour le modèle que vous souhaitez utiliser pour l'application en recherchant sur le Mendix Marketplace et sélection de l'option « Modèles » :

Cliquez sur le modèle souhaité, puis cliquez sur l'onglet « Versions » dans la page du modèle.

Si vous ouvrez les versions individuelles, vous trouverez des détails relatifs à chacune d'elles, y compris la version du framework qui correspond à la «Mendix version » à laquelle il est destiné. Pas tous Mendix les versions sont représentées. Si vous devez utiliser une version qui n'est pas affichée, choisissez la dernière version du framework antérieure à la version souhaitée, puis mettez à niveau l'application dans Studio Pro une fois l'application créée.

Copiez l'UUID fourni pour la version que vous souhaitez utiliser : il s'agit de l'ID de modèle qui peut être inséré dans l'instruction createNewApp.
import { MendixPlatformClient } from "mendixplatformsdk";
import { projects, security, domainmodels, enumerations, texts } from 'mendixmodelsdk';
main().catch(console.error);
async function main()
{
const client = new MendixPlatformClient();
// Create the app
const app = await client.createNewApp("SDKTestApp", {summary: "Test App created using the Mendix SDKs", templateId: "5cdf7b82-e902-4b8d-b0dd-bdd939c9e82f"});
console.log("App created with App ID: " + app.appId);
Ensuite, nous créons une copie de travail pour l’application et ouvrons le modèle afin de pouvoir le mettre à jour.
// Open a working copy
const workingCopy = await app.createTemporaryWorkingCopy("trunk");
const model = await workingCopy.openModel();
Configurer le module
Ensuite, nous créons le nouveau module d'application et lui donnons le nom que nous avons choisi, puis initialisons la sécurité du module. Dans cette application, nous allons travailler avec deux rôles, un pour l'accès administrateur et un pour les utilisateurs réguliers. La façon dont j'ai configuré cela n'est pas très réaliste, car j'essaie simplement de montrer comment configurer la sécurité dans le SDK : les administrateurs auront un accès en écriture aux entités, tandis que les utilisateurs réguliers n'auront qu'un accès en lecture (à une exception près).
// Create a new module
const module = projects.Module.createIn(model.allProjects()[0]);
module.name = "SDKModule";
// Set up module security
const moduleSecurity = security.ModuleSecurity.createIn(module);
const adminRole = security.ModuleRole.createIn(moduleSecurity);
adminRole.name = "Admin";
const userRole = security.ModuleRole.createIn(moduleSecurity);
userRole.name = "User";
Enfin, nous devons créer le modèle de domaine pour le module et nous sommes prêts à commencer à le remplir.
S // Create the module domain model
const domainModel = domainmodels.DomainModel.createIn(module);
Créer une énumération
Le « Statut de la commande » sur l'entité de commande doit être une énumération, créons-la donc d'abord et donnons-lui quatre valeurs possibles : NOUVEAU, En attente, En cours et TerminéePour simplifier les choses, nous fournissons simplement des légendes en anglais américain pour chaque valeur.
// Make the Order Status enumeration with its values
const statusEnumeration = enumerations.Enumeration.createIn(module);
statusEnumeration.name = "ENUM_Status";
const statusEnumerationValue1 = enumerations.EnumerationValue.createIn(statusEnumeration);
statusEnumerationValue1.name = "_New";
const statusEnumerationCaption1 = texts.Text.createInEnumerationValueUnderCaption(statusEnumerationValue1);
const statusEnumerationTranslation1 = texts.Translation.create(model);
statusEnumerationTranslation1.text = "New";
statusEnumerationTranslation1.languageCode = "en_US";
statusEnumerationCaption1.translations.push(statusEnumerationTranslation1);
const statusEnumerationValue2 = enumerations.EnumerationValue.createIn(statusEnumeration);
statusEnumerationValue2.name = "Hold";
const statusEnumerationCaption2 = texts.Text.createInEnumerationValueUnderCaption(statusEnumerationValue2);
const statusEnumerationTranslation2 = texts.Translation.create(model);
statusEnumerationTranslation2.text = "On Hold";
statusEnumerationTranslation2.languageCode = "en_US";
statusEnumerationCaption2.translations.push(statusEnumerationTranslation2);
const statusEnumerationValue3 = enumerations.EnumerationValue.createIn(statusEnumeration);
statusEnumerationValue3.name = "Progress";
const statusEnumerationCaption3 = texts.Text.createInEnumerationValueUnderCaption(statusEnumerationValue3);
const statusEnumerationTranslation3 = texts.Translation.create(model);
statusEnumerationTranslation3.text = "In Progress";
statusEnumerationTranslation3.languageCode = "en_US";
statusEnumerationCaption3.translations.push(statusEnumerationTranslation3);
const statusEnumerationValue4 = enumerations.EnumerationValue.createIn(statusEnumeration);
statusEnumerationValue4.name = "Completed";
const statusEnumerationCaption4 = texts.Text.createInEnumerationValueUnderCaption(statusEnumerationValue4);
const statusEnumerationTranslation4 = texts.Translation.create(model);
statusEnumerationTranslation4.text = "Completed";
statusEnumerationTranslation4.languageCode = "en_US";
statusEnumerationCaption4.translations.push(statusEnumerationTranslation4);
OK, c'était assez simple, alors passons à quelque chose d'un peu plus complexe.
Créer des entités
Au fur et à mesure que nous progressons, vous devriez voir une correspondance claire entre le code que nous avons ici et l’exécution des mêmes actions dans Studio Pro.
Commençons par l'entité de ligne de commande. La création de l'entité est simple. L'emplacement dicte la position que l'entité prendra dans le diagramme du modèle de domaine de Studio Pro : où x spécifie la position horizontale en points de diagramme mesurés à partir de la gauche du diagramme, et y spécifie la position verticale en points de diagramme mesurés à partir du haut du diagramme. Les deux reflètent le coin supérieur gauche de la zone d'entité.
// Create the Order Line Entity and attributes
const orderLineEntity = domainmodels.Entity.createIn(domainModel);
orderLineEntity.name = "OrderLine";
orderLineEntity.location = { x: 100, y: 100};
orderLineEntity.documentation = "The Order Line entity created using the Model SDK";
Les deux premiers attributs sont également simples : l’un est un entier et l’autre est un décimal.
const orderLineIdAttributeType = domainmodels.IntegerAttributeType.create(model);
const orderLineIdAttributeDefault = domainmodels.StoredValue.create(model);
orderLineIdAttributeDefault.defaultValue = "";
const orderLineIdAttribute = domainmodels.Attribute.createIn(orderLineEntity);
orderLineIdAttribute.name = "OrderLineId";
orderLineIdAttribute.type = orderLineIdAttributeType;
orderLineIdAttribute.value = orderLineIdAttributeDefault;
orderLineIdAttribute.documentation = "The Id of this Order Line unique within an order created using the Model SDK";
const orderLineValueAttributeType = domainmodels.DecimalAttributeType.create(model);
const orderLineValueAttributeDefault = domainmodels.StoredValue.create(model);
orderLineValueAttributeDefault.defaultValue = "";
const orderLineValueAttribute = domainmodels.Attribute.createIn(orderLineEntity);
orderLineValueAttribute.name = "OrderLineValue";
orderLineValueAttribute.type = orderLineValueAttributeType;
orderLineValueAttribute.value = orderLineValueAttributeDefault;
orderLineValueAttribute.documentation = "The Value of this Order Line created using the Model SDK";
Le troisième attribut présente quelques inconvénients :
- L'attribut est une chaîne et a une longueur (300) spécifiée.
- L'attribut a une valeur par défaut qui n'est pas vide : « Happy Days ».
- L'attribut est soumis à une règle de validation obligatoire. Cela nécessite la définition d'un message d'erreur. Encore une fois, nous utilisons simplement l'anglais américain pour plus de simplicité.
const orderLineProductNameAttributeType = domainmodels.StringAttributeType.create(model);
orderLineProductNameAttributeType.length = 300;
const orderLineProductNameAttributeDefault = domainmodels.StoredValue.create(model);
orderLineProductNameAttributeDefault.defaultValue = "Happy Days";
const orderLineProductNameAttribute = domainmodels.Attribute.createIn(orderLineEntity);
orderLineProductNameAttribute.name = "ProductName";
orderLineProductNameAttribute.type = orderLineProductNameAttributeType;
orderLineProductNameAttribute.value = orderLineProductNameAttributeDefault;
orderLineProductNameAttribute.documentation = "The name of the Product the subject of this Order Line created using the Model SDK";
const orderLineEntityValidationRule1 = domainmodels.ValidationRule.createIn(orderLineEntity);
orderLineEntityValidationRule1.attribute = orderLineProductNameAttribute;
domainmodels.RequiredRuleInfo.createIn(orderLineEntityValidationRule1);
const orderLineEntityValidationRuleText = texts.Text.createInValidationRuleUnderErrorMessage(orderLineEntityValidationRule1);
const orderLineEntityValidationRuleTranslation = texts.Translation.create(model);
orderLineEntityValidationRuleTranslation.text = "A Product Name must be entered";
orderLineEntityValidationRuleTranslation.languageCode = "en_US";
orderLineEntityValidationRuleText.translations.push(orderLineEntityValidationRuleTranslation);
Ensuite, nous allons créer l'entité de commande. C'est un peu différent de la ligne de commande car il s'agit d'une spécialisation de System.FileDocument. Comme je l'ai mentionné dans le Blog SDK, partie 1, l'utilisation d'éléments de module système est un peu délicate car, pour des raisons de sécurité, vous ne pouvez pas les référencer directement dans un script SDK. Nous utilisons donc une solution de contournement. Si l'entité de généralisation n'était pas dans le module système, nous pourrions la référencer directement.
// Create the Order Entity and its attributes
const orderEntity = domainmodels.Entity.createIn(domainModel);
orderEntity.name = "Order";
orderEntity.location = { x: 500, y: 100};
orderEntity.documentation = "The Order Entity specializing from System.FileDocument created using the Model SDK";
const orderEntityGeneralization = domainmodels.Generalization.createIn(orderEntity);
(orderEntityGeneralization as any)["__generalization"].updateWithRawValue("System.FileDocument");
Pour l'instant, nous ne faisons rien d'extraordinaire avec les attributs de l'entité de commande, donc le code est simple, mais notez encore qu'il existe un attribut de chaîne avec une longueur spécifiée (100), l'attribut AutoNumber OrderId a une valeur de départ par défaut définie (définie sur '1') et OrderStatus (qui utilise l'énumération que nous avons créée ci-dessus) a sa valeur par défaut définie ('_New').
const orderIdAttributeType = domainmodels.AutoNumberAttributeType.create(model);
const orderIdAttributeDefault = domainmodels.StoredValue.create(model);
orderIdAttributeDefault.defaultValue = "1";
const orderIdAttribute = domainmodels.Attribute.createIn(orderEntity);
orderIdAttribute.name = "OrderId";
orderIdAttribute.type = orderIdAttributeType;
orderIdAttribute.value = orderIdAttributeDefault;
orderIdAttribute.documentation = "The Id of this Order Id unique accross the system created using the Model SDK";
const orderValueAttributeType = domainmodels.DecimalAttributeType.create(model);
const orderValueAttributeDefault = domainmodels.StoredValue.create(model);
orderValueAttributeDefault.defaultValue = "";
const orderValueAttribute = domainmodels.Attribute.createIn(orderEntity);
orderValueAttribute.name = "OrderValue";
orderValueAttribute.type = orderValueAttributeType;
orderValueAttribute.value = orderValueAttributeDefault;
orderValueAttribute.documentation = "The Value of this Order created using the Model SDK";
const customerNameAttributeType = domainmodels.StringAttributeType.create(model);
customerNameAttributeType.length = 100;
const customerNameAttributeDefault = domainmodels.StoredValue.create(model);
customerNameAttributeDefault.defaultValue = "";
const customerNameAttribute = domainmodels.Attribute.createIn(orderEntity);
customerNameAttribute.name = "CustomerName";
customerNameAttribute.type = customerNameAttributeType;
customerNameAttribute.value = customerNameAttributeDefault;
customerNameAttribute.documentation = "The name of the Customer who raised this Order created using the Model SDK";
const orderStatusAttributeType = domainmodels.EnumerationAttributeType.create(model);
orderStatusAttributeType.enumeration = statusEnumeration;
const orderStatusAttributeDefault = domainmodels.StoredValue.create(model);
orderStatusAttributeDefault.defaultValue = "_New";
const orderStatusAttribute = domainmodels.Attribute.createIn(orderEntity);
orderStatusAttribute.name = "OrderStatus";
orderStatusAttribute.type = orderStatusAttributeType;
orderStatusAttribute.value = orderStatusAttributeDefault;
orderStatusAttribute.documentation = "The status of this Order created using the Model SDK";
Créez l'entité OrderSearch non persistante et ses attributs. La non-persistance est définie à l'aide d'un indicateur simple.
// Create the OrderSearch non-persistent Entity
const orderSearchEntity = domainmodels.Entity.createIn(domainModel);
orderSearchEntity.name = "OrderSearch";
orderSearchEntity.location = { x: 900, y: 100};
orderSearchEntity.documentation = "A non-persistent entity used for searching Orders created using the Model SDK";
const orderSearchEntityNoGeneralization = domainmodels.NoGeneralization.createIn(orderSearchEntity);
orderSearchEntityNoGeneralization.persistable = false;
const orderSearchMinimumValueAttributeType = domainmodels.DecimalAttributeType.create(model);
const orderSearchMinimumValueDefaultValue = domainmodels.StoredValue.create(model);
orderSearchMinimumValueDefaultValue.defaultValue = "";
const orderSearchMiniumuValueAttribute = domainmodels.Attribute.createIn(orderSearchEntity);
orderSearchMiniumuValueAttribute.name = "MinimumValue";
orderSearchMiniumuValueAttribute.type = orderSearchMinimumValueAttributeType;
orderSearchMiniumuValueAttribute.value = orderSearchMinimumValueDefaultValue;
orderSearchMiniumuValueAttribute.documentation = "The search order value minimum value created using the Model SDK";
const orderSearchMaximumValueAttributeType = domainmodels.DecimalAttributeType.create(model);
const orderSearchMaximumValueDefaultValue = domainmodels.StoredValue.create(model);
orderSearchMaximumValueDefaultValue.defaultValue = "";
const orderSearchMaxiumuValueAttribute = domainmodels.Attribute.createIn(orderSearchEntity);
orderSearchMaxiumuValueAttribute.name = "MaximumValue";
orderSearchMaxiumuValueAttribute.type = orderSearchMaximumValueAttributeType;
orderSearchMaxiumuValueAttribute.value = orderSearchMaximumValueDefaultValue;
orderSearchMaxiumuValueAttribute.documentation = "The search order value maximum value created using the Model SDK";
Enfin, nous devons créer l'association entre les entités de commande et de ligne de commande. Connexion enfant et parentConnection les paramètres sont similaires à ceux de l'entité Localisation valeurs, mais au lieu de positions absolues dans le diagramme, elles se réfèrent à des pourcentages. x fait référence à la distance le long de la largeur de l'entité dans le diagramme, et la y fait référence à la distance à laquelle se trouve l'entité sur le côté du diagramme. Grâce à ces éléments, vous pouvez définir la ligne représentant l'association pour qu'elle se termine à n'importe quel point de la zone de l'entité.
Une fois l'association créée, nous définissons le comportement de suppression afin que les objets OrderLine associés soient supprimés lorsqu'un objet de commande est supprimé.
// Create the association between the OrderLine and Order Entities
const association = domainmodels.Association.createIn(domainModel);
association.name = "OrderLine_Order";
association.child = orderEntity;
association.parent = orderLineEntity;
association.type = domainmodels.AssociationType.Reference;
association.owner = domainmodels.AssociationOwner.Default;
association.childConnection = { x: 0, y: 50};
association.parentConnection = { x: 100, y: 50};
association.documentation = "Association created using the Model SDK";
const associationDeleteBehavior = domainmodels.AssociationDeleteBehavior.createIn(association);
associationDeleteBehavior.childDeleteBehavior = domainmodels.DeletingBehavior.DeleteMeAndReferences;
association.deleteBehavior = associationDeleteBehavior;
Tout cela devrait se présenter sous la forme d'un diagramme de modèle de domaine qui ressemble à ceci :

Paramètres de sécurité de l'entité
Nous devons maintenant exécuter une liste de paramètres pour contrôler la sécurité de chacun des rôles d'administrateur et d'utilisateur que nous avons créés. Chaque attribut et association doit être configuré séparément et si les rôles diffèrent, chaque rôle doit également être configuré séparément. Cette opération est relativement simple, même si elle prend du temps.
Nous devons également définir les paramètres des attributs hérités de System.FileDocument en utilisant une solution de contournement similaire à celle utilisée précédemment, car le module système n'est pas directement accessible.
// Set up the security on the new module's Entities
// Security settings for Order Line Entity for Admin user
const orderLineEntityAdminAccessRule = domainmodels.AccessRule.createInEntityUnderAccessRules(orderLineEntity);
orderLineEntityAdminAccessRule.allowCreate = true;
orderLineEntityAdminAccessRule.allowDelete = true;
orderLineEntityAdminAccessRule.defaultMemberAccessRights = domainmodels.MemberAccessRights.ReadWrite;
orderLineEntityAdminAccessRule.moduleRoles.push(adminRole);
const orderLineEntityAdminAccessOrderLineId = domainmodels.MemberAccess.createIn(orderLineEntityAdminAccessRule);
orderLineEntityAdminAccessOrderLineId.attribute = orderLineIdAttribute;
orderLineEntityAdminAccessOrderLineId.accessRights = domainmodels.MemberAccessRights.ReadWrite;
const orderLineEntityAdminAccessProductName = domainmodels.MemberAccess.createIn(orderLineEntityAdminAccessRule);
orderLineEntityAdminAccessProductName.attribute = orderLineProductNameAttribute;
orderLineEntityAdminAccessProductName.accessRights = domainmodels.MemberAccessRights.ReadWrite;
const orderLineEntityAdminAccessOrderLineValue = domainmodels.MemberAccess.createIn(orderLineEntityAdminAccessRule);
orderLineEntityAdminAccessOrderLineValue.attribute = orderLineValueAttribute;
orderLineEntityAdminAccessOrderLineValue.accessRights = domainmodels.MemberAccessRights.ReadWrite;
const orderLineEntityAdminAccessAssociation = domainmodels.MemberAccess.createIn(orderLineEntityAdminAccessRule);
orderLineEntityAdminAccessAssociation.association = association;
orderLineEntityAdminAccessAssociation.accessRights = domainmodels.MemberAccessRights.ReadWrite;
// Security settings for Order Line Entity for regular user
const orderLineEntityUserAccessRule = domainmodels.AccessRule.createInEntityUnderAccessRules(orderLineEntity);
orderLineEntityUserAccessRule.allowCreate = false;
orderLineEntityUserAccessRule.allowDelete = false;
orderLineEntityUserAccessRule.defaultMemberAccessRights = domainmodels.MemberAccessRights.ReadOnly;
orderLineEntityUserAccessRule.moduleRoles.push(userRole);
const orderLineEntityUserAccessOrderLineId = domainmodels.MemberAccess.createIn(orderLineEntityUserAccessRule);
orderLineEntityUserAccessOrderLineId.attribute = orderLineIdAttribute;
orderLineEntityUserAccessOrderLineId.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderLineEntityUserAccessProductName = domainmodels.MemberAccess.createIn(orderLineEntityUserAccessRule);
orderLineEntityUserAccessProductName.attribute = orderLineProductNameAttribute;
orderLineEntityUserAccessProductName.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderLineEntityUserAccessOrderLineValue = domainmodels.MemberAccess.createIn(orderLineEntityUserAccessRule);
orderLineEntityUserAccessOrderLineValue.attribute = orderLineValueAttribute;
orderLineEntityUserAccessOrderLineValue.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderLineEntityUserAccessAssociation = domainmodels.MemberAccess.createIn(orderLineEntityUserAccessRule);
orderLineEntityUserAccessAssociation.association = association;
orderLineEntityUserAccessAssociation.accessRights = domainmodels.MemberAccessRights.ReadOnly;
// Security settings for Order Entity for Admin user
const orderEntityAdminAccessRule = domainmodels.AccessRule.createInEntityUnderAccessRules(orderEntity);
orderEntityAdminAccessRule.allowCreate = true;
orderEntityAdminAccessRule.allowDelete = true;
orderEntityAdminAccessRule.defaultMemberAccessRights = domainmodels.MemberAccessRights.ReadWrite;
orderEntityAdminAccessRule.moduleRoles.push(adminRole);
const orderEntityAdminAccessOrderId = domainmodels.MemberAccess.createIn(orderEntityAdminAccessRule);
orderEntityAdminAccessOrderId.attribute = orderIdAttribute;
orderEntityAdminAccessOrderId.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderEntityAdminAccessOrderValue = domainmodels.MemberAccess.createIn(orderEntityAdminAccessRule);
orderEntityAdminAccessOrderValue.attribute = orderValueAttribute;
orderEntityAdminAccessOrderValue.accessRights = domainmodels.MemberAccessRights.ReadWrite;
const orderEntityAdminAccessCustomerName = domainmodels.MemberAccess.createIn(orderEntityAdminAccessRule);
orderEntityAdminAccessCustomerName.attribute = customerNameAttribute;
orderEntityAdminAccessCustomerName.accessRights = domainmodels.MemberAccessRights.ReadWrite;
const orderEntityAdminAccessOrderStatus = domainmodels.MemberAccess.createIn(orderEntityAdminAccessRule);
orderEntityAdminAccessOrderStatus.attribute = orderStatusAttribute;
orderEntityAdminAccessOrderStatus.accessRights = domainmodels.MemberAccessRights.ReadWrite;
const orderEntityAdminAccessName = domainmodels.MemberAccess.createIn(orderEntityAdminAccessRule);
(orderEntityAdminAccessName as any)["__attribute"].updateWithRawValue("System.FileDocument.Name");
orderEntityAdminAccessName.accessRights = domainmodels.MemberAccessRights.ReadWrite;
const orderEntityAdminAccessFileId = domainmodels.MemberAccess.createIn(orderEntityAdminAccessRule);
(orderEntityAdminAccessFileId as any)["__attribute"].updateWithRawValue("System.FileDocument.FileID");
orderEntityAdminAccessFileId.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderEntityAdminAccessDeleteAfterDownload = domainmodels.MemberAccess.createIn(orderEntityAdminAccessRule);
(orderEntityAdminAccessDeleteAfterDownload as any)["__attribute"].updateWithRawValue("System.FileDocument.DeleteAfterDownload");
orderEntityAdminAccessDeleteAfterDownload.accessRights = domainmodels.MemberAccessRights.ReadWrite;
const orderEntityAdminAccessContents = domainmodels.MemberAccess.createIn(orderEntityAdminAccessRule);
(orderEntityAdminAccessContents as any)["__attribute"].updateWithRawValue("System.FileDocument.Contents");
orderEntityAdminAccessContents.accessRights = domainmodels.MemberAccessRights.ReadWrite;
const orderEntityAdminAccessHasContents = domainmodels.MemberAccess.createIn(orderEntityAdminAccessRule);
(orderEntityAdminAccessHasContents as any)["__attribute"].updateWithRawValue("System.FileDocument.HasContents");
orderEntityAdminAccessHasContents.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderEntityAdminAccessSize = domainmodels.MemberAccess.createIn(orderEntityAdminAccessRule);
(orderEntityAdminAccessSize as any)["__attribute"].updateWithRawValue("System.FileDocument.Size");
orderEntityAdminAccessSize.accessRights = domainmodels.MemberAccessRights.ReadOnly;
// Security settings for Order Entity for regular user
const orderEntityUserAccessRule = domainmodels.AccessRule.createInEntityUnderAccessRules(orderEntity);
orderEntityUserAccessRule.allowCreate = false;
orderEntityUserAccessRule.allowDelete = false;
orderEntityUserAccessRule.defaultMemberAccessRights = domainmodels.MemberAccessRights.ReadOnly;
orderEntityUserAccessRule.moduleRoles.push(userRole);
const orderEntityUserAccessOrderId = domainmodels.MemberAccess.createIn(orderEntityUserAccessRule);
orderEntityUserAccessOrderId.attribute = orderIdAttribute;
orderEntityUserAccessOrderId.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderEntityUserAccessOrderValue = domainmodels.MemberAccess.createIn(orderEntityUserAccessRule);
orderEntityUserAccessOrderValue.attribute = orderValueAttribute;
orderEntityUserAccessOrderValue.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderEntityUserAccesCustomerName = domainmodels.MemberAccess.createIn(orderEntityUserAccessRule);
orderEntityUserAccesCustomerName.attribute = customerNameAttribute;
orderEntityUserAccesCustomerName.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderEntityUserAccesOrderStatus = domainmodels.MemberAccess.createIn(orderEntityUserAccessRule);
orderEntityUserAccesOrderStatus.attribute = orderStatusAttribute;
orderEntityUserAccesOrderStatus.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderEntityUserAccessName = domainmodels.MemberAccess.createIn(orderEntityUserAccessRule);
(orderEntityUserAccessName as any)["__attribute"].updateWithRawValue("System.FileDocument.Name");
orderEntityUserAccessName.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderEntityUserAccessFileId = domainmodels.MemberAccess.createIn(orderEntityUserAccessRule);
(orderEntityUserAccessFileId as any)["__attribute"].updateWithRawValue("System.FileDocument.FileID");
orderEntityUserAccessFileId.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderEntityUserAccessDeleteAfterDownload = domainmodels.MemberAccess.createIn(orderEntityUserAccessRule);
(orderEntityUserAccessDeleteAfterDownload as any)["__attribute"].updateWithRawValue("System.FileDocument.DeleteAfterDownload");
orderEntityUserAccessDeleteAfterDownload.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderEntityUserAccessContents = domainmodels.MemberAccess.createIn(orderEntityUserAccessRule);
(orderEntityUserAccessContents as any)["__attribute"].updateWithRawValue("System.FileDocument.Contents");
orderEntityUserAccessContents.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderEntityUserAccessHasContents = domainmodels.MemberAccess.createIn(orderEntityUserAccessRule);
(orderEntityUserAccessHasContents as any)["__attribute"].updateWithRawValue("System.FileDocument.HasContents");
orderEntityUserAccessHasContents.accessRights = domainmodels.MemberAccessRights.ReadOnly;
const orderEntityUserAccessSize = domainmodels.MemberAccess.createIn(orderEntityUserAccessRule);
(orderEntityUserAccessSize as any)["__attribute"].updateWithRawValue("System.FileDocument.Size");
orderEntityUserAccessSize.accessRights = domainmodels.MemberAccessRights.ReadOnly;
// Security settings for OrderSearch Entity for both Admin and regular users
const orderSearchEntityUserAccessRule = domainmodels.AccessRule.createInEntityUnderAccessRules(orderSearchEntity);
orderSearchEntityUserAccessRule.allowCreate = true;
orderSearchEntityUserAccessRule.allowDelete = true;
orderSearchEntityUserAccessRule.defaultMemberAccessRights = domainmodels.MemberAccessRights.ReadWrite;
orderSearchEntityUserAccessRule.moduleRoles.push(adminRole);
orderSearchEntityUserAccessRule.moduleRoles.push(userRole);
const orderSearchEntityUserAccessMinimumValue = domainmodels.MemberAccess.createIn(orderSearchEntityUserAccessRule);
orderSearchEntityUserAccessMinimumValue.attribute = orderSearchMiniumuValueAttribute;
orderSearchEntityUserAccessMinimumValue.accessRights = domainmodels.MemberAccessRights.ReadWrite;
const orderSearchEntityUserAccessMaximumValue = domainmodels.MemberAccess.createIn(orderSearchEntityUserAccessRule);
orderSearchEntityUserAccessMaximumValue.attribute = orderSearchMaxiumuValueAttribute;
orderSearchEntityUserAccessMaximumValue.accessRights = domainmodels.MemberAccessRights.ReadWrite;e
Aboutissement
C'est presque fini maintenant ! Ensuite, nous définissons le projet sur le niveau de sécurité « production » (tout vérifier) et prenons les rôles de module que nous avons créés précédemment et les mappons aux rôles du projet.
Ensuite, effacez les modifications et validez.
// Set up the project level security roles. Set Production level security and put the module roles into the project roles
const projectSecurity = await model.allProjectSecurities()[0].load();
projectSecurity.securityLevel = security.SecurityLevel.CheckEverything;
projectSecurity.checkSecurity = true;
const adminRoleName = projectSecurity.adminUserRoleName;
const projectAdminRole = projectSecurity.userRoles.find(t => t.name === adminRoleName);
const projectUserRole = projectSecurity.userRoles.find(t => t.name === "User");
if (projectAdminRole != undefined)
projectAdminRole.moduleRoles.push(adminRole);
if (projectUserRole != undefined)
projectUserRole.moduleRoles.push(userRole);
console.log("Committing changes to the repository");
await model.flushChanges();
await workingCopy.commitToRepository("trunk", {commitMessage: "App created and modified by my Create App Model SDK script"});
}
Donc, avec ce script enregistré dans un dossier initialisé NodeJS (voir Blog SDK 1 pour savoir comment faire cela) en tant que fichier TypeScript (j'ai appelé le mien 'createapp.ts'), vous pouvez maintenant compiler le TypeScript en JavaScript en utilisant tsc et utilisez node pour exécuter le JavaScript généré.
Une fois ce script exécuté, vous devriez maintenant avoir une application créée avec un module ajouté et un modèle de domaine renseigné. OK, cela ne fait pas grand-chose pour l'instant — nous commencerons à mettre un peu de matière sur les os dans la troisième partie de cette série de blogs.

Erreurs
Quelques avertissements nés de l'expérience acquise lors de l'utilisation des SDK pour mettre à jour des applications. Le système SDK ne signale pas toujours les erreurs. Lorsque vous faites une erreur, vous pouvez en prendre connaissance à différents moments du processus :
- Le compilateur TypeScript signale des problèmes lors de la compilation en JavaScript.
- Lorsque vous exécutez JavaScript à l'aide de Node, les problèmes vous sont signalés.
- Lorsque vous ouvrez l'application dans Studio Pro, des erreurs/problèmes peuvent être signalés dans l'onglet Erreurs, ou ce que vous avez créé/modifié semble incorrect.
- Si c'est vraiment mauvais, Studio Pro peut signaler des exceptions lors de l'ouverture ou ne pas ouvrir l'application du tout et mordre la poussière.
Cela m'amène à dire : avoir toujours une position de repli. Faites une copie de sauvegarde du projet s'il s'agit d'un projet existant. Copiez le projet et travaillez sur la copie. Travaillez sur une branche. Peut-être toutes ces…

Fais ce que je dis, pas ce que je fais
Bon, de nombreux développeurs expérimentés doivent probablement grimacer en lisant le code ci-dessus. Je suis sûr qu'il est évident que ces scripts peuvent devenir très longs et répétitifs (les changements de modèle de domaine sont simples par rapport à la configuration de formulaires et de microflux par exemple), mais j'aime garder les choses aussi claires que possible, en particulier pour un article de blog. Cela implique d'utiliser des noms de variables significatifs et d'éviter une syntaxe complexe.
La première chose que nous pouvons faire est d'utiliser des fonctions qui aideront à raccourcir le script, par exemple comme ceci :
createEnumerationOption(model, statusEnumeration, "_New", "New");
createEnumerationOption(model, statusEnumeration, "Hold", "On Hold");
createEnumerationOption(model, statusEnumeration, "Progress", "In Progress");
createEnumerationOption(model, statusEnumeration, "Completed", "Completed");
.....
function createEnumerationOption(model: IModel, enumeration: enumerations.Enumeration, name: string, caption_en_US: string)
{
const value = enumerations.EnumerationValue.createIn(enumeration);
value.name = name;
const caption = texts.Text.createInEnumerationValueUnderCaption(value);
const translation = texts.Translation.create(model);
translation.text = caption_en_US;
translation.languageCode = "en_US";
caption.translations.push(translation);
}
Vous verrez probablement l’intérêt de créer une bibliothèque de fonctions et/ou de classes que vous pouvez inclure et utiliser dans une variété de scripts SDK que vous écrivez.
Mais les scripts longs peuvent également rencontrer des problèmes de performances pour d'autres raisons. J'ai appris trois choses pour réduire la charge mémoire et améliorer la vitesse :
- Appelez fréquemment FlushChanges. Cela déplace les modifications que vous avez apportées jusqu'à présent de votre environnement NodeJS local vers le Mendix ModelServer réduit votre empreinte mémoire locale.
- Utilisez des variables à portée locale lorsque vous le pouvez. L'utilisation de fonctions facilite cette tâche. Les variables à portée locale naissent, vivent et meurent dans le bloc que vous définissez et réduisent ainsi l'accumulation de données transportées dans le reste du script. Chaque référence de variable dans votre script nécessite une recherche quelconque dans les composants internes de JavaScript et plus vous avez défini de variables, plus cela prend du temps (et plus la mémoire est consommée).
- Gardez les noms de variables courts, ou du moins plus courts, car cela améliorera les temps d'exécution et réduira la surcharge de mémoire dans les scripts longs. Il est évident qu'il y a un compromis entre concision et clarté. J'ai une application qui écrit des fichiers TypeScript pour modifier des applications à ma place et je peux la configurer pour produire des noms de variables significatifs (pour le débogage) ou des noms de variables courts (pour la production) en appuyant sur un commutateur. Vous pouvez également choisir d'utiliser un outil distinct pour minimiser le JavaScript généré avant de l'exécuter, mais je n'ai pas essayé cela, donc je n'ai aucune idée de son fonctionnement.
Dans le dépôt Github mentionné ci-dessous, j'ai inclus deux scripts, un script simpliste qui est présenté dans ce blog ('createapp.ts') et un script alternatif qui implémente certaines des modifications que j'ai suggérées ici ('createapp2.ts'). Chaque script produit la même application, mais le second est nettement plus court et, je pense, plus facile à suivre.
Voilà qui conclut cet article. Les scripts sont disponibles sur Github à l'adresse https://github.com/Adrian-Preston/SDKBlog2.
Je continuerai en développant certaines fonctionnalités dans la troisième partie – à venir bientôt !