Adding Demo Data to your apps

When you create apps for Microsoft Dynamics 365 Business Central, the goal is to have people use it and pay for your app. For that, besides to just providing great functionality, you also need to work on your online help and marketing.

One major piece of the puzzle is also to allow your potential users to test out the functionality. You can allow a test drive for your app where users can try the full functionality for a limited time to ensure that the functionality is what they are looking for. I will be writing more about test drives in future posts. Today, I want to focus on a small, but important piece of the puzzle: Demo Data.

We have two different methods of adding demo data to the environment and I am going to talk about both of them, but focusing on the one that we found to be the easiest for now. We are using configuration packages to define our data. The advantage with those is that you can easily change the data and do not have to change the implementation of getting the data into the demo system.

You can also define the demo data through special codeunits that you create that just create the data, but that is a lot of coding and you have to change your app when you want to change your demo data. This is the reason we did not go for this: We don’t want to have to publish a new version of the app just to fix some demo data.

What demo data do we need?

If you are publishing your app in only one country, you need to only focus on data to make it work in your local country’s Business Central environment. However, if you are going to offer your app in multiple countries, you need to include demo data for multiple countries, since each localization has differences, even as simple as different languages and therefore different posting groups.

You want to include any data in your demo environment that is required to turn a standard “CRONUS” company into a company that is fully setup to use your app. This includes setup data, possibly new customers, items, vendors, etc. But also transactional data could be needed, such as open orders that can be processed.

How do we prepare the demo data?

We have put some effort in and deployed a standard docker container for the latest version of Business Central for each country we want to publish out apps in. Once we have done that, we are setting up the data manually to ensure that everything is working as expected. While creating the data, we note down the different tables that we touch for the next step.

Exporting Demo Data

You then want to create a configuration package for your app and include all tables that were touched during the manual data creation. You also want to make sure that you do not include any fields that you did not change values in so that the standard values stay in place – even if they change. Once you have created the package, you can export the package into a “.rapidstart” package. We do suggest to then create a new container with a new database, import the demo package that you have created, and then go through the processes of your app to ensure that everything works. If not, you go back to your first container, add some data or change it and re-export the package and start the validation process again.

Importing Demo Data

Now we come to the most important part: How do we get the data back into the system when your app is installed? Well, we use the following method described below. There are probably other methods available as well, but we have chosen to store the rapidstart packages with the naming convention “[AppName]_[localization]_[version].rapidstart” in an Azure storage account and then import it in the app. I am going to walk you through the different steps.

Creating an Azure storage account

You first have to sign up for an Azure subscription, if you don’t have one yet. You can do that on Microsoft’s site.

Once you have signed up and have a subscription ready, you need to create a storage account. You do that in the Azure portal by clicking on “Storage Accounts” on the left and then selecting Add. You can see the details of this process in the screenshot below. Just select the subscription and either create a new resource group or select an existing one. Use the values defined in the screenshots for the rest. Then select “Review + Create”.

Azure Storage Account Setup
Azure Storage Account Setup Advanced

Configuring Storage Account

For the configuration of the storage account and upload of files, I am using the Microsoft Azure Storage Explorer. You have to add the subscription you created under “Manage Accounts”. Navigate to “Storage Accounts” -> You storage account name -> Blob Containers and click on “New Folders”. Give the folder a name, such as “configuration packages”. Once you right click on the newly created folder, you can select “Set Public Access Level” and set it to “Public read access for blobs only”. This way, you do not have to use access keys to download the files and it is data that anyone could have access to anyway. Last, but not least, upload your configuration package.

Changes in your app

We are only importing the demo data into the sandbox or demo companies for Business Central. If the user is in a sandbox or demo company, we are displaying a notification at the top on all role centers to allow users to select the import of the data. I am not showing this in detail, since it changes a bit between Business Central 2019 Wave 1 and Wave 2, but you should be able to create a notification easily. We provide also a configuration table where we store, if a user selected to not show the notification again, even if they don’t download the data. If the data was imported by someone, the notification won’t be shown either.

The actual download can then be done as shown below. We create a new codeunit with an import function that is executed when you want to import the data.

You can then call the function “ImportConfigurationPackage(StorageAccount, ContainerName, BlobName)”.

  1. codeunit 50100 "My Import Config. Package"
  2. {
  3.     var
  4.         GetBlobUriTxt: Label 'https://%1.blob.core.windows.net/%2/%3', Locked = true;
  5.         DownloadErr: Label 'An error occured downloading a file from Azure storage.\\Error: %1';
  6.         BlockedByEnvironmentErr: Label 'You cannot download data when the tenant blocks service connections.';
  7.         ImportPackagesTxt: Label 'Importing configuration packages...';
  8.  
  9.     procedure ImportConfigurationPackage(StorageAccount: Text; ContainerName: Text; BlobName: Text)
  10.     var
  11.         //this is for wave 1: TempBlob: Record TempBlob temporary;
  12.         ConfigPackageImport: Codeunit "Config. Package - Import";
  13.         TempBlob: Codeunit "Temp Blob"; // this is for wave 2
  14.         FileOutStream: OutStream;
  15.         Window: Dialog;
  16.     begin
  17.         if GuiAllowed() then
  18.             Window.Open(ImportPackagesTxt);
  19.  
  20.         if (StorageAccount = '') or (ContainerName = '') or (BlobName = '') then
  21.             exit;
  22.  
  23.         Clear(TempBlob);
  24.         // this is for wave 1: TempBlob.Blob.CreateOutStream(FileOutStream);
  25.         TempBlob.CreateOutStream(FileOutStream); // wave 2
  26.         DownloadFileFromAzureBlob(StorageAccount, ContainerName, BlobName, FileOutStream);
  27.  
  28.         ConfigPackageImport.ImportAndApplyRapidStartPackageStream(TempBlob);
  29.  
  30.         if GuiAllowed() then
  31.             Window.Close();
  32.     end;
  33.  
  34.     procedure DownloadFileFromAzureBlob(StorageAccount: Text; ContainerName: Text; BlobName: Text; var FileOutStream: OutStream)
  35.     var
  36.         Uri: Text;
  37.         Client: HttpClient;
  38.         ResponseMessage: HttpResponseMessage;
  39.         ContentInStream: InStream;
  40.     begin
  41.         if (StorageAccount = '') or (ContainerName = '') or (BlobName = '') then
  42.             exit;
  43.  
  44.         Uri := StrSubstNo(GetBlobUriTxt, StorageAccount, ContainerName, BlobName);
  45.  
  46.         ClearLastError();
  47.         if not Client.Get(Uri, ResponseMessage) then
  48.             Error(DownloadErr, GetLastErrorText());
  49.  
  50.         if ResponseMessage.IsBlockedByEnvironment() then
  51.             Error(BlockedByEnvironmentErr);
  52.         if not ResponseMessage.IsSuccessStatusCode() then
  53.             Error(DownloadErr, ResponseMessage.ReasonPhrase());
  54.  
  55.         ClearLastError();
  56.         if not ResponseMessage.Content().ReadAs(ContentInStream) then
  57.             Error(DownloadErr, GetLastErrorText());
  58.  
  59.         CopyStream(FileOutStream, ContentInStream);
  60.     end;
  61. }

What Next?

You obviously have to test the functionality to make sure that the import works and that the data is applied. If the data is applied properly and the app is working with your demo data, you can send your app for validation and get the changes published.

Going forward, when you want to update your demo data, you would only have to change the rapidstart package and upload it to the storage account. Nothing else has to be done.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.