External Storage Plugin for the .NET RESTful Engine

The RESTful Engine is designed to use the computer's file system as a repository for all the files produced during the report generation process. If, however, you require something different for your setup, say using an AWS S3 bucket to store all the files, we provide the IRepository interface. This article will detail how to get started with the IRepository interface, as well as provide details surrounding the methods defined in the interface. You can see our sample S3 Repository plugin that implements the IRepository interface on our github.

important

Note: If you are using VMs or containers with a load balancer, we recommend you set up a repository for object storage services like AWS S3 or Azure Storage.

Requirements#

In order to implement your own repository, you will need:

  • WindwardRepository.dll
  • WindwardModels.dll

These DLLs will be in your .NET RESTful Engine bin folder.

Setting it Up#

  1. Create a new C# project and add the dlls mentioned to the project references. Then include the libraries to your class file, and define a class that implements IRepository:
using WindwardRepository;
using WindwardModels;
namespace CustomRepo
{
public class Repository : IRepository
.
.
.
  1. You will then need to implement the following repository methods (for more information about these methods please read the "Methods and Variables" section of this article):

  2. You will then need to implement the following repository methods (for more information about these methods please read the "Methods and Variables" section of this article):

    • void SetJobHandler(IJobHandler handler)
      • The SetJobHandler method sets the job handler for the RESTful engine
    • String CreateRequest(Template template, RepositoryStatus.REQUEST_TYPE requestType)
      • The CreateRequest method is used to add report processing requests to the processing queue
    • void SaveError(Template template, ServiceError error)
      • The SaveError method saves an error that occurred during execution of a template
    • ServiceError GetError(String guid)
      • The GetError method is used to retrieve an error associated with a specific request
    • void SaveReport(Template template, Document document)
      • The SaveReport method saves a completed document generation request
    • Document GetReport(String guid)
      • The GetReport method retrieves the document object associated with the passed GUID
    • RepositoryRequest TakeRequest()
      • The TakeRequest method gets the pending jobs and sets them to processing
    • RequestStatus GetReportStatus(string guid)
      • The GetReportStatus method returns the status of the current request associated with the passed GUID
    • DocumentMeta GetReportMeta(String guid)
      • The GetReportMeta method retrieves the documentMeta object associated with the passed GUID
    • void SaveMetrics(Template template, Metrics metrics)
      • The SaveMetrics method saves a completed metrics request
    • Metrics GetMetrics(String guid)
      • The GetMetrics method retrieves the metrics object associated with the passed GUID
    • void SaveTagTree(Template template, TagTree tagtree)
      • The SaveTagTree method saves a completed tagtree request
    • Metrics GetTagTree(String guid)
      • The GetTagTree method retrieves the tagtree object associated with the passed GUID
    • void DeleteReport(String guid)
      • The DeleteReport method is used to delete a generated document from the repository using its guid as the identifier
    • void SaveTemplate(CachedObject template) method saves template
      • The SaveTemplate takes in CachedTemplate and adds it to the template repository.
    • CachedTemplate GetTemplate(string templateID) method returns a stored template
      • The GetTemplate method retrieves a stored template from the template repository and returns a CachedTemplate.
    • void DeleteTemplate(string templateID) method deletes template from template storage
      • The DeleteTemplate method deletes the template with the associated TemplateID from the template repository.
    • void ShutDown()
      • The Shutdown method reverts all jobs in processing to pending (waiting to start). The web server is closing down
  3. Once you implement those methods in your repository project, compile it down to a jar

  4. Reference the jar in your RESTful engine's WindwardReports.properties file

Methods and Variables#

This is a list of the variables and methods you will need to implement your version of IRepository.

note

Please note that the code snippets provided for some of the methods bellow are only provided as an aid / guide for you as you define your repository. Also note that you need these methods and variables for the base implementation. Feel free to use any variable or helper methods you need to achieve what you need to achieve.

JobHandler Variable#

private IJobHandler JobHandler;

The job handler used to manage the requests made to the RESTful engine.

SetJobHandler Method#

Method definition:

void SetJobHandler(IJobHandler handler)

Input parameters:

  1. IJobHandler handler
    - The job handler that will process requests in this repository
    The SetJobHandler method sets the job handler for the RESTful engine. You should define the following setters and getters in your repository class:
private IJobHandler JobHandler { get; set; }

Then just call the setter in this method:

public void SetJobHandler(IJobHandler handler){
JobHandler = handler;
}

CreateRequest Method#

Method definition:

string CreateRequest(Template template, RepositoryStatus.REQUEST_TYPE requestType)

Input parameters:

  1. Template template
    • The template that requires processing
  2. RepositoryStatus.REQUEST_TYPE requestType
    • What type of processing does this template need (ex. document, metrics, tagtree)

The CreateRequest method is used to add processing requests to the processing queue. The method returns the Guid (or unique identifier) for the template as a string. In here you want to assign the template a GUID as so:

template.Guid = Guid.NewGuid().ToString();

Before returing the GUID, make sure to signal the job handler:

JobHandler?.Signal();
note

Note that you have access to all the models used in these methods (like ServiceError, Template, Document, etc.) For a full list of the models, check our swagger documentation. You can use these to serialize / deserialize objects, especially when it comes to the Save/Get methods bellow.

SaveError Method#

Method definition:

void SaveError(Template template, ServiceError error)

Input parameters:

  1. Template template
    • The template that failed during processing
  2. ServiceError error
    • An exception that occured attempting to execute a request.

The SaveError method saves an error that occurred during execution of a template. You can do this however you want; you can save it as an entry in your database, you can save it as a file, whatever works for you. As an example, the default FileSystemRepository implemented in the RESTful engine serializes the errors as data contracts to files titled {template.guid}.error. Just make sure however you save it, you use the template's guid as an identifier (name of the file, id entry in database, etc.).

GetError Method#

Method definition:

ServiceError GetError(String guid)

Input parameters:

  1. guid
    • The unique identifier for this request.

The GetError method is used to retrieve an error associated with a specific request. The method returns a ServiceError object. Use the guid input parameter as an identifier to find the error however you chose to save it in the SaveError method.

SaveReport Method#

Method definition:

void SaveReport(Template template, Document document)

Input parameters:

  1. Template template
    • The template associated with this request.
  2. Document document
    • The generated report.

The SaveReport method saves a completed document generation request. You can save the document object however you want, as long as it is easily identifiable using its GUID. As an example, the default FileSystemRepository implemented in the RESTful engine serializes the errors as data contracts to files titled " {template.guid}.docGen.complete". Just make sure however you save it, you use the template's guid as an identifier (name of the file, id entry in database, etc.).

GetReport Method#

Method definition:

Document GetReport(String guid)

Input parameters:

  1. guid
    • The unique identifier for this request.

The GetReport method retrieves the document object associated with the passed GUID. Use the GUID input parameter as an identifier to find the document object however you chose to save it in the SaveReport method.

TakeRequest Method#

Method definition:

RepositoryRequest TakeRequest()

The TakeRequest method gets pending jobs and sets them to processing (we save pending jobs with ".pending" extension and change it to ".generating" extension. You then want to return a RepositoryRequest object. These are the two constructors you can use for RepositoryRequest:

public RepositoryRequest(Template template, REQUEST_TYPE type);
public RepositoryRequest(RepositoryRequest src);

These are the possible REQUEST_TYPE depending on the job:

public static enum REQUEST_TYPE {
Unknown,
DocGen,
DocStream,
DocGenMeta,
Metrics,
TagTree;

If there are no pending jobs, this method returns null.

GetReportStatus Method#

Method definition:

public RequestStatus GetReportStatus(string guid)

Input parameters:

  1. guid
    • The unique identifier for this request.

The GetReportStatus method returns the status of a job (identified by its GUID). This method returns a RequestStatus object:

public RequestStatus(RepositoryStatus.JOB_STATUS jobStatus, RepositoryStatus.REQUEST_TYPE requestType)
{
JobStatus = jobStatus;
RequestType = requestType;
}

Where JobStatus is an enum:

public enum JOB_STATUS
{
Lock, //( A lock file ie. not really a job)
Pending, //(Waiting to run)
Generating, //(Presently running.)
Complete, //(Request is complete, successful)
Error //(Request had an error. Complete but failed)
}

and RequestType is an enum:

public enum REQUEST_TYPE
{
Unknown, //(Should only be set for JOB_STATUS == Error)
DocGen, //(Generating a document)
DocStream, //(Retrieve generated report as a stream)
DocGenMeta, //(Retrieve document "meta" data)
Metrics, //(Calling GetMetrics)
TagTree //(Calling GetTagTree)
}

As an example, the way we have it setup, when a document generation job is complete it will save the file with the following extention: "docgen.complete". So when we call the GetReportStatus method on this job, it will return the following RequestStatus object:

new RequestStatus(RepositoryStatus.JOB_STATUS.Complete, RepositoryStatus.REQUEST_TYPE.DocGen)

GetReportMeta Method#

Method definition:

DocumentMeta GetMeta(String guid)

Input parameters:

  1. guid
    • The unique identifier for this request.

The GetMeta method retrieves the documentMeta object associated with the passed GUID. This method works slightly differently than other methods, as it basically make use of the already existing document object, and strips the necessary information from it. For this method we recommend you implement it like we did (with the associated helper method) or at least implement something with similar functionality:

public DocumentMeta GetReportMeta(string guid)
{
Document document = GetReport(guid);
DocumentMeta documentMeta = SetReportMeta(document);
return documentMeta;
}
internal static DocumentMeta SetReportMeta(Document genDoc)
{
DocumentMeta largeDoc = new DocumentMeta();
largeDoc.Guid = genDoc.Guid;
largeDoc.NumberOfPages = genDoc.NumberOfPages;
largeDoc.ImportInfo = genDoc.ImportInfo;
largeDoc.Tag = genDoc.Tag;
largeDoc.Errors = genDoc.Errors;
if(genDoc.Pages == null){
Uri url = HttpContext.Current.Request.Url;
string tempUri = url.AbsoluteUri.ToString();
tempUri = tempUri.Substring(0, tempUri.Length - 4);
largeDoc.Uri = tempUri + "file";
}
return largeDoc;
}

SaveMetrics Method#

Method definition:

void SaveMetrics(Template template, Metrics metrics)

Input parameters:

  1. Template template
    • The template associated with this request.
  2. Metrics metrics
    • The generated metrics.

The SaveMetrics method saves a completed metrics request. You can save the metrics object however you want, as long as it is easily identifiable using its GUID. As an example, the default FileSystemRepository implemented in the RESTful engine serializes the errors as data contracts to files titled " {template.guid}.metrics.complete". Just make sure however you save it, you use the template's guid as an identifier (name of the file, id entry in database, etc.).

GetMetrics Method#

Method definition:

Metrics GetMetrics(String guid)

Input parameters:

  1. guid
    • The unique identifier for this request.

The GetMetrics method retrieves the metrics object associated with the passed GUID. Use the GUID input parameter as an identifier to find the metrics object however you chose to save it in the SaveMetrics method.

SaveTagTree Method#

Method definition:

void SaveTagTree(Template template, TagTree tagtree)

Input parameters:

  1. Template template
    • The template associated with this request.
  2. TagTree tagtree
    • The generated tagtree.

The SaveTagTree method saves a completed tagtree request. You can save the tagtree object however you want, as long as it is easily identifiable using its GUID. As an example, the default FileSystemRepository implemented in the RESTful engine serializes the errors as data contracts to files titled " {template.guid}.tagtree.complete". Just make sure however you save it, you use the template's guid as an identifier (name of the file, id entry in database, etc.).

GetTagTree Method#

Method definition:

Metrics GetTagTree(String guid)

Input parameters:

  1. guid
    • The unique identifier for this request.

The GetTagTree method retrieves the tagtree object associated with the passed GUID. Use the GUID input parameter as an identifier to find the tagtree object however you chose to save it in the SaveTagTree method.

DeleteReport Method#

Method definition:

void DeleteReport(String guid)

Input parameters:

  1. guid
    • The unique identifier for this request.

The DeleteReport method is used to delete a generated document from the repository using its guid as the identifier.

SaveTemplate Method#

Method definition:

void SaveTemplate(CachedTemplate template)

Input parameters:

  1. template
    • The CachedTemplate you want to add to the template repository

The SaveTemplate method saves a template to the template repository. The template is represented by the CachedTemplate object.

GetTemplate Method#

Method definition:

public CachedTemplate GetTemplate(string templateID)

Input parameters:

  1. templateID
    • The unique identifier the template you want to get.

The GetTemplate method retrieves the template associated with the passed templateID. Use the templateID input parameter as an identifier to find the CachedTemplate object however you chose to save it in the SaveMetrics method.

DeleteTemplate Method#

Method definition:

void DeleteTemplate(string templateID)

Input parameters:

  1. templateID
    • The unique identifier the template you want to delete.

The DeleteTemplate method is used to delete a template from the template repository using its templateID as the identifier.

Shutdown Method#

Method definition:

void Shutdown()

The Shutdown method reverts all jobs in processing to pending (waiting to start). The web server is closing down. Here you will want to get any requests written to the disk and kill the worker thread. The way we have it implemented we would change all ".generate" files to ".pending" files in preparation for the server shutdown.

How to Use Defined Repository#

Now that you have defined your repository, it is time to include it in your RESTful Engine installation. The first thing you need to do is compile your repository project into a dll. Once you have the dll, you want to do the following in your .NET RESTful engine's web.config:

<appSettings>
<add key="repository" value="filename!classname" />
</appSetings>

Where:

  • filename is the path to your IRepository.jar implementation (if there are spaces in the path, put it in quotes)
  • classname is the name of the class that implements the IRepository interface