Overview

When managing semi-static skills, you have the ability to upload files to storage in the database, whose data can be retrieved and used for writing a skill. Once a file is uploaded to storage, a FileUpload event will occur. This article will cover the basics of writing skills that utilize information from files in skill storage, such as storage levels and when to use these files.

 

In this Article

  

Storage Levels

There are three different levels in which files can be stored:

  • Publisher Level - Files stored at this level are only accessible to skills created by the publisher (the organization that publishes the skill). This level acts as a repository for the publisher and is used for files stored at the organization level for access by multiple skills. These will be files that are uploaded at the organization level using the API or the the organization level using General.
  • Installer Level - Files stored at the installer level are only accessible to skills associated with this level. These files are always associated with a skill id and an organization id that has installed the skill. These will be files that are uploaded at the skill level using the API or the storage section of the skill page.
    • For example, PrecisionLender may write a skill that refers to data the client has. When PrecisionLender publishes the skill to the client, the client would upload the file and use this level to access the data.
  • Current Skill Level - Files stored at this level are only available to the specific skill. These files are always associated with with a skill id and the publishers organization id and are uploaded at the skill level using the API or in the storage section of the skill page.

 

What are the Storage Powers?

The storage powers are a return power type that when called, will retrieve a specified file. These powers can be called at any storage level with the exception of the putFile power, which is only available to Publisher and Current Skill levels. 

The available storage powers are:

  • getFile() - this power is used when you are uploading any type of file such as .xls, .xlsx, .docx, etc. Note: You can also use the getFile power to upload JSON and CSV files.
    • Power Parameters 
      • path - entered as a string, is the file path along with the file name used to retrieve the file. You can use directory names within the path to better organize your files. 
  • putFile() - this power is used to create or update any file type. This is only available at Publisher and Current Skill levels.
    • Power Parameters 
      • path - entered as a string, is the file path along with the file name used to retrieve the file. You can use directory names within the path to better organize your files. 
      • data - data defines the data to be stored as part of the file
  • getJSONFile() - this power is used when the file being retrieved contains the same data type. For example, a file that only contains strings.
    • Power Parameters 
      • path - entered as a string, is the file path along with the file name used to retrieve the file. You can use directory names within the path to better organize your files. 
  • getJSONFileOf<>() - this power is used when the file being retrieved contains more than one data type, such as a number and a string.
    • Type Parameter (angle brackets)
      • Define the file interface by entering the name of the interface if an interface has been defined with the data types
      • If not defined, this will indicate a data type of 'any'.
    • Power Parameters 
      • path - entered as a string, is the file path along with the file name used to retrieve the file. You can use directory names within the path to better organize your files. 
  • get2dCSVFile() - this power is used when you're retrieving a two-dimensional file.
    • Power Parameters 
      • path - entered as a string, is the file path along with the file name used to retrieve the file. You can use directory names within the path to better organize your files. 
      • options? - an optional parameter where you can define additional information about the csv file if needed for parsing the data
  • getCSVFile() - this power is used when the file being retrieved contains the same data type. For example, a file that only contains strings.
    • Power Parameters 
      • path - entered as a string, is the file path along with the file name used to retrieve the file. You can use directory names within the path to better organize your files. 
      • options? - an optional parameter where you can define additional information about the csv file if needed for parsing the data
  • getCSVFileOf<>() -  this power is used when the file being retrieved contains more than one data type, such as a number and a string. 
    • Type Parameter (angle brackets)
      • Define the file interface by entering the name of the interface if an interface has been defined with the data types
      • If not defined, this will indicate a data type of 'any'.
    • Power Parameters 
      • path - entered as a string, is the file path along with the file name used to retrieve the file. You can use directory names within the path to better organize your files. 
      • autoMapDataTypes? - entered as a boolean and defaults to false when not defined. When defined as true, this will automatically map the data type based on the data type in the uploaded csv file. This means that if what is defined in the interface differs from what is defined in the file, then when set to true, the skill will utilize the data type contained in the file.
      • options? - an optional parameter where you can define additional information about the csv file if needed for parsing the data

 

When to use Storage

Storage is primarily used for files that contain smaller sets of data. When the skill runs, it will check each row of the file to pull the information requested by the skill. Unlike data sets, which are indexed and queryable, storage is used when you want the skill to pull information from a specific file, but you may not know exactly what you need. You have the ability to upload any file type, however we recommend using CSV or JSON files to utilize the built-in Andi® capabilities.

For example, you may have a skill written whose purpose is to provide a link when a particular event occurs. You can upload a CSV file containing all potential links to storage so that when the right event happens, the skill will look for the link in the file.

When uploading files to storage, you cannot download or open those files within the Andi® window. However, you can upload a file containing links for users to access that content that displays in the Andi® window.

 

How to Call Storage Powers

Storage powers will be called via the storage property of the Andi® context. The examples for each level shown below perform the same action. However we recommend storing the storage level as a variable as shown in the second example if planning to upload multiple files.

 

Publisher Level

Example 1:

return skillContext.powers.andi.storage.publisher.getFile("filename.json")

Example 2:

let currentStorage = skillContext.powers.andi.storage.publisher

return currentStorage.getJSONFileOf<goal>(fileName)

 

Installer Level

Example 1:

return skillContext.powers.andi.storage.installer.getFile("filename.json")

Example 2:

let currentStorage = skillContext.powers.andi.storage.installer

return currentStorage.getJSONFileOf<goal>(fileName)

 

Current Skill Level

Example 1:

return skillContext.powers.andi.storage.currentSkill.getFile("filename.json")
Example 2:
let currentStorage = skillContext.powers.andi.storage.currentSkill
  
  return currentStorage.getJSONFileOf<goal>(fileName)
 

Example JSON Storage Skill In the run.ts file

In this example, the file 'goals.json' acts as a lookup table to find a goal amount for a particular user.

An interface for the expected shape of the file has been defined. The file being retrieved must match the interface otherwise the skill will not work properly.

// example goal json file
/* 
{

    "goals" : [
        {
            "userId": "00000000-0000-0000-0000-000000000000",
            "userName": "User Name",
            "goalAmount": 100000.00
        }
        
    ]

}
*/

// data contract representing the data row in the JSON file being pulled from storage.
export interface goal {
    userId: string;
    userName: string;
    goalAmount: number;
}

// data contract representing the table of rows defined in the goal interface
export interface goalValues{
    goals: goal[];
}
The code below represents the rows in the JSON file that are being pulled from storage. This function looks up the goal values from the 'goals.JSON' file by specifying the data contract for the shape of the data located in the file. It then searches for the first goal in the file that matches the event user id. If no user is found, the default value of 0 is returned. 
// this function looks up the goal values from the goals.json file by specifying 
// the data contract for the shape of the data located in the goals.json file
// it then searches for the first goal in that file that matches the event user id
// if no user is found then the default value of 0 is returned
// in addition of getJSONFileOf there is getJSONFile which doesn't require a data
// contract but does not provide the intellisense on the returned values.
export function getGoalAmount(skillContext: andiSkills.ISkillContext): Promise{
    
        let fileName = "goals.json";
        let currentSkillStorage = skillContext.powers.andi.storage.currentSkill;
        let defaultAmount : number = 0;
    
        
        return currentSkillStorage.getJSONFileOf(fileName)
        .then(values => {

                if(values == null) return defaultAmount;

                let goal = values.goals.find((item) => {
                    return item.userId == skillContext.event.userId;
                });

                if (!goal) return defaultAmount;
                
                return goal.goalAmount;

        });    
        
 
}

// this function creates the field information filed tag for the message passed to the message parameter
export function createFieldTag(skillContext: andiSkills.ISkillContext,message: string) : Promise{
    return skillContext.powers.andi.fieldTag.sendFieldTag(
        andiSkills.FieldTagTypes.Info,
        // this is just a key to uniquely identify your fieldtag
        "opportunity-change-loan-goal-example", 
        1, 
        message,
        [],
        null, 
        function(api, model, comparehash, custom){
            var eventData = model.applicationEventData;
            
            var currentCommercialLoanAccount = eventData.engineModel.commercialLoanAccounts.find(function (commercialLoanAccount) {
                return commercialLoanAccount.id === model.currentContext;
            }); 
            
            var shouldIShow: boolean = (currentCommercialLoanAccount);

            return shouldIShow;
        },
        null, 
        null, 
        null 
    );
}

 

Example CSV Storage Skill the run.ts file

In this example, the file 'goals.csv' acts as a lookup table to find a goal amount for a particular user. 

An interface for the expected shape of the file has been defined. The file being retrieved must match the interface otherwise the skill will not work properly.

// example goal csv file
/* 
userId,userName,goalAmount
00000000-0000-0000-0000-000000000000,"User Name",2000000.00

*/

// data contract representing the data row in the CSV file being pulled from storage. export interface goal { userId: string; userName: string; goalAmount: number; }
The code below represents the rows in the CSV file that are being pulled from storage. This function looks up the goal values from the 'goals.csv' file by specifying the data contract for the shape of the data located in the file. It then searches for the first goal in the file that matches the event user id. If no user is found, the default value of 0 is returned. 
export function getGoalAmount(skillContext: andiSkills.ISkillContext): Promise<number>{


let fileName = "goals.csv";

let currentSkillStorage = skillContext.powers.andi.storage.publisher

let defaultAmount : number = 0;


return currentSkillStorage.getCSVFileOf<goal>(fileName)

.then(values => {

if(values==null) returndefaultAmount;

let goal=values.find((item) => {

return item.userId == skillContext.event.userId;

});

if (!goal) return defaultAmount;

 

return goal.goalAmount;

});
 

}

// this function creates the field information filed tag for the message passed to the message parameter

export function createFieldTag(skillContext: andiSkills.ISkillContext,message: string) : Promise<andiSkills.ISkillActivity>{

return skillContext.powers.andi.fieldTag.sendFieldTag(

andiSkills.FieldTagTypes.Info,

// this is just a key to uniquely identify your fieldtag

"opportunity-change-loan-goal-example",

1,

message,

[],

null,

function(api, model, comparehash, custom){

var eventData = model.applicationEventData;

var currentCommercialLoanAccount = eventData.engineModel.commercialLoanAccounts.find(function (commercialLoanAccount) {

return commercialLoanAccount.id === model.currentContext;

});

 
var shouldIShow: boolean = (currentCommercialLoanAccount);

return shouldIShow;

},

null,
null,
null

);

}

 

 

Was this article helpful?
0 out of 0 found this helpful