Appendix A: Code Example of Shopify Connector as a Custom Console Application

  • Updated

Below you will find a C# code example of a console application, which uses Perfion Shopify Connector manager library with a custom logger and which uses most of the features the manager offers. The code can be adjusted and applied for other type applications.

All libraries one needs to use in any project which uses Connector manager can be found in Connector installation folder and directly linked to the project.

The Connector manager implements “IConnectorManager” interface as shown below. It allows subscribing to four events, which will be triggered at any important Connector manager related actions. The manager can also be started and stopped, and finally, one can get manager’s current status.

Console application – Perfion Connector IConnectorManager interface
public interface IConnectorManager {
  event EventHandler<ConnectorStateEventArgs> StateChangeEvent;
  event EventHandler<ConnectorErrorStateEventArgs> ErrorStateChangeEvent;
  event EventHandler<ConnectorSyncTypeEventArgs> SyncTypeChangeEvent;
  event EventHandler<ConnectorSyncProgressEventArgs> SyncProgressChangeEvent;
  void Start( bool runOnce = false );
  void Stop();
  ConnectorStatus GetStatus();
}

The Connector manager can also use a custom logger. The logger must implement Perfion “ILogger” interface.

Console application – Perfion Connector ILogger interface
public enum LogType { Error, Warning, Progress, Info, Debug }
public interface ILogger {
  LogType LogLevel { get; set; }
  void Log( string message, LogType logType );
  void LogDebug( string message );
  void LogInfo( string message );
  void LogProgress( string message );
  void LogWarning( string message );
  void LogError( string message );
  void LogException( string message, Exception ex );
}

The example console application will use a custom logger “ExampleLogger”, which allows to intercept all log messages and then sends them via event. The “ExampleLogger” is initialized by specifying the maximum log message type it will accept and then it will log only those messages, which match this predefined level. For example, if “Progress” level is selected as a log level, then the logger will log only messages of type “Error”, “Warning” and “Progress”. Setting “Debug” as log level will allow to log messages of all types.

The “ExampleLogger” is shown only as an example, but it can be quickly modified to write log messages to the file, database, etc.

Console application – ExampleLogger example
using PerfionLogger;
using System;
namespace ShopifyConsoleAppExample {
  public class LoggerEventArgs : EventArgs {
    DateTime date;
    public DateTime Date { get { return date; } }
    LogType logLevel;
    public LogType LogLevel { get { return logLevel; } }
    string message;
    public string Message { get { return message; } }
    public LoggerEventArgs( LogType logType, string message ) {
      date = DateTime.Now;
      logLevel = logType;
      this.message = message;
    }
  }
  public class ExampleLogger : ILogger {
    static readonly object lockObj = new object();
    public event EventHandler<LoggerEventArgs> LogEvent;
    LogType logLevel;
    public LogType LogLevel { get { return logLevel; } set { logLevel = value; } }
    public ExampleLogger( LogType logType ) {
      logLevel = logType;
    }
    public ExampleLogger( string logType ) {
      if( !Enum.TryParse( logType, true, out logLevel ) ) {
        logLevel = LogType.Progress;
      }
    }
    public void LogDebug( string message ) {
      Log( message, LogType.Debug );
    }
    public void LogInfo( string message ) {
      Log( message, LogType.Info );
    }
    public void LogProgress( string message ) {
      Log( message, LogType.Progress );
    }
    public void LogWarning( string message ) {
      Log( message, LogType.Warning );
    }
    public void LogError( string message ) {
      Log( message, LogType.Error );
    }
    public void LogException( string message, Exception ex ) {
      message = $"{message}. Error: {ex}";
      Log( message, LogType.Error );
    }
    public void Log( string message, LogType logType ) {
      try {
        lock( lockObj ) {
          if( logType > logLevel ) return; // Control which messages are logged by using logLevel
          LoggerEventArgs args = new LoggerEventArgs( logType, message );
          LogEvent?.Invoke( null, args );
        }
      } catch { }
    }
  }
}

The example console application does the following:

  • Reads parameters from configuration file
  • Initializes a custom “ExampleLogger” logger and subscribes to its event “LogEvent” for printing log messages to the console.
  • Initializes Connector manager. Note, the manager uses three loggers, but in this example we use only one logger. The manager consist of three interfaces: Perfion ECommerce API, Shopify API and manager itself, which uses those first two together to get everything in sync. These three interfaces may each have their own logger, but one can also use the same single logger for all interfaces.
  • Subscribes to all manager’s events to print all actions performed by manager to the console in real time. All the methods used to handle manager’s events will further show how one can access various types of manager state information.
  • Starts the manager in a new background thread using user’s chosen mode.
  • Starts the manager monitoring process, which every few seconds retrieves information from manager and in case it is in sleep mode, present how long it will take for manager to wake up. In case the manager is stopped, the console application will be terminated.
Console application – Main method
using PerfionConnectorManager;
using PerfionServiceUtils;
using System;
using System.Linq;
using System.Threading;
using ShopifySyncManager;
using static PerfionConnectorManager.ConnectorStatus;
 namespace ShopifyConsoleAppExample {
  class Program {
    static void Main( string[] args ) {
      // Use parameters from configuration file
      var settings = Properties.Settings.Default;
      var connectorConfig = new ShopifyConnectorConfigParams {
        ECBaseUrl = settings.ECBaseURL,
        ProgressReportStepInPct = settings.ProgressReportStepInPct,
        PerfionChannelName = settings.PerfionChannelName,
        ShopifyTimeoutInSec = settings.ShopifyTimeoutInSec,
        ShopifyRetryCount = settings.ShopifyRetryCount,
        ECTimeoutInSec = settings.ECTimeoutInSec,
        ECRetryCount = settings.ECRetryCount,
        ECDataChunkSize = settings.ECDataChunkSize,
        EnableStatistics = settings.EnableStatistics
      };
       // Initialize logger
      var logger = new ExampleLogger( settings.LogType );
      logger.LogEvent += new EventHandler<LoggerEventArgs>( logUpdateEvent );
       // Initialize manager
      IConnectorManager manager =
        new ShopifySynchronizationManager( connectorConfig, logger, logger, logger);
       // Subscribe to manager's events
      manager.StateChangeEvent += new EventHandler<ConnectorStateEventArgs>( stateUpdateEvent );
      manager.ErrorStateChangeEvent +=
        new EventHandler<ConnectorErrorStateEventArgs>( errorStateUpdateEvent );
      manager.SyncTypeChangeEvent += new EventHandler<ConnectorSyncTypeEventArgs>( syncStateUpdateEvent );
      manager.SyncProgressChangeEvent +=
        new EventHandler<ConnectorSyncProgressEventArgs>( syncProgressUpdateEvent );
       // Get user's chosen manager running mode
      bool runOnce = false;
      if( args != null && args.Length > 0 ) {
        runOnce = args.Any( arg => string.Compare( arg, "-runonce", ignoreCase: true ) == 0 );
      }
       // Start manager in a new thread
      startManager( manager, runOnce );
       // Retrieve information from manager while it is running
      writeLineToOutput( $"Manager mode: {manager.GetStatus()?.ManagerMode}" );
      while( true ) {
        var status = manager.GetStatus();
        if( status.ConnectorState == State.Sleeping ) {
          // When Connector is sleeping, this will report remaining sleep time
          var sleepInfo = status.GetSleepInfo();
          string timeLeftToSleep = sleepInfo?
            .GetTimeUntilWakeupAsString( false, Utils.TimeIntervalResolution.Seconds );
          writeLineToOutput( $"    Time until wakeup: {timeLeftToSleep}" );
        }
        if( status.ConnectorState == State.Stopped ) break; // Exit application when manager stops
         Thread.Sleep( 5000 );
      }
    }
     /// <summary> Start manager in a new background thread </summary>
    static void startManager( IConnectorManager manager, bool runOnce ) {
      var syncThread = new Thread( () => manager.Start( runOnce ) ) {
        Priority = ThreadPriority.BelowNormal,
        IsBackground = true
      };
      syncThread.Start();
    }
     /// <summary> Triggered when manager updates its state </summary>
    static void stateUpdateEvent( object sender, ConnectorStateEventArgs args ) {
      string info = $"[State update] {args.PreviousState} => {args.NewState}";
      writeLineToOutput( info );
      if( args.NewState == State.Sleeping ) {
        // When manager changes its state to Sleeping, show when it will wake up again
        var sInfo = args.CopyOfConnectorStatus.GetSleepInfo();
        string wakeUpIn = sInfo.GetTimeUntilWakeupAsString( false, Utils.TimeIntervalResolution.Seconds );
        string info2 = $"  Will wake up after: {wakeUpIn}";
        writeLineToOutput( info2 );
      }
    }
     /// <summary> Triggered when manager updates its error state </summary>
    static void errorStateUpdateEvent( object sender, ConnectorErrorStateEventArgs args ) {
      string info = $"[Error state update] {args.PreviousErrorState} => {args.NewErrorState}";
      writeLineToOutput( info );
    }
     /// <summary> Triggered when manager updates its sync state </summary>
    static void syncStateUpdateEvent( object sender, ConnectorSyncTypeEventArgs args ) {
      string info = $"  [Sync state update] {args.PreviousSyncType} => {args.NewSyncType}";
      writeLineToOutput( info );
    }
     /// <summary> Triggered when manager updates product or category synchronization progress </summary>
    static void syncProgressUpdateEvent( object sender, ConnectorSyncProgressEventArgs args ) {
      var sInfo = args.CopyOfConnectorStatus.GetSyncInfo();
      if( args.PreviousProgress < 0 + float.Epsilon ) {
        // Run once after synchronization starts for category or for product
        // Show how many items there were selected for synchronization, e.g. new/updated/deleted counts
        int newCount = sInfo.GetSyncNewItemsCount();
        int updateCount = sInfo.GetSyncUpdatedItemsCount();
        int deleteCount = sInfo.GetSyncDeletedItemsCount();
        string syncScopeInfo = $"    [Sync progress update]
          Add: {newCount}, update: {updateCount}, delete: {deleteCount}";
        writeLineToOutput( syncScopeInfo );
      }
       // Report progress after a single item have finished sync and also total item success/failure status
      float prevProgress = args.PreviousProgress;
      float newProgress = args.NewProgress;
      int totalSuccededCount = sInfo.GetSyncSuccessItemsCount();
      int totalFailedCount = sInfo.GetSyncFailedItemsCount();
      string lastItemSucceeded = ( args.ItemSyncSuccess == null )
        ? "" : $", last item succeeded: {args.ItemSyncSuccess}";
      string progressInfo = $"    [Sync progress update]
        {prevProgress} => {newProgress}{lastItemSucceeded} " +
        $"(OK: {totalSuccededCount}, failed: {totalFailedCount})";
      writeLineToOutput( progressInfo );
    }
     /// <summary> Triggered when manager creates a new log message </summary>
    static void logUpdateEvent( object sender, LoggerEventArgs args ) {
      string date = args.Date.ToString( "yyyy'-'MM'-'dd' 'HH':'mm':'ss" );
      string message = $"[{date}][{args.LogLevel}] {args.Message}";
      // writeLineToOutput( message );
    }
     /// <summary> Write line to console </summary>
    static void writeLineToOutput( string text ) {
      Console.WriteLine( text );
    }
  }
}

The console application uses configuration file to supply Connector manager with needed parameters. Below you can see an example of such configuration file.

Console application – configuration file example
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
      <section name="ShopifyConsoleAppExample.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    </sectionGroup>
  </configSections> 
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/>
  </startup>
  <applicationSettings>
    <ShopifyConsoleAppExample.Properties.Settings>
      <setting name="ECBaseURL" serializeAs="String">
        <value>http://192.168.10.20:8080/perfion/getdata.asmx</value>
      </setting>
      <setting name="LogDirectory" serializeAs="String">
        <value>C:\Services\ShopifyService\LogDir\</value>
      </setting>
      <setting name="ServiceName" serializeAs="String">
        <value>ShopifyServiceTest</value>
      </setting>
      <setting name="LogType" serializeAs="String">
        <value>Info</value>
      </setting>
      <setting name="PerfionChannelName" serializeAs="String">
        <value>Shopify Channel A</value>
      </setting>
      <setting name="ShopifyTimeoutInSec" serializeAs="String">
        <value>60</value>
      </setting>
      <setting name="ShopifyRetryCount" serializeAs="String">
        <value>3</value>
      </setting>
      <setting name="ECTimeoutInSec" serializeAs="String">
        <value>300</value>
      </setting>
      <setting name="ECRetryCount" serializeAs="String">
        <value>3</value>
      </setting>
      <setting name="ECDataChunkSize" serializeAs="String">
        <value>25</value>
      </setting>
      <setting name="ProgressReportStepInPct" serializeAs="String">
        <value>10</value>
      </setting>
      <setting name="EnableStatistics" serializeAs="String">
        <value>False</value>
      </setting>   
    </ShopifyConsoleAppExample.Properties.Settings>
  </applicationSettings>
</configuration>
 

Was this article helpful?

0 out of 0 found this helpful

Comments

0 comments

Please sign in to leave a comment.