Wednesday, August 8, 2018

OData

What is OData?

OData is a standard protocol for creating and consuming data. 
The purpose of OData is to provide a protocol that is based on Representational State Transfer (REST) for create, read, update, and delete (CRUD) operations. 
OData applies web technologies such as HTTP and JavaScript Object Notation (JSON) to provide access to information from various programs. 

OData provides the following benefits:
  • It lets developers interact with data by using RESTful web services.
  • It provides a simple and uniform way to share data in a discoverable manner.
  • It enables broad integration across products.
  • It enables integration by using the HTTP protocol stack.
The public OData service endpoint enables access to data in a consistent manner across a broad range of clients.

OData services

We provide an OData REST endpoint. This endpoint exposes all the data entities that are marked as IsPublic in the Application Object Tree (AOT). It supports complete CRUD (create, retrieve, update, and delete) functionality that users can use to insert and retrieve data from the system. 

Supported features from the OData specification

The following are the high-level features that are enabled for the OData service, per the OData specification.
  • CRUD support is handled through HTTP verb support for POST, PATCH, PUT, and DELETE.
  • Available query options are
    • $filter
    • $count
    • $orderby
    • $skip
    • $top
    • $expand
    • $select
  • The OData service supports serving driven paging with a maximum page size of 1,000.

Filter details

There are built-in operators for $filter
  • Equals
  • Not equals
  • Greater than
  • Greater than or equal
  • Less than
  • Less than or equal
  • And
  • Or
  • Not
  • Addition
  • Subtraction
  • Multiplication
  • Division
You can also use the Contains option with $filter requests. It has been implemented as a wildcard character. For example: http://host/service/EntitySet?$filter=StringField eq '\*retail\*'

Batch requests

Batch requests are supported in the OData service.

Metadata annotations

/data/$metadata provides annotations. EnumType is support in $metadata.
EnumType metadata

Cross-company behavior

By default, OData returns only data that belongs to the user's default company. To see data from outside the user's default company, specify the ?cross-company=true query option. This option will return data from all companies that the user has access to.
Example: http://[baseURI\]/data/FleetCustomers?cross-company=true
To filter by a particular company that isn't your default company, use the following syntax: http://[baseURI\]/data/FleetCustomers?$filter=dataAreaId eq 'usrt'&cross-company=true

Validate methods

The following table summarizes the validate methods that the OData stack calls implicitly on the corresponding data entity.
ODataMethods (listed in the order in which they are called)
Create
  1. Clear()
  2. Initvalue()
  3. PropertyInfo.SetValue() for all specified fields in the request
  4. Validatefield()
  5. Defaultrow
  6. Validatewrite()
  7. Write()
Update
  1. Forupdate()
  2. Reread()
  3. Clear()
  4. Initvalue()
  5. PropertyInfo.SetValue() for all specified fields in the request
  6. Validatefield()
  7. Defaultrow()
  8. Validatewrite()
  9. Write()
Delete
  1. Forupdate()
  2. Reread()
  3. checkRestrictedDeleteActions()
  4. Validatedelete()
  5. Delete()

Exposing OData entities

OData entities are based on the concept of an updatable view. When the IsPublic property for an updatable view is set to TRUE, that view is exposed as a top-level OData entity.

Setting navigation properties between OData entities

Links between OData entities are described by a navigation property. Navigation properties describe the navigation from one end of an association to the other end.

Adding actions on OData entities

Actions let you inject behaviors into the data model. To add actions, add a method to the updatable view, and decorate that method with specific attributes. Here is an example.
[SysODataActionAttribute("CalcMaintenanceDuration", true)]
public int CalculateMaintenanceDuration()
{
    //do something
    return 0;
}
In this example, the SysODataActionAttribute class decorates the CalculateMaintenanceDurationmethod that is exposed as an action. The first argument of the attribute is the publicly exposed name of the action, and the second argument indicates whether this action is always available. Methods that are exposed as actions can return any primitive type or another public updatable view. After this method is exposed, it appears in the OData $metadata. Here is an example.
Exposed method in the OData $metadata
The following example of an OData action takes in a parameter and returns a list.
[SysODataActionAttribute("GetColors", true),
 SysODataCollectionAttribute("return", Types::Record, "CarColor")]
public List GetColorsByAvailability(boolean onlyAvailableVehicles)
{
    List returnList = new List(Types::Record);
    // do something
    return returnList;
}
In this example, the SysODataCollectionAttribute class enables OData to expose strongly typed collections from X++. This class takes in three parameters:
  • The name of the parameter that is a list (Use return for the return value of the method.)
  • The X++ type of the members of this list
  • The public name of the OData resource that is contained in the collection
After these actions are exposed, they can be invoked from the service root URL.
You can find actions that are defined on data entities by searching for the SysODataActionAttributeattribute in the source code.

Querying or browsing an OData endpoint

OData enables an SQL-like language that lets you create rich queries against the database, so that the results include only the data items that you want. To create a query, append criteria to the resource path. For example, you can query the Customers entity collection by appending the following query options in your browser.
URLDescription
[Your organization's root URL]/data/CustomersList all the customers.
[Your organization's root URL]/data/Customers?$top=3List the first three records.
[Your organization's root URL]/data/Customers?$select=FirstName,LastNameList all the customers, but show only the first name and last name properties.
[Your organization's root URL]/data/Customers?$format=jsonList all the customers in a JSON format that can be used to interact with JavaScript clients.
The OData protocol supports many similar filtering and querying options on entities. 

Authentication

OData sits on the same authentication stack as the server.

Tips and tricks

Run multiple requests in a single transaction

The OData batch framework uses changesets. Each changeset contains a list of requests that should be treated as single atomic unit. In other words, either all the requests are run successfully or, if any request fails, none of the requests are run successfully. The following example shows how to send a batch request that has a list of requests in a single changeset.
The SaveChangesOptions.BatchWithSingleChangeset option in SaveChanges() helps guarantee that all requests are bundled into a single changeset.
public static void CreateProductColors(Resources context)
    {
        var productColorsCollection = new DataServiceCollection<ProductColor>(context);

        var color1 = new ProductColor();
        productColorsCollection.Add(color);
        color1.ColorId = "New Color1"; // set any other properties needed

        var color2 = new ProductColor();
        productColorsCollection.Add(color1);
        color2.ColorId = "New Color2"; // set any other properties needed

        context.SaveChanges(SaveChangesOptions.BatchWithSingleChangeset);
    }

Prevent unset records from being posted when you use an OData client

When you create a new record by using an OData client, as shown in example 1, properties that aren't set are included in the body of the request, and default values are assigned to them. To prevent this behavior and post only properties that are set explicitly, use the SaveChangesOptions.PostOnlySetProperties option in SaveChanges(), as shown in example 2.
Example 1
public static void CreateVendor(Resources context)
    {
        var vendorCollection = new DataServiceCollection<Vendor>(context);
        var vendor = new Vendor();
        vendorCollection.Add(vendor);
        // set properties
        context.SaveChanges();
    }
Example 2
public static void CreateVendor(Resources context)
    {
        var vendorCollection = new DataServiceCollection<Vendor>(context);
        var vendor = new Vendor();
        vendorCollection.Add(vendor);
        // set properties

        // Save specifying PostOnlySetProperties flag
        context.SaveChanges(SaveChangesOptions.PostOnlySetProperties);
    }

Handling duplicate names between enums and entities in metadata

There are instances where enums and entities share the same name. This name duplication results in OData client code generation errors. To recover from this error, the helper code in gitHub https://github.com/Microsoft/Dynamics-AX-Integration/blob/master/ServiceSamples/ODataConsoleApplication/MetadataDocumentValidator.cscan be used to identify duplicate name instances that must be removed. The generated metadata document can be used for further processing of the Odata logic on the client side.

Monday, August 6, 2018

Role based security in AX

Role based security in AX 

In role-based security, access is not granted to individual users, only to security roles. Users are assigned to roles. A user who is assigned to a security role has access to the set of privileges that is associated with that role. A user who is not assigned to any role has no privileges.
In Microsoft Dynamics AX, role-based security is aligned with the structure of the business. Users are assigned to security roles based on their responsibilities in the organization and their participation in business processes. The administrator grants access to the duties that users in a role perform, not to the program elements that users must use.
Permissions represent access to individual securable objects, such as menu items and tables. 
Privileges are composed of permissions and represent access to tasks, such as canceling payments and processing deposits. 
Duties are composed of privileges and represent parts of a business process, such as maintaining bank transactions. 
Both duties and privileges can be assigned to roles to grant access to Microsoft Dynamics AX.


Security roles

All users must be assigned to at least one security role in order to have access to Microsoft Dynamics AX. The security roles that are assigned to a user determine the duties that the user can perform and the parts of the user interface that the user can view.

Process cycles

A business process is a coordinated set of activities in which one or more participants consume, produce, and use economic resources to achieve organizational goals.
Process cycles are used for organization only. The process cycles themselves cannot be assigned to roles.

Duties

Duties correspond to parts of a business process. The administrator assigns duties to security roles. A duty can be assigned to more than one role.
In the security model for Microsoft Dynamics AX, duties contain privileges. For example, the Maintain bank transactions duty contains the Generate deposit slips and Cancel payments privileges. Although both duties and privileges can be assigned to security roles, we recommend that you use duties to grant access to Microsoft Dynamics AX.

Privileges

In the security model for Microsoft Dynamics AX, a privilege specifies the level of access that is required to perform a job, solve a problem, or complete an assignment. Privileges can be assigned directly to roles. However, for easier maintenance, we recommend that you assign only duties to roles.
A privilege contains permissions to individual application objects, such as user interface elements and tables. For example, the Cancel paymentsprivilege contains permissions to the menu items, fields, and tables that are required to cancel payments.

Permissions

Each function in Microsoft Dynamics AX, such as a form or a service, is accessed through an entry point. Menu items, web content items, and service operations are referred to collectively as entry points.
In the security model for Microsoft Dynamics AX, permissions group the securable objects and access levels that are required to run a function. This includes any tables, fields, forms or server side methods that are accessed through the entry point.
Only developers can create or modify permissions.

Method wrapping and Chain of Command (CoC) in D365

Class extension via method wrapping and Chain of Command (CoC)

The functionality for class extension, or class augmentation, has been improved in Microsoft Dynamics 365 for Finance and Operations. You can now wrap logic around methods that are defined in the base class that you're augmenting. 
  • You can extend the logic of public and protected methods without having to use event handlers. 
  • When you wrap a method, you can also access public and protected methods, and variables of the base class. 
In this way, you can start transactions and easily manage state variables that are associated with your class.

For example, a model contains the following code.

class BusinessLogic1 { str DoSomething(int arg) { … } }

You can now augment the functionality of the DoSomething method inside an extension class by reusing the same method name. An extension class must belong to a package that references the model where the augmented class is defined.

[ExtensionOf(ClassStr(BusinessLogic1))] final class BusinessLogic1_Extension { str DoSomething(int arg) { // Part 1 var s = next DoSomething(arg + 4); // Part 2 return s; } }

In this example, the wrapper around DoSomething and the required use of the next keyword create a Chain of Command (CoC) for the method. CoC is a design pattern where a request is handled by a series of receivers. The pattern supports loose coupling of the sender and the receivers.

We now run the following code.

BusinessLogic1 c = new BusinessLogic1(); info(c.DoSomething(33));

When this code is run, the system finds any method that wraps the DoSomething method. The system randomly runs one of these methods, such as the DoSomething method of the BusinessLogic1_Extension class. When the call to the next DoSomething method occurs, the system randomly picks another method in the CoC. If no more wrapped methods exist, the system calls the original implementation.

Capabilities

The following sections give more details about the capabilities of method wrapping and CoC.

Wrapping public and protected methods

Protected or public methods of classes, tables, or forms can be wrapped by using an extension class that augments that class, table, or form. The wrapper method must have the same signature as the base method.
  • When you augment form classes, only root-level methods can be wrapped. You can't wrap methods that are defined in nested classes.
  • Only methods that are defined in regular classes can be wrapped. Methods that are defined in extension classes can't be wrapped by augmenting the extension classes.

What about default parameters?

Methods that have default parameters can be wrapped by extension classes. However, the method signature in the wrapper method must not include the default value of the parameter.
For example, the following simple class has a method that has a default parameter.
class Person
{ Public void salute( str message = "Hi"){ } }

In this case, the wrapper method must resemble the following example.
[ExtensionOf(classtr(Person))]
final class aPerson_Extension { Public void salute( str message ){ } }
In the aPerson_Extension extension class, notice that the salute method doesn't include the default value of the message parameter.

Wrapping instance and static methods

Instance and static methods can be wrapped by extension classes. If a static method is the target that will be wrapped, the method in the extension must be qualified by using the static keyword.
For example, we have the following A class.
class A { public static void aStaticMethod( int parameter1) { // … } }
In this case, the wrapper method must resemble the following example.
[ExtensionOf(classstr(A)] final class An_Extension { public static void aStaticMethod( int parameter1) { Next aStaticMethod( 10 ); } }

Wrapper methods must always call next

Wrapper methods in an extension class must always call next, so that the next method in the chain and, finally, the original implementation are always called. This restriction helps guarantee that every method in the chain contributes to the result.
In the current implementation of this restriction, the call to next must be in the first-level statements in the method body.
Here are some important rules:
  • Calls to next can't be done conditionally inside an if statement.
  • Calls to next can't be done in whiledo-while, or for loop statements.
  • next statement can't be preceded by a return statement.
  • Because logical expressions are optimized, calls to next can't occur in logical expressions. At runtime, the execution of the complete expression isn't guaranteed.

Wrapping a base method in an extension of a derived class

The following example shows how to wrap a base method in an extension of a derived class. For this example, the following class hierarchy is used.
class A
{ public void salute(str message) { Info(message); } } class B extends A { } class C extends A { }

Therefore, there is one base class, A. Two classes, B and C, are derived from A. We will augment or create an extension class of one of the derived classes (in this case, B), as shown here.
[Extensionof(classstr(B))]
final class aB_Extension { public void salute(str message) { next salute( message ); Info("B extension"); } }

Although the aB_Extension class is an extension of B, and B doesn't have a method definition for the salute method, you can wrap the salute method that is defined in the base class, A. Therefore, only instances of the B class will include the wrapping of the salute method. Instances of the A and Cclasses will never call the wrapper method that is defined in the extension of the B class.
This behavior becomes clearer if we implement a method that uses these three classes.
class ProgramTest
{ Public static void Main( Args _args) { var a = new A( ); var b = new B( ); var c = new C( ); a.salute("Hi"); b.salute("Hi"); c.salute("Hi"); } }
For calls to a.salute(“Hi”) and c.salute(“Hi”), the Infolog shows only the message “Hi.” However, when b.salute(“Hi”) is called, the Infolog shows “Hi” followed by “B extension.”
By using this mechanism, you can wrap the original method only for specific derived classes.

Accessing protected members from extension classes

As of Platform update 9, you can access protected members from extension classes. These protected members include fields and methods. Note that this support isn't specific to wrapping methods but applies all the methods in the class extension. Therefore, class extensions are more powerful than they were before.

The Hookable attribute

If a method is explicitly marked as [Hookable(false)], the method can't be wrapped in an extension class. In the following example, anyMethod can't be wrapped in a class that augments anyClass1.
class anyClass1 { [HookableAttribute(false)] public void anyMethod() {…} }

Final methods and the Wrappable attribute

Public and protected methods that are marked as final can't be wrapped in extension classes. You can override this restriction by using the Wrappable attribute and setting the attribute parameter to true ([Wrappable(true)]). Similarly, to override the default capability for (non-final) public or protected methods, you can mark those methods as non-wrappable ([Wrappable(false)]).
In the following example, the doSomething method is explicitly marked as non-wrappable, even though it's a public method. The doSomethingElse method is explicitly marked as wrappable, even though it's a final method.
class anyClass2 { [Wrappable(false)] public void doSomething(str message) { …} [Wrappable(true)] final public void doSomethingElse(str message){ …} }

Restrictions on wrapper methods

The following sections describe restrictions on the use of CoC and method wrapping.

Kernel methods can't be wrapped

Kernel classes aren't X++ classes. Instead, they are classes that are defined in the kernel of the Microsoft Dynamics 365 Unified Operations platform. Even though extension classes are supported for kernel classes, method wrapping isn't supported for methods of kernel classes. In other words, if you want to wrap a method, the base method must be an X++ method.

X++ classes that are compiled by using Platform update 8 or earlier

The method wrapping feature requires specific functionality that is emitted by an X++ compiler that is part of Platform update 9 or later. Methods that are compiled by using earlier versions don't have the infrastructure to support this feature.

Nested class methods (forms) can't be wrapped

The concept of nested classes in X++ applies to forms for overriding data source methods and form control methods. Methods in nested classes can't be wrapped in class extensions.

How to enable the dimension fields based on the Item selected on the form.

[Form] public class KMTShipFromWarehouses extends FormRun {     InventDimCtrl_Frm_EditDimensions        inventDimFormSetup;     /// &l...