<?xml version="1.0" encoding="utf-8"?>
<AlvaoApplication xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" ModelVersion="1">
  <Applications>
    <Application id="29">
      <Name>Automatic device warranty discovery</Name>
      <Description>The application periodically retrieves warranties for Dell devices from the support system using their serial numbers (service tags). To ensure the application functions correctly, request client_id and client_secret from the Dell TechDirect API team and enter them into the DellWarrantyApi.ClientId and DellWarrantyApi.ClientSecret settings in the Advanced settings.</Description>
      <UniqueId>0078649d-c4d5-4637-8f0b-e5dcb1169cf2</UniqueId>
      <Version>3</Version>
      <AdvancedSettings>
        <Setting>
          <Name>Vendors.Dell.WarrantyApi.ClientId</Name>
          <Value />
        </Setting>
        <Setting>
          <Name>Vendors.Dell.WarrantyApi.ClientSecret</Name>
          <Value />
        </Setting>
      </AdvancedSettings>
      <Scripts>
        <Script id="58">
          <Name>LoadWarrantyPeriodicAction</Name>
          <Code>using System;
using System.Data;
using Microsoft.Data.SqlClient;
using Alvao.Global;
using Alvao.API.Common;
using Alvao.Apps.API;
using Alvao.API.AM;
using Alvao.API.Common.Model.Database;
using Alvao.Context;
using System.Collections.Generic;
using Dapper;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Linq;
using Newtonsoft.Json;

public class LoadWarrantyPeriodicAction : IPeriodicAction
{
    public string Name 
    { 
        get =&gt; "LoadWarrantyPeriodicAction";
        set { }
    }

    public void OnPeriod(SqlConnection con)
    {
        var logger = Alvao.API.Internal.TenantDiagnosticsLog.Get();
        if(!Activation.IsModuleActivated(ModuleInfo.ModuleId.AMCustomApps))
        {
            logger.Warn($"Automatic device warranty discovery cannot be used. Module AM Custom Apps is not activated.");
            return;
        }
        
        if(DateTime.Now.Hour != 0)
            return;
        
        var integrations = GetVendorIntegrations();
        foreach(var integration in integrations)
        {
            try {
                logger.Info($"Loading warranty data for {integration.VendorName} devices.");
                var infos = integration.GetDevicesWarrantyInfo();
                foreach(var info in infos)
                {
                    ObjectProperty.Update(info.NodeId, tblKind.KindCode.WarrantyUntil, info.WarrantyUntil, true, true);
                    var supportServices = string.Join(";", info.ActiveServiceLevelDescriptions ?? Enumerable.Empty&lt;string&gt;());
                    ObjectProperty.Update(info.NodeId, tblKind.KindCode.SupportServices, supportServices, true, true);
                }
                logger.Info($"Successfully loaded warranty data for {infos.Count()} devices.");
            }
            catch(Exception ex)
            {
                logger.Error(ex, $"Error while loading warranty data for {integration.VendorName} devices: {ex.Message}");
            }
        }
    }   

    internal static IEnumerable&lt;IVendorWarrantyIntegration&gt; GetVendorIntegrations()
    {
        var vendorInterface = typeof(IVendorWarrantyIntegration);
        var assemblies = AppDomain.CurrentDomain.GetAssemblies();
        var integrations = new List&lt;IVendorWarrantyIntegration&gt;();
        foreach(var assembly in assemblies)
        {
            foreach(var at in assembly.GetTypes())
            {
                if (vendorInterface.IsAssignableFrom(at))
                {
                    if (!at.IsInterface)
                        integrations.Add((IVendorWarrantyIntegration)Activator.CreateInstance(at));
                }
            }
        }
 
        return integrations;
    }
}

public interface IVendorWarrantyIntegration 
{
    public string VendorName { get; }
    public IEnumerable&lt;DeviceWarrantyInfo&gt; GetDevicesWarrantyInfo();
}

public class DeviceWarrantyInfo
{
    public int NodeId { get; set; }
    public DateTime? WarrantyUntil { get; set; }
    public IEnumerable&lt;string&gt; ActiveServiceLevelDescriptions { get; set; } = Enumerable.Empty&lt;string&gt;();
}

public class DellWarrantyIntegration : IVendorWarrantyIntegration 
{
    private static readonly HttpClient client = new HttpClient();
    private const int BatchSize = 100; // 100 = Dell Api limit
    private const string DellApiUrl = "https://apigtwb2c.us.dell.com/PROD/sbil/eapi/v5";
    public string VendorName =&gt; "Dell";

    public IEnumerable&lt;DeviceWarrantyInfo&gt; GetDevicesWarrantyInfo()
    {
        var results = new List&lt;DeviceWarrantyInfo&gt;();

        var devices = GetDellDeviceServiceTags();
        int currentBatchSize = 0;
        int processed = 0;
        do {
            var batch = devices.Skip(processed).Take(BatchSize).ToList();
            currentBatchSize = batch.Count();

            var batchInfos = LoadWarrantiesFromApi(batch);
            results.AddRange(batchInfos);

            processed += currentBatchSize;
        }
        while(currentBatchSize &gt; 0);

        return results;
    }
    
    private IEnumerable&lt;DeviceWarrantyInfo&gt; LoadWarrantiesFromApi(IEnumerable&lt;Device&gt; devices)
    {
        if(!devices.Any())
            return Enumerable.Empty&lt;DeviceWarrantyInfo&gt;();

        var serialNumberDeviceDict = devices.ToDictionary(d =&gt; d.SerialNumber, d =&gt; d);
        var infoByTag = devices.ToDictionary(
            d =&gt; d.SerialNumber,              
            d =&gt; new DeviceWarrantyInfo       
            {                                 
                NodeId = d.Id,                
                WarrantyUntil = null,         
                ActiveServiceLevelDescriptions = Enumerable.Empty&lt;string&gt;()
            });                                
            
        string tags = string.Join(",", devices.Select(d =&gt; d.SerialNumber));
        var httpRequest = new HttpRequestMessage(HttpMethod.Get, $"{DellApiUrl}/asset-entitlements?servicetags={tags}");
        httpRequest.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", GetToken());
        var response = client.SendAsync(httpRequest).Result;
        var responseContent = response.Content.ReadAsStringAsync().Result;
        if (response.IsSuccessStatusCode)
        {
            var apiResponse = JsonConvert.DeserializeObject&lt;IEnumerable&lt;DellWarranty&gt;&gt;(responseContent) ?? Enumerable.Empty&lt;DellWarranty&gt;();

            foreach(var warranty in apiResponse)
            {
                if(!serialNumberDeviceDict.ContainsKey(warranty.ServiceTag))
                {
                    continue;
                }

                var info = infoByTag[warranty.ServiceTag];
                info.WarrantyUntil = warranty.GetWarranty();
                info.ActiveServiceLevelDescriptions = warranty.GetActiveServiceLevelDescriptions(DateTime.UtcNow);
            }

            return infoByTag.Values;

        }
        else 
            throw new Exception($"Unsuccessful response ({response.StatusCode}) received from Dell Api: {responseContent}");
    }


    static string GetToken()
    {
        using (var client = new HttpClient())
        {
            var request = new HttpRequestMessage(HttpMethod.Post, "https://apigtwb2c.us.dell.com/auth/oauth/v2/token");
            var requestBody = new FormUrlEncodedContent(new[]
            {
            new KeyValuePair&lt;string, string&gt;("grant_type", "client_credentials"),
            new KeyValuePair&lt;string, string&gt;("client_id", DbProperty.VendorsDellWarrantyApiClientId),
            new KeyValuePair&lt;string, string&gt;("client_secret", DbProperty.VendorsDellWarrantyApiClientSecret)
            });

            request.Content = requestBody;
            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            var response = client.Send(request);
            if(!response.IsSuccessStatusCode)
            {
                throw new Exception("Invalid credentials provided. Check Vendors.Dell.WarrantyApi.ClientId and Vendors.Dell.WarrantyApi.ClientSecret.");
            }

            var responseContent = response.Content.ReadAsStringAsync().Result;
            var tokenResponse = Newtonsoft.Json.JsonConvert.DeserializeObject&lt;TokenResponse&gt;(responseContent);

            return tokenResponse.AccessToken;
        }
    }
    public class TokenResponse
    {
        [Newtonsoft.Json.JsonProperty("access_token")]
        public string AccessToken { get; set; }
    } 

    private IEnumerable&lt;Device&gt; GetDellDeviceServiceTags()
    {
        using var scope = AlvaoContext.GetConnectionScope();
        return scope.Connection.Query&lt;Device&gt;(
            @"select NodeId as Id, SerialNumber 
            from NodeCust nc
            join tblNode n on nc.NodeId = n.intNodeId
            where Manufacturer like 'Dell%' and SerialNumber is not null and n.IsDiscarded = 0 and n.IsRemoved=0", null, scope.Transaction);
    }

    public class DellWarranty 
    {
        public string ServiceTag { get; set; }
        public IEnumerable&lt;Entitlement&gt; Entitlements { get; set; }

        public DateTime? GetWarranty()
        {
            return Entitlements.Any() ? Entitlements.Max(e =&gt; e.EndDate) : null;
        }

        public IEnumerable&lt;string&gt; GetActiveServiceLevelDescriptions(DateTime utcNow)
        {
            if (Entitlements == null)
                return Enumerable.Empty&lt;string&gt;();

            IEnumerable&lt;string&gt; activeDescriptions = Entitlements
                .Where(e =&gt; e != null &amp;&amp; ToUtcSafe(e.EndDate) &gt; utcNow)
                .Select(e =&gt; e.ServiceLevelDescription)
                .Where(s =&gt; !string.IsNullOrWhiteSpace(s));
            return activeDescriptions;
        }

        private static DateTime ToUtcSafe(DateTime dt)
        {
            return dt.Kind == DateTimeKind.Utc ? dt : dt.ToUniversalTime();
        }
    }

    public class Entitlement 
    {
        public DateTime EndDate { get; set; }
        public string ServiceLevelDescription { get; set; }
    }
}

public class Device 
{
    public int Id { get; set; }
    public string SerialNumber { get; set; }
    public DateTime? Warranty { get; set;}
}</Code>
          <IsLibCode>false</IsLibCode>
          <Codesign>sHZ4vJZq2OU9NDgRNk3zXbYPzdxiaXiC/1OQgeu9b7cW13Vj/bLJYPt1WkSxbYb7CeBd/gnfZYk7U5+SXBcLf0We8MdLOOZAnmA/zEe6DGXpc2jcDEj9rrGRAqv078BScoHCyZMDDXeh/3mjwhJu5tm4+YILY1qZ6A2yi2HHXi5NRSP6P34B9u4tpdynqixma3cdanCYf8UuRKTqwCTm9KKuJGX9tAwSE7LQ8PulHBNLblmZ976bVC4kWWzdUjor2fjC2lmt7OB9zU1HIjtd+nhpnYu+1uQbgT+QW9c9knPGp+MDur4gidrp7Vm/r1reEEi8t/5eus9SKE9H9cd6sQ==</Codesign>
        </Script>
      </Scripts>
    </Application>
  </Applications>
</AlvaoApplication>