Wednesday, 4 December 2024

Power Platform licensing capability Notes

 Power Apps Premium Licensing Costs (4 Dec 2024): 

The retail price is $20/user per month, but it drops to $12/user/month for 2k licenses per org.  Large customers can negotiate as always.  Premium connectors & 40k requests per user (24-hours rolling)

Power Apps Pricing | Microsoft Power Platform

Power Automate Premium allows 40k requests per user (per 24-hour rolling), and the E365 allows 6k requests. Circa $18 TBC.

Dynamics 365 professional allows 40k requests per user (per 24-hour rolling) and gives the user premium Power Apps licensing.

Tuesday, 19 November 2024

Playwright Post 4 - 6 Min walkthru of Playwright testing with Azure Monitor

Overview: Install VS Code and Playwright Extensions, create tests, set MFA for Canvas Apps/Power Apps, loop through Power App applications and check the home page is loading, write logs to Azure App Insights and show via the Azure Dashboard.


6 min - annotated Playwright setup and use video

====================

BDD in playwright playwright-bdd - npm

Cucumber in Playwright GitHub - dhrumil-soni-th/playwright-cucumber-learning

GitHub - mxschmitt/awesome-playwright: A curated list of awesome tools, utils and projects using Playwright

GitHub - mxschmitt/awesome-playwright: A curated list of awesome tools, utils and projects using Playwright

GitHub - mxschmitt/awesome-playwright: A curated list of awesome tools, utils and projects using Playwright



========================

Playwright Series

Friday, 8 November 2024

Playwright series - Post 2 - Refactored TS code for Consciously verify Apps in Production

Overview: Create a function with Playwright tests that loops through all my production apps, logs in, and validates the Page title load on each app's home page.

Steps:

1. Create the spec.ts code that reads app.json to loop thru and validate sites

2. Record and Store the session state for all future test runs (Used for MFA in the tests runs)

3. Create an apps.json file containing URLs to open and validate


4. After running the test, you'll see that the 3 tests were completed successfully. In my case, there were 2 Power Apps with MFA enabled and an anonymous public website that had been checked.

Optional
Create short cuts to run your tests using PowerShell
PS C:\repos\PW> npx playwright test -g "Prod-CanvasApps" --project=chromium --headed

Next Steps:

Run continuously using the Azure Playwright Service.

=========================

Playwright Series

Playwright Post 1 - Overview of E2E testing using Playwright

Playwright Post 2 - Continuously Test/Monitor Canvas apps and website with MFA enabled (this post)

Playwright Post 3 - Add App Insights logging inside your Playwright tests 

Playwright Post 4 - 6 Min walkthru of Playwright testing with Azure Monitor

Playwright Post 5 - Understanding how playwright works

Playwright Post 6 - Unattended testing when secured with MFA 


Other Posts

Upgrading two C# Blazor web applications and verifying using Playwright - super fast 

Mendix - Part 2 - Diving deeper (E2E automation testing of Mendix using Playwright)

Low-code testing with playwright walkthru

Continiously Monitor Apps using Playwright with TS

Testing Canvasa apps with Playwright using C# (rather use TS, it's better)

Wednesday, 6 November 2024

Fix links within Pdf files when moving from a File share to web hosting

Overview: Build a console to help migrate more than 80k PDF document internal links. The client used a DFS SMB file share to hold index PDFs and multiple documents that needed to be moved to a SharePoint document library.

Hypothesis: Loop through all pdf's in a folder; if there are links, identify the file server links and convert them to web links so they work in the new SharePoint document library.  Various tools were identified as possible solutions but came up short in the migration.  Two good tools are Replace Magic & PDF-XChange Editor.

Resolution: Below is the C# code I wrote to change the links in the VS code.  The debugger was useful as there were many different types of links within the plethora of PDFs.


C# Code

using iText.Kernel.Pdf;
using iText.Kernel.Pdf.Annot;
using iText.Kernel.Pdf.Action;
using iText.Bouncycastle.Crypto;  // pdf fails at runtime periodically without the directive
using System.Text.RegularExpressions;
class Program
{
    static void Main(string[] args)
    {
        string folderPath = @"C:\Users\PaulBeck\Downloads\Software\LinkConvert\ConvertLinksCsharp\"; // Replace with your folder path          
        Console.WriteLine("Please enter a folder Path: e.g. " + folderPath);
        string inputPath = Console.ReadLine();        
        Console.WriteLine("Last Path: e.g. Childfolder2");
        string inputPathVol = Console.ReadLine();
        if (inputPath.Length>5)  {
            folderPath = inputPath;   }
        string[] pdfFiles = Directory.GetFiles(folderPath, "*.pdf");
        foreach (string file in pdfFiles)
        {
            Uri fileUri = new Uri(file);
            string directory = Path.GetDirectoryName(file);
            string filename = Path.GetFileNameWithoutExtension(file);
            string extension = Path.GetExtension(file);           
            string newFilename = $"{filename}_new{extension}";  // Create the new filename
            string newFilePath = Path.Combine(directory, newFilename); // Combine the directory and new filename to form the new URL
            UpdatePdf(fileUri.AbsoluteUri,newFilePath, inputPathVol);
        }
    }
private static void UpdatePdf(string inputFilePath, string outputFilePath, string lastPathPart)
{
    var varInnerName = "";
    PdfDocument pdfDoc = new PdfDocument(new PdfReader(inputFilePath), new PdfWriter(outputFilePath));
        for (int i = 1; i <= pdfDoc.GetNumberOfPages(); i++)        // Iterate through the pages
        {
            var page = pdfDoc.GetPage(i);
            var annotations = page.GetAnnotations();
            foreach (var annotation in annotations)            // Iterate through the annotations
            {
                if (annotation.GetSubtype().Equals(PdfName.Link))
                {
                    var linkAnnotation = (PdfLinkAnnotation)annotation;
                    var action = linkAnnotation.GetAction();                                                        
                    if (action is PdfDictionary dictionary)
                    {
                        foreach (var key in dictionary.KeySet())
                        {
                            var value = dictionary.Get(key);
                            Console.WriteLine($"{key}: {value}");
                            var varPdfNameF = dictionary.Get(PdfName.F);
                            if (varPdfNameF is PdfDictionary varF2Dict)      {
                                varInnerName = varF2Dict.Get(PdfName.F).ToString();  }
                        }
                    }
                    else
                    {   Console.WriteLine("No URL found.");    }  
if (action != null && (action.Get(PdfName.S).Equals(PdfName.GoToR) || action.Get(PdfName.S).Equals(PdfName.Launch)))                
                      {
                        var varF = action.GetAsString(PdfName.F)?.ToString() ?? "";
                        var uri = $"https://radimaging.sharepoint.com/sites/Documents/Standards/{lastPathPart}/{varInnerName}";          
                       string pattern = @"\.\./";
                       if(uri.Contains("../"))    // string cleanUrl1 = Regex.Replace(uri, pattern, string.Empty);
                       {          
                        uri = $"https://radimaging.sharepoint.com/sites/Documents/Standards/{varInnerName}";
                        uri= Regex.Replace(uri, pattern, string.Empty);                       
                       }
                        var newAction = PdfAction.CreateURI(uri);
                        if (varF.Length > 20)      {
                            newAction = PdfAction.CreateURI(varF);     }
                        linkAnnotation.SetAction(newAction);
                    }           
                }
            }
        }
        pdfDoc.Close();
        Console.WriteLine("PDF links updated successfully!");
    }
}


Quick way to use the PDF X-Change Editor tool to change links.


Sunday, 13 October 2024

Document Retention: 6 years is always a solid guess

Overview: Documents need to be retained for specific periods and disposed of at a certain point. 

  1. PAYE Records: 3 years after the end of the tax year to which they relate
  2. Banking records: 6 years
  3. Intellectual Property: 6 years
  4. Donations: 3 or 6 years
  5. Employment Records: Generally, 6 years
  6. Health & Safety Policy: The life of the company and historic versions, previous versions  should be kept for a minimum of ten years
  7. Most other Health and Safety Documents: Retain for 6 years if there are updates.
  8. Accidents and injuries: at least 3 years from the date of reporting
  9. Pension scheme records:  life of Pension scheme +
  10. Hazardous waste: 3 years
  11. Data Protection Act (personal data): Delete as soon as possible
  12. Company Documents

  • Key company documents: Articles of Association, certificate of incorporation, memorandum of association, stakeholder official documents,  ...:  retained for the life of the company. 
  • Directors' contracts & Directors’  qualifying indemnity provisions:  six years after termination or expiry.
  • Annual Budgets, quarterly financial reports, VAT records, Statutory Report and Accounts:  6 years
  • Corporation Tax records: 7 years after filing
  • Contract for purchase of own shares, labour agreements: 10 years from the buying date
  • Property Records: 15 years after expiry
Note: This is my rough guide, as lots of people love talking about Document Retention.