iguanaServer.lua
Verified Featured
Added by iNTERFACEWARE
Provides programmatic access to various operations that can be performed on Iguana channels.
Source Code
-- $Revision: 1.3 $
-- $Date: 2013-12-11 19:14:07 $
--
-- The iguanaServer module
-- Copyright (c) 2011-2014 iNTERFACEWARE Inc. ALL RIGHTS RESERVED
-- iNTERFACEWARE permits you to use, modify, and distribute this file in accordance
-- with the terms of the iNTERFACEWARE license agreement accompanying the software
-- in which it is used.
--
--------------------------------------------------------------------------------
-- Parameter checking.
-- In all functions the parameter "Args" should be a table.
--------------------------------------------------------------------------------
local function checkParamTable(Arg, Optional)
if Optional and Arg == nil then
return {}
end
local ArgType = type(Arg)
if ArgType ~= "table" then
error("Parameter table expected, got " .. ArgType .. ".", 3)
end
return Arg
end
local function checkParam(Args, Name, ExpectedType, Optional, DefaultValue)
local Arg = Args[Name]
if Optional and Arg == nil then
return DefaultValue
end
local ArgType = type(Arg)
if ArgType ~= ExpectedType then
error("Expected " .. ExpectedType .. ' for parameter "' .. Name ..
'", got ' .. ArgType .. ".", 3)
end
return Arg
end
local function checkLiveParam(Args, Default)
local Success, Result = pcall(checkParam, Args, "live", "boolean", true,
Default)
if not Success then
error(Result, 3)
end
-- Ignore the result if the channel is running live.
if not iguana.isTest() then
return true
end
return Result
end
-- Parameter checking for variables with length values (strings, tables, etc.).
local function checkNonEmptyParam(Args, Name, ExpectedType, Optional)
local Success, Result = pcall(checkParam, Args, Name, ExpectedType, Optional)
if not Success then
error(Result, 3)
end
if Result and #Result == 0 then
error('Parameter "' .. Name .. '" must be nonempty.', 3)
end
return Result
end
-- Helper function for public calls that allow channels to be identified by name
-- or by GUID. The underlying web service calls that use these parameters will
-- give priority to channel GUIDs if both are specified since these are more
-- permanent than channel names.
local function checkForNameOrGuid(Args)
local Success, Result, Guid = pcall(function()
return checkNonEmptyParam(Args, "name", "string", true),
checkNonEmptyParam(Args, "guid", "string", true)
end)
if not Success then
error(Result, 3)
end
local Name = Result
if not Name and not Guid then
error('Parameter "name" or "guid" is required.', 3)
end
return Name, Guid
end
--------------------------------------------------------------------------------
-- Miscellaneous functions.
--------------------------------------------------------------------------------
-- NOTE: This may be better as a public function
-- of the connection object.
local function getCurrentVersion(Host)
-- No authentication is needed to access this page.
local Success, Result = pcall(net.http.get,
{url=Host .. "/current_version", live=true})
if not Success then
error(Result, 3)
end
return json.parse(Result)
end
-- Doesn't copy tables with sub-tables... yet.
local function copyTable(Table, Iter)
local Copy = {}
for Key, Val in Iter(Table) do
Copy[Key] = Val
end
return Copy
end
local NULL_GUID = '00000000000000000000000000000000'
local function getAttrValue(E, Attr)
local A = (E and E[Attr]) or nil
return (A and A:isLeaf() and A:nodeValue() ~= '' and A:nodeValue()) or nil
end
local function getTranslatorImpl(Component, GuidAttr, UseMostRecentAttr, MilestoneAttr)
local Guid = getAttrValue(Component, GuidAttr)
local UseMostRecent = getAttrValue(Component, UseMostRecentAttr) == 'true'
local MilestoneName = getAttrValue(Component, MilestoneAttr)
if Guid and Guid ~= NULL_GUID then
local T = {['Guid']=Guid}
if not UseMostRecent then
T.Milestone = MilestoneName
end
return T
else
return nil
end
end
local function getTranslator(OC, NC, Component, GuidAttr, UseMostRecentAttr, MilestoneAttr)
local OldTrans, NewTrans =
getTranslatorImpl(OC[Component], GuidAttr, UseMostRecentAttr, MilestoneAttr),
getTranslatorImpl(NC[Component], GuidAttr, UseMostRecentAttr, MilestoneAttr)
if OldTrans and NewTrans then
return {old=OldTrans, new=NewTrans}
else
return nil
end
end
local function addToProjectList(Projects, Pair)
if Pair then
Projects[#Projects+1] = Pair
end
end
local function getTranslatorProjects(OldConfig, NewConfig)
local Projects = {}
local OC = OldConfig.channel or ''
local NC = NewConfig.channel or ''
-- Source component
local Pair =
getTranslator(OC, NC, 'from_llp_listener', 'ack_script', 'ack_use_most_recent', 'ack_milestone') or
getTranslator(OC, NC, 'from_mapper', 'guid', 'use_most_recent_milestone', 'milestone') or
getTranslator(OC, NC, 'from_http', 'guid', 'use_most_recent_milestone', 'milestone')
addToProjectList(Projects, Pair)
-- Filter component
Pair =
getTranslator(OC, NC, 'message_filter', 'translator_guid', 'use_most_recent_milestone', 'translator_milestone')
addToProjectList(Projects, Pair)
-- Destination component
Pair =
getTranslator(OC, NC, 'to_mapper', 'guid', 'use_most_recent_milestone', 'milestone')
addToProjectList(Projects, Pair)
return Projects
end
--------------------------------------------------------------------------------
-- Help data definition.
--------------------------------------------------------------------------------
local ObjectHelp, ModuleHelp = {}, {}
-- To enable the use of auto-completion while editing the help data, require the
-- module from somewhere in your main() function instead of at the global scope.
if iguana.isTest() then
-- Help data for server objects.
local ListChannelsHelp = help.example()
ListChannelsHelp.ParameterTable = true
ListChannelsHelp.Title = "listChannels"
ListChannelsHelp.Usage = "Server:listChannels{[live=<boolean>]} or Server:listChannels([<boolean>])"
ListChannelsHelp.Desc = "Returns a XML report with information on the server itself and the channels within it."
ListChannelsHelp.Parameters = {
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is true. boolean"}}
}
ListChannelsHelp.Returns = {
{Desc="The status of the server and the channels within it. xml node tree"}
}
ListChannelsHelp.Examples = {
[[-- Construct a list of the channel names in the server.
local Status = Server:listChannels{}.IguanaStatus
local ChannelNames = {}
for i=1, Status:childCount("Channel") do
table.insert(ChannelNames, Status:child("Channel", i).Name)
end]]
}
ListChannelsHelp.SeeAlso = {}
ObjectHelp.listChannels = ListChannelsHelp
local GetChannelConfigHelp = help.example()
GetChannelConfigHelp.SummaryLine = "Retrieves the configuration for a channel serialized as XML."
GetChannelConfigHelp.ParameterTable = true
GetChannelConfigHelp.Title = "getChannelConfig"
GetChannelConfigHelp.Usage = "Server:getChannelConfig{name=<string> OR guid=<string> [, live=<boolean>]}"
GetChannelConfigHelp.Desc = GetChannelConfigHelp.SummaryLine ..
" This operation requires the user to have view permission for the channel."..
[[
Note: We recommend using the guid to identify a channel, because the guid does not change when a channel is renamed.]]
GetChannelConfigHelp.Parameters = {
{name={Desc="alternative: (name OR guid required) The name of the channel to view. string"}},
{guid={Desc="alternative: (name OR guid required) The GUID of the channel to view. string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is true. boolean"}}
}
GetChannelConfigHelp.Returns = {
{Desc="The configuration of the specified channel. xml node tree"}
}
GetChannelConfigHelp.Examples = {
[[-- Get the configuration of the channel named "My Channel".
Server:getChannelConfig{name="My Channel"}]]
}
GetChannelConfigHelp.SeeAlso = {}
ObjectHelp.getChannelConfig = GetChannelConfigHelp
local RemoveChannelHelp = help.example()
RemoveChannelHelp.SummaryLine = "Removes a channel from the Iguana server."
RemoveChannelHelp.ParameterTable = true
RemoveChannelHelp.Title = "removeChannel"
RemoveChannelHelp.Usage = "Server:removeChannel{name=<string> OR guid=<string> [, live=<boolean>]}"
RemoveChannelHelp.Desc = RemoveChannelHelp.SummaryLine ..
" This operation requires the user to have administrator privileges."..
[[
Note: We recommend using the guid to identify a channel, because the guid does not change when a channel is renamed. ]]
RemoveChannelHelp.Parameters = {
{name={Desc="alternative: (name OR guid required) The name of the channel to remove. string"}},
{guid={Desc="alternative: (name OR guid required) The GUID of the channel to remove. string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is false. boolean"}}
}
RemoveChannelHelp.Returns = {
{Desc="The configuration of the channel that was removed. xml node tree"}
}
RemoveChannelHelp.Examples = {
[[-- Remove a channel with a specific GUID.
Server:removeChannel{guid="2E89ECEDEBC53A7E6977DA1AB3F4E08C"}]],
[[-- An example where pcall() is used while removing the channel to log any errors
-- that occur.
local Success, Result = pcall(Server.removeChannel, Server, {name="My Channel"})
if not Success then
iguana.logError(Result)
end]]
}
RemoveChannelHelp.SeeAlso = {}
ObjectHelp.removeChannel = RemoveChannelHelp
local VersionInfoHelp = help.example()
VersionInfoHelp.SummaryLine = "Returns a table containing version information for the Iguana server."
VersionInfoHelp.ParameterTable = false
VersionInfoHelp.Title = "versionInfo"
VersionInfoHelp.Usage = "Server:versionInfo()"
VersionInfoHelp.Desc = VersionInfoHelp.SummaryLine ..
" If the connection isn't live then the table will be empty."
VersionInfoHelp.Parameters = {}
VersionInfoHelp.Returns = {
{Desc="Version information for the Iguana server. table"}
}
VersionInfoHelp.Examples = {
[[local Version = Server:versionInfo()
if Version.Major == 5 and Version.Minor == 6 then
-- perform an operation on the server specific to this Iguana version
end]]
}
VersionInfoHelp.SeeAlso = {}
ObjectHelp.versionInfo = VersionInfoHelp
local StartChannelHelp = help.example()
StartChannelHelp.SummaryLine = "Starts a channel in the Iguana server."
StartChannelHelp.ParameterTable = true
StartChannelHelp.Desc = StartChannelHelp.SummaryLine .. "
" ..
"Tip: Starting channels is performed asynchronously, so to " ..
"determine if the channel has been successfully started you will " ..
"need to poll the status of the channel using the pollChannelStatus{} function."..
[[
Note: We recommend using the guid to identify a channel, because the guid does not change when a channel is renamed.]]
StartChannelHelp.Title = "startChannel"
StartChannelHelp.Usage = "Server:startChannel{name=<string> OR guid=<string> [, live=<boolean>]}"
StartChannelHelp.Parameters = {
{name={Desc="alternative: (name OR guid required) The name of the channel to start. string"}},
{guid={Desc="alternative: (name OR guid required) The GUID of the channel to start. string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is false. boolean"}}
}
StartChannelHelp.Returns = {
{Desc="The status of the server and the channels within it. xml node tree"}
}
StartChannelHelp.Examples = {
[[Server:startChannel{name="My Channel"}]]
}
StartChannelHelp.SeeAlso = {}
ObjectHelp.startChannel = StartChannelHelp
local StopChannelHelp = help.example()
StopChannelHelp.SummaryLine = "Stops a channel in the Iguana server."
StopChannelHelp.ParameterTable = true
StopChannelHelp.Desc = StopChannelHelp.SummaryLine .. "
" ..
"Tip: Stopping channels is performed asynchronously, so to " ..
"determine if the channel has been successfully stopped you will " ..
"need to poll the status of the channel using the pollChannelStatus{} function."..
[[
Note: We recommend using the guid to identify a channel, because the guid does not change when a channel is renamed.]]
StopChannelHelp.Title = "stopChannel"
StopChannelHelp.Usage = "Server:stopChannel{name=<string> OR guid=<string> [, live=<boolean>]}"
StopChannelHelp.Parameters = {
{name={Desc="alternative: (name OR guid required) The name of the channel to stop. string"}},
{guid={Desc="alternative: (name OR guid required) The GUID of the channel to stop. string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is false. boolean"}}
}
StopChannelHelp.Returns = {
{Desc="The status of the server and the channels within it. xml node tree"}
}
StopChannelHelp.Examples = {
[[Server:stopChannel{name="My Channel"}]]
}
StopChannelHelp.SeeAlso = {}
ObjectHelp.stopChannel = StopChannelHelp
local StartAllChannelsHelp = help.example()
StartAllChannelsHelp.SummaryLine = "Starts all channels in the Iguana server."
StartAllChannelsHelp.ParameterTable = true
StartAllChannelsHelp.Desc = StartAllChannelsHelp.SummaryLine .. "
" ..
"Tip: Starting channels is performed asynchronously, so to " ..
"determine if the channels have been successfully started you will need " ..
"need to poll the status of each channel using the pollChannelStatus{} function."
StartAllChannelsHelp.Title = "startAllChannels"
StartAllChannelsHelp.Usage = "Server:startAllChannels{[live=<boolean>]} or Server:startAllChannels([<boolean>])"
StartAllChannelsHelp.Parameters = {
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is false. boolean"}}
}
StartAllChannelsHelp.Returns = {
{Desc="The status of the server and the channels within it. xml node tree"}
}
StartAllChannelsHelp.Examples = {
[[Server:startAllChannels()]]
}
StartAllChannelsHelp.SeeAlso = {}
ObjectHelp.startAllChannels = StartAllChannelsHelp
local StopAllChannelsHelp = help.example()
StopAllChannelsHelp.SummaryLine = "Stops all channels in the Iguana server."
StopAllChannelsHelp.ParameterTable = true
StopAllChannelsHelp.Desc = StopAllChannelsHelp.SummaryLine .. "
" ..
"Tip: Stopping channels is performed asynchronously, so to " ..
"determine if the channels have been successfully stopped you will need " ..
"need to poll the status of each channel using the pollChannelStatus{} function."
StopAllChannelsHelp.Title = "stopAllChannels"
StopAllChannelsHelp.Usage = "Server:stopAllChannels{[live=<boolean>]} or Server:stopAllChannels([<boolean>])"
StopAllChannelsHelp.Parameters = {
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is false. boolean"}}
}
StopAllChannelsHelp.Returns = {
{Desc="The status of the server and the channels within it. xml node tree"}
}
StopAllChannelsHelp.Examples = {
[[Server:stopAllChannels()]]
}
StopAllChannelsHelp.SeeAlso = {}
ObjectHelp.stopAllChannels = StopAllChannelsHelp
local GetDefaultConfigHelp = help.example()
GetDefaultConfigHelp.SummaryLine = "Returns the default configuration for a channel with the specified components."
GetDefaultConfigHelp.ParameterTable = true
GetDefaultConfigHelp.Title = "getDefaultConfig"
GetDefaultConfigHelp.Usage = "Server:getDefaultConfig{source=<string>, destination=<string> [, live=<boolean>]}"
GetDefaultConfigHelp.Desc = GetDefaultConfigHelp.SummaryLine ..
" You can use the constants provided by the iguanaServer module to specify the component types.
" ..
"This function can be used in conjunction with addChannel{} to add new channels to an Iguana server."
GetDefaultConfigHelp.Parameters = {
{source={Desc="The source component type for the channel. string"}},
{destination={Desc="The destination component type for the channel. string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is true. boolean"}}
}
GetDefaultConfigHelp.Returns = {
{Desc="The default configuration for a channel with the specified components. xml node tree"}
}
GetDefaultConfigHelp.Examples = {
[[-- Retrieve the default configuration for a LLP Listener -> To Translator
-- channel and increment the port number in the source component by one.
local Config = Server:getDefaultConfig{
source=iguanaServer.LLP_LISTENER,
destination=iguanaServer.TO_TRANSLATOR
}
local Port = Config.channel.from_llp_listener.port
Port:setInner(Port:nodeValue() + 1)]]
}
GetDefaultConfigHelp.SeeAlso = {}
ObjectHelp.getDefaultConfig = GetDefaultConfigHelp
local AddChannelHelp = help.example()
AddChannelHelp.SummaryLine = "Adds a new channel to the Iguana server."
AddChannelHelp.ParameterTable = true
AddChannelHelp.Title = "addChannel"
AddChannelHelp.Usage = "Server:addChannel{config=<xml node tree> [, source_password=<string>] [, destination_password=<string>] [, salt=<string>] [, live=<boolean>]}
" ..
" or Server:addChannel(<xml node tree>)"
AddChannelHelp.Desc = AddChannelHelp.SummaryLine ..
" The channel added must have a unique name. This operation requires the user to have administrator privileges."
AddChannelHelp.Parameters = {
{config={Desc="The configuration for the new channel. xml node tree"}},
{source_password={Opt=true, Desc="The password for the source component. Only applicable to channels with a From File or From Database component. string"}},
{destination_password={Opt=true, Desc="The password for the destination component. Only applicable to channels with a To File or To Database component. string"}},
{salt={Opt=true, Desc="A value used to re-encrypt the passwords in certain component types. Useful when cloning channels between servers to prevent passwords from becoming invalid. string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is false. boolean"}}
}
AddChannelHelp.Returns = {
{Desc="The configuration of the newly added channel. xml node tree"}
}
AddChannelHelp.Examples = {
[[-- Add a new LLP Listener -> To Translator channel with
-- the default configuration.
local Config = Server:getDefaultConfig{
source=iguanaServer.LLP_LISTENER,
destination=iguanaServer.TO_TRANSLATOR
}
Config.channel.name = "My Channel"
Server:addChannel(Config)]],
[[-- Clone a channel within the same server and give it a unique name.
local Config = Server:getChannelConfig{name="My Channel"}.channel
Config.name = Config.name .. " (Clone)"
Server:addChannel(Config)]]
}
AddChannelHelp.SeeAlso = {}
ObjectHelp.addChannel = AddChannelHelp
local PollChannelStatusHelp = help.example()
PollChannelStatusHelp.SummaryLine = "Continuously polls the server until the specified channel reaches a particular status, " ..
"or until the number of retries is exceeded."
PollChannelStatusHelp.ParameterTable = true
PollChannelStatusHelp.Title = "pollChannelStatus"
PollChannelStatusHelp.Usage = "Server:pollChannelStatus{name=<string> OR guid=<string>, channel_status=<string>
" ..
" [, num_retries=<number>] [, interval=<number>] [, status=<xml node tree>] [, live=<boolean>]}"
PollChannelStatusHelp.Desc = PollChannelStatusHelp.SummaryLine ..
" This is useful when starting or stopping channels since these are asynchronous operations and may not take effect right away.
" ..
'There are constants defined in the iguanaServer module that can be used for the "channel_status" parameter.'..
[[
Note: We recommend using the guid to identify a channel, because the guid does not change when a channel is renamed.]]
PollChannelStatusHelp.Parameters = {
{name={Desc="alternative: (name OR guid required) The name of the channel to poll for a status change. string"}},
{guid={Desc="alternative: (name OR guid required) The GUID of the channel to poll for a status change. string"}},
{channel_status={Desc='The status of the channel to poll until. Can be either "on" or "off". string'}},
{num_retries={Opt=true, Desc="The number of times to poll the server before returning. Default is 10. number"}},
{interval={Opt=true, Desc="The length of time to sleep between poll attempts. Default is 100 milliseconds. number"}},
{status={Opt=true, Desc="The status of the Iguana server returned from a previous function call, such as listChannels(), startChannel{}, etc. " ..
"If this argument is given then it will be checked first for the desired status before sending any additional network requests. xml node tree"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is true. boolean"}}
}
PollChannelStatusHelp.Returns = {
{Desc="True if the specified channel reached the desired status within the alloted number of retry attempts, false otherwise. " ..
'This function always returns true when "live" has been set to false. boolean'},
{Desc="The last viewed status of the channel, if the first return value is false. string"}
}
PollChannelStatusHelp.Examples = {
[[-- Wait for the channel to reach the "on" status after issuing a start request.
-- Notice how the return value of startChannel{} is passed in as an argument
-- to pollChannelStatus{}. Some types of channels start immediately, which will
-- alleviate the need to send any additional network requests in pollChannelStatus{}.
local Name = "My Channel"
local Status = Server:startChannel{name=Name}
Server:pollChannelStatus{name=Name, channel_status=iguanaServer.CHANNEL_ON,
status=Status}]]
}
PollChannelStatusHelp.SeeAlso = {}
ObjectHelp.pollChannelStatus = PollChannelStatusHelp
local CloneChannelHelp = help.example()
CloneChannelHelp.SummaryLine = "Clones a channel within the same Iguana server or to a remote one."
CloneChannelHelp.ParameterTable = true
CloneChannelHelp.Title = "cloneChannel"
CloneChannelHelp.Usage = "Server:cloneChannel{name=<string> OR guid=<string> [, other=<Iguana server object>]
" ..
" [, new_name=<string>] [, configurator=<function>] [, live=<boolean>]}"
CloneChannelHelp.Desc = CloneChannelHelp.SummaryLine ..
[[ The "configurator" parameter can be used to make modifications to the configuration of the new channel before it gets added.
Note: We recommend using the guid to identify a channel, because the guid does not change when a channel is renamed.]]
CloneChannelHelp.Parameters = {
{name={Desc="alternative: (name OR guid required) The name of the channel to clone. string"}},
{guid={Desc="alternative: (name OR guid required) The GUID of the channel to clone. string"}},
{other={Opt=true, Desc="The Iguana server to clone the channel to. If this is left unspecified then the clone will be made locally. Iguana server object"}},
{new_name={Opt=true, Desc="The new name to use for the channel. If the clone is being made locally then this parameter is required, " ..
"since the channel would be invalid otherwise. string"}},
{configurator={Opt=true, Desc="A function that accepts the XML configuration for the channel being cloned as an argument. function"}},
{sample_data={Opt=true, Desc="Specifies how sample data will be cloned from the channel's Translator project(s). Should be \"append\", \"replace\", or unspecified (meaning sample data will not be cloned). string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is false. boolean"}}
}
CloneChannelHelp.Returns = {
{Desc="The configuration of the newly added channel. xml node tree"}
}
CloneChannelHelp.Examples = {
[[-- Simple example whereby a channel is cloned locally and no changes are made
-- to the configuration for the new channel (except for the name).
Server:cloneChannel{name="My Channel", new_name="My Other Channel"}]],
[[-- A more advanced example where the channel is cloned to a remote server and changes
-- are made to the new channel's configuration using the "configurator" function.
-- Assume that the variable Remote is an Iguana server object returned by
-- iguanaServer.connect{} and that "My Channel" has a LLP Listener source component.
-- Notice how the anonymous "configurator" function is able to access variables
-- within its closure (e.g. the PortNum variable).
local PortNum = 5350
Server:cloneChannel{name="My Channel", other=Remote, sample_data="replace", configurator=function(Config)
Config.channel.from_llp_listener.port = PortNum
end}]]
}
CloneChannelHelp.SeeAlso = {}
ObjectHelp.cloneChannel = CloneChannelHelp
local GetServerSaltHelp = help.example()
GetServerSaltHelp.SummaryLine = "Retrieves the salt used by the Iguana server for encryption purposes."
GetServerSaltHelp.ParameterTable = true
GetServerSaltHelp.Title = "getServerSalt"
GetServerSaltHelp.Usage = "Server:getServerSalt{[live=<boolean>]} or Server:getServerSalt([<boolean>])"
GetServerSaltHelp.Desc = GetServerSaltHelp.SummaryLine ..
" The main use for this function is to preserve password settings when cloning channels between different servers with certain component types." ..
" The component types concerned are From/To Database and From/To File."
GetServerSaltHelp.Parameters = {
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is true. boolean"}}
}
GetServerSaltHelp.Returns = {
{Desc="The salt used by the server for encryption. string"}
}
GetServerSaltHelp.Examples = {
[[-- Showing how the salt can be used when cloning a channel to different server.
-- Assume that "My Channel" has at least one component with the relevant
-- password settings.
local Config = Server:getChannelConfig{name="My Channel"}
local Salt = Server:getServerSalt()
Remote:addChannel{config=Config, salt=Salt}]]
}
GetServerSaltHelp.SeeAlso = {}
ObjectHelp.getServerSalt = GetServerSaltHelp
local UpdateChannelHelp = help.example()
UpdateChannelHelp.SummaryLine = "Updates the configuration of an existing channel."
UpdateChannelHelp.ParameterTable = true
UpdateChannelHelp.Title = "updateChannel"
UpdateChannelHelp.Usage = "Server:updateChannel{config=<xml node tree> [, source_password=<string>] [, destination_password=<string>] [, live=<boolean>]}
" ..
" or Server:updateChannel(<xml node tree>)"
UpdateChannelHelp.Desc = UpdateChannelHelp.SummaryLine ..
" This can be used in conjunction with getChannelConfig{}, by first retrieving the configuration for the channel and then modifying it before being passed into this function.
" ..
"This operation requires the channel to be stopped before being updated. The user must also have edit permission for the channel."
UpdateChannelHelp.Parameters = {
{config={Desc="The configuration to update the channel with. xml node tree"}},
{source_password={Opt=true, Desc="The password for the source component. Only applicable to channels with a From File or From Database component. string"}},
{destination_password={Opt=true, Desc="The password for the destination component. Only applicable to channels with a To File or To Database component. string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is false. boolean"}}
}
UpdateChannelHelp.Returns = {
{Desc="The configuration of the updated channel. xml node tree"}
}
UpdateChannelHelp.Examples = {
[[-- Enable the usage of the Filter in an existing channel.
local Config = Server:getChannelConfig{name="My Channel"}
Config.channel.use_message_filter = "true"
Server:updateChannel(Config)]]
}
UpdateChannelHelp.SeeAlso = {}
ObjectHelp.updateChannel = UpdateChannelHelp
-- exportProject()
local ExportProjectHelp = help.example()
ExportProjectHelp.SummaryLine = "Retrieves a zip file containing the contents of a Translator project."
ExportProjectHelp.ParameterTable = true
ExportProjectHelp.Title = "exportProject"
ExportProjectHelp.Usage = "Server:exportProject{guid=<string> [, milestone_name=<string>] [, sample_data=<boolean>] [, destination_file=<string>] [, live=<boolean>]}
"..
"or Server:exportProject(<string>)"
ExportProjectHelp.Desc = ExportProjectHelp.SummaryLine ..
" The function can return the base64-encoded contents of the zip file, or write the file out to disk."
ExportProjectHelp.Parameters = {
{guid={Desc="The GUID of the Translator project to export. string"}},
{milestone_name={Opt=true, Desc="The name of the milestone to export. Default is the project's most recent milestone. string"}},
{sample_data={Opt=true, Desc="If false, sample data will be excluded from the project zip. Default is true. boolean"}},
{destination_file={Opt=true, Desc="If present, project zip will be written out to this specified location. string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is true. boolean"}}
}
ExportProjectHelp.Returns = {
{Desc="The base64-encoded contents of the project zip file, or the path to which the zip file was written if destination_file was provided."}
}
ExportProjectHelp.Examples = {
[[-- Download the project to <Guid>.zip.
-- Alternatively omit the destination_file parameter to simply get the
-- base64-encoded contents of the zip file.
Server:exportProject{guid=Guid, sample_data=true, destination_file=Guid..'.zip'}]]
}
ExportProjectHelp.SeeAlso = {}
ObjectHelp.exportProject = ExportProjectHelp
-- importProject()
local ImportProjectHelp = help.example()
ImportProjectHelp.SummaryLine = "Sets the contents of a Translator project to the contents of a project zip file."
ImportProjectHelp.ParameterTable = true
ImportProjectHelp.Title = "importProject"
ImportProjectHelp.Usage = "Server:importProject{guid=<string> [, source_file=<string>] [, project=<string>] [, sample_data=<string>] [, live=<boolean>]}"
ImportProjectHelp.Desc = ImportProjectHelp.SummaryLine ..
" The function can accept the base64-encoded contents of the zip file, or read the file from disk."
ImportProjectHelp.Parameters = {
{guid={Desc="The GUID of the Translator project to import to. string"}},
{project={Opt=true, Desc="Base64-encoded project zip file contents. string"}},
{source_file={Opt=true, Desc="Location of the project zip file on disk. string"}},
{sample_data={Opt=true, Desc="Should be \"append\", \"replace\", or unspecified (meaning sample data will not be included). string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is false. boolean"}}
}
ImportProjectHelp.Returns = {
{Desc="The GUID of the Translator project into which the zip file was imported."}
}
ImportProjectHelp.Examples = {
[[-- Import a base64-encoded zip file (as received from exportProject()).
Server:importProject{guid=DestGuid, project=B64Proj, sample_data='replace', live=true}
-- or, import from a zip file on disk.
Server:importProject(guid=DestGuid, source_file='my_template.zip', sample_data='replace', live=true}]]
}
ImportProjectHelp.SeeAlso = {}
ObjectHelp.importProject = ImportProjectHelp
-- saveProjectMilestone()
local SaveProjectMilestoneHelp = help.example()
SaveProjectMilestoneHelp.SummaryLine = "Saves a milestone for the specified Translator project."
SaveProjectMilestoneHelp.ParameterTable = true
SaveProjectMilestoneHelp.Title = "saveProjectMilestone"
SaveProjectMilestoneHelp.Usage = "Server:saveProjectMilestone{guid=<string> milestone_name=<string>}"
SaveProjectMilestoneHelp.Desc = SaveProjectMilestoneHelp.SummaryLine
SaveProjectMilestoneHelp.Parameters = {
{guid={Desc="The GUID of the Translator project. string"}},
{milestone_name={Desc="The name of the milestone. string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is false. boolean"}}
}
SaveProjectMilestoneHelp.Returns = {
{Desc="The name of the milestone that was saved."}
}
SaveProjectMilestoneHelp.Examples = {
[[local Now = tostring(os.time())
Server:saveProjectMilestone{guid=DestGuid, milestone_name=Now..' - imported', live=true}]]
}
SaveProjectMilestoneHelp.SeeAlso = {}
ObjectHelp.saveProjectMilestone = SaveProjectMilestoneHelp
-- updateProject()
local UpdateProjectHelp = help.example()
UpdateProjectHelp.SummaryLine = "Copies a source Translator project to a destination Translator project."
UpdateProjectHelp.ParameterTable = true
UpdateProjectHelp.Title = "updateProject"
UpdateProjectHelp.Usage = "Server:updateProject{source_guid=<string>, destination_guid=<string>, new_milestone_name=<string> [, source_milestone_name=<string>] [, sample_data=<string>], [, live=<boolean>]}"
UpdateProjectHelp.Desc = UpdateProjectHelp.SummaryLine ..
" The function will read a specific milestone from the source project, and save a new milestone for the destination project."..
" The destination project can be local or remote (specified through the \"other\" parameter)."
UpdateProjectHelp.Parameters = {
{source_guid={Desc="The GUID of the source Translator project. string"}},
{destination_guid={Desc="The GUID of the destination Translator project. string"}},
{new_milestone_name={Desc="The name of the new milestone for the destination Transaltor project. string"}},
{other={Opt=true, Desc="The Iguana server to on which the destination Translator project is found. If this is left unspecified then the update will be performed locally. Iguana server object"}},
{source_milestone_name={Opt=true, Desc="The name of the milestone to export from the source Translator project. string"}},
{sample_data={Opt=true, Desc="Should be \"append\", \"replace\", or unspecified (meaning sample data will not be included). string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is false. boolean"}}
}
UpdateProjectHelp.Returns = {
{Desc="The name of the milestone that was saved."}
}
UpdateProjectHelp.Examples = {
[[local Now = tostring(os.time())
Server:updateProject{
source_guid=SourceGuid,
destination_guid=DestGuid,
new_milestone_name=Now..' - imported',
sample_data='replace'
}]]
}
UpdateProjectHelp.SeeAlso = {}
ObjectHelp.updateProject = UpdateProjectHelp
-- isLive()
local IsLiveHelp = help.example()
IsLiveHelp.SummaryLine = "Returns true if the connection to the Iguana server is \"live\"."
IsLiveHelp.ParameterTable = false
IsLiveHelp.Title = "isLive"
IsLiveHelp.Usage = "Server:isLive()"
IsLiveHelp.Desc = IsLiveHelp.SummaryLine ..
" This is set in iguanaServer.connect()."
IsLiveHelp.Parameters = {}
IsLiveHelp.Returns = {
{Desc="true if the connection is live, false otherwise. boolean"}
}
IsLiveHelp.Examples = {
[[Server:cloneChannel{name="My Channel", new_name="My Other Channel"}
if Server:isLive() then
print("\"My Other Channel\" successfully created.")
end]]
}
IsLiveHelp.SeeAlso = {}
ObjectHelp.isLive = IsLiveHelp
-- Help data for the module.
local ConnectHelp = help.example()
ConnectHelp.SummaryLine = "Connects to an Iguana server."
ConnectHelp.ParameterTable = true
ConnectHelp.Title = "connect"
ConnectHelp.Usage = "iguanaServer.connect{url=<string>, username=<string>, password=<string> [, live=<boolean>]}"
ConnectHelp.Desc = ConnectHelp.SummaryLine ..
" This function returns an object that can be used to perform operations on the server's channels. " ..
"The colon operator should be used to make method calls on the object, e.g. Server:functionName(Args)."
ConnectHelp.Parameters = {
{url={Desc="The URL of the Iguana server. string"}},
{username={Desc="The name of the user to login as. string"}},
{password={Desc="The password of the user to login as. string"}},
{live={Opt=true, Desc="If true, the function will be executed in the editor. Default is true. " ..
"If this is set to false then none of the operations performed on the object will be executed in the editor. boolean"}}
}
ConnectHelp.Returns = {
{Desc="A connection to the specified Iguana server. Iguana server object"}
}
ConnectHelp.Examples = {
[[local Server = iguanaServer.connect{
url="localhost:6543",
username="admin",
password="password"
}]]
}
ConnectHelp.SeeAlso = {}
ModuleHelp.connect = ConnectHelp
local CloneXmlNodeHelp = help.example()
CloneXmlNodeHelp.SummaryLine = "Utility function for cloning specific types of XML nodes."
CloneXmlNodeHelp.ParameterTable = true
CloneXmlNodeHelp.Title = "cloneXmlNode"
CloneXmlNodeHelp.Usage = "iguanaServer.cloneXmlNode{source=<xml node tree>, dest=<xml node tree>}"
CloneXmlNodeHelp.Desc = CloneXmlNodeHelp.SummaryLine ..
" This is useful for configuring certain channel settings.
" ..
"The default implementation of this function will only clone XML elements with attributes for child nodes. " ..
"Most of the XML nodes in the responses sent back by the various channel API request handlers adhere to this structure."
CloneXmlNodeHelp.Parameters = {
{source={Desc="The node to clone. xml node tree"}},
{dest={Desc="The node to append the new node to. xml node tree"}}
}
CloneXmlNodeHelp.Returns = {
{Desc="The new node that was made. xml node tree"}
}
CloneXmlNodeHelp.Examples = {
[[-- Below we show how you can use this function to configure the sources for a
-- a channel with a From Channel source component. Note that the sources are
-- actually stored in the configuration for the destination component, rather
-- than the From Channel component.
local Config = Server:getDefaultConfig{source=iguanaServer.FROM_CHANNEL,
destination=iguanaServer.LLP_CLIENT}
local DequeueList = Config.channel.to_llp_client.dequeue_list
local Dequeue = DequeueList.dequeue
Dequeue.source_name = "Channel 1"
-- Now clone this node to configure the second source channel.
Dequeue = iguanaServer.cloneXmlNode{source=Dequeue, dest=DequeueList}
Dequeue.source_name = "Channel 2"]]
}
CloneXmlNodeHelp.SeeAlso = {}
ModuleHelp.cloneXmlNode = CloneXmlNodeHelp
end
local function setHelpData(Table, HelpData)
for Name, Func in pairs(Table) do
if HelpData[Name] then
help.set{input_function=Func, help_data=HelpData[Name]}
end
end
end
--------------------------------------------------------------------------------
-- Public API.
--------------------------------------------------------------------------------
iguanaServer = {}
-- Constants for component types passed to the getDefaultConfig{} function.
iguanaServer.LLP_LISTENER = "LLP Listener"
iguanaServer.FROM_DATABASE = "From Database"
iguanaServer.FROM_FILE = "From File"
iguanaServer.FROM_PLUGIN = "From Plugin"
iguanaServer.FROM_HTTPS = "From HTTPS"
iguanaServer.FROM_CHANNEL = "From Channel"
iguanaServer.FROM_TRANSLATOR = "From Translator"
iguanaServer.TO_TRANSLATOR = "To Translator"
iguanaServer.TO_DATABASE = "To Database"
iguanaServer.LLP_CLIENT = "LLP Client"
iguanaServer.TO_FILE = "To File"
iguanaServer.TO_PLUGIN = "To Plugin"
iguanaServer.TO_HTTPS = "To HTTPS"
iguanaServer.TO_CHANNEL = "To Channel"
-- Constants for the various Status values that a channel can have in the
-- response to a /status request.
iguanaServer.CHANNEL_ON = "on"
iguanaServer.CHANNEL_OFF = "off"
iguanaServer.CHANNEL_STARTING = "..." -- corresponds to the "yellow light" state
iguanaServer.CHANNEL_ERROR = "error"
-- This function stores the instance variables for the server object in a
-- closure as a form of information hiding. If access to these variables is
-- really needed after instantiating the object then it would be trivial to
-- write appropriate getter/setter functions.
function iguanaServer.connect(Args)
checkParamTable(Args)
-- Private variables.
local Data = {}
Data.Url = checkParam(Args, "url", "string")
Data.Username = checkParam(Args, "username", "string")
Data.Password = checkParam(Args, "password", "string")
Data.Live = checkLiveParam(Args, true)
-- Holds references to the various functions that can be called on the object.
local Obj = {}
-- Helper functions.
-- Should be the first call made in any public function to enforce the
-- object-oriented style of parameter passing.
local function checkSelfParam(self)
if self ~= Obj then
error('Implicit "self" parameter is not equal to object table.\n' ..
"Try calling the function using colon syntax (e.g. Server:funcName()).", 3)
end
end
-- Makes a request to a handler in the channel API.
local function makeApiRequest(Handler, Params, Live, HttpMethod)
HttpMethod = HttpMethod or net.http.get
local Success, Result, Status = pcall(HttpMethod, {
url=Data.Url .. Handler,
parameters = Params,
auth={username=Data.Username, password=Data.Password},
live=Data.Live and Live
})
if not Success or Status >= 400 then
error(Result, 3)
end
return Result
end
-- We will only attempt to connect to the server if live is set to true.
if Data.Live then
-- First attempt to get the version information for the server. An error
-- will be thrown if the hostname is invalid.
Data.Version = getCurrentVersion(Data.Url)
if Data.Version.Major < 5 or
Data.Version.Major == 5 and Data.Version.Minor < 6 then
error("iguanaServer only supports operations on Iguana instances version 5.6 and up", 2)
end
-- Next send a dummy request to the /status handler to validate the login
-- info given.
makeApiRequest("/status", {}, true)
end
-- Public functions.
-- Using colon syntax for the function definitions here isn't strictly
-- necessary because instance variables are held in the closure for the
-- connect{} function rather than the implicit "self" paramater. But we do so
-- anyway because this is the style that is most commonly used for object
-- methods. Note that you can also call functions using regular dot syntax so
-- long as you pass in the object table for the first parameter
-- (e.g. Server.funcName(Server)).
function Obj:listChannels(Args)
checkSelfParam(self)
if type(Args) == "boolean" then
Args = {live=Args}
else
Args = checkParamTable(Args, true)
end
local Live = checkLiveParam(Args, true)
return xml.parse(makeApiRequest("/status", {}, Live))
end
function Obj:getChannelConfig(Args)
checkSelfParam(self)
checkParamTable(Args)
local Name, Guid = checkForNameOrGuid(Args)
local Live = checkLiveParam(Args, true)
-- For debugging purposes you may want to remove the compact="true"
-- parameter if you need to inspect the raw XML directly.
return xml.parse(makeApiRequest("/get_channel_config",
{name=Name, guid=Guid, compact="true"},
Live))
end
function Obj:removeChannel(Args)
checkSelfParam(self)
checkParamTable(Args)
local Name, Guid = checkForNameOrGuid(Args)
local Live = checkLiveParam(Args, false)
return xml.parse(makeApiRequest("/remove_channel",
{name=Name, guid=Guid, compact="true"},
Live, net.http.post))
end
-- NOTE: There isn't a live parameter for this function because if the version
-- info isn't available already then this means that the server object is
-- non-live, in which case a live value here wouldn't make a difference.
function Obj:versionInfo()
checkSelfParam(self)
-- Returns a copy of the Version table so that it can't be modified
-- outside the module.
return copyTable(Data.Version or {}, pairs)
end
function Obj:startChannel(Args)
checkSelfParam(self)
checkParamTable(Args)
local Name, Guid = checkForNameOrGuid(Args)
local Live = checkLiveParam(Args, false)
return xml.parse(makeApiRequest("/status",
{name=Name, guid=Guid, action="start"},
Live, net.http.post))
end
function Obj:stopChannel(Args)
checkSelfParam(self)
checkParamTable(Args)
local Name, Guid = checkForNameOrGuid(Args)
local Live = checkLiveParam(Args, false)
return xml.parse(makeApiRequest("/status",
{name=Name, guid=Guid, action="stop"},
Live, net.http.post))
end
function Obj:startAllChannels(Args)
checkSelfParam(self)
if type(Args) == "boolean" then
Args = {live=Args}
else
Args = checkParamTable(Args, true)
end
local Live = checkLiveParam(Args, false)
return xml.parse(makeApiRequest("/status", {action="startall"}, Live,
net.http.post))
end
function Obj:stopAllChannels(Args)
checkSelfParam(self)
if type(Args) == "boolean" then
Args = {live=Args}
else
Args = checkParamTable(Args, true)
end
local Live = checkLiveParam(Args, false)
return xml.parse(makeApiRequest("/status", {action="stopall"}, Live,
net.http.post))
end
function Obj:getDefaultConfig(Args)
checkSelfParam(self)
checkParamTable(Args)
local Source = checkNonEmptyParam(Args, "source", "string")
local Destination = checkNonEmptyParam(Args, "destination", "string")
local Live = checkLiveParam(Args, true)
return xml.parse(makeApiRequest("/get_default_config",
{source=Source, destination=Destination, compact="true"},
Live))
end
function Obj:addChannel(Args)
checkSelfParam(self)
local ArgType = type(Args)
if ArgType == "userdata" then
Args = {config=Args}
else
checkParamTable(Args)
checkParam(Args, "config", "userdata")
checkNonEmptyParam(Args, "source_password", "string", true)
checkNonEmptyParam(Args, "destination_password", "string", true)
checkNonEmptyParam(Args, "salt", "string", true)
end
local Live = checkLiveParam(Args, false)
return xml.parse(makeApiRequest("/add_channel",
{config=tostring(Args.config),
source_password=Args.source_password,
destination_password=Args.destination_password,
salt=Args.salt,
compact="true"},
Live, net.http.post))
end
function Obj:pollChannelStatus(Args)
checkSelfParam(self)
checkParamTable(Args)
-- Higher-order function for finding a channel node in the response to a
-- /status request.
local function findChannel(Attribute, Value)
return function(Status)
local IguanaStatus = Status.IguanaStatus
for i = 1, IguanaStatus:childCount("Channel") do
local Channel = IguanaStatus:child("Channel", i)
if Channel[Attribute]:nodeValue() == Value then
return Channel
end
end
return nil
end
end
local Name, Guid = checkForNameOrGuid(Args)
local ChannelFinder
if Guid then -- give priority to channel GUIDs
ChannelFinder = findChannel("Guid", Guid)
else
ChannelFinder = findChannel("Name", Name)
end
local ChannelStatus = checkNonEmptyParam(Args, "channel_status", "string")
-- The channel status in the response to /status can also be set to "..."
-- or "error" which correspond to the starting state and error state,
-- respectively, but it doesn't make much sense to poll for these states.
if ChannelStatus ~= iguanaServer.CHANNEL_ON and
ChannelStatus ~= iguanaServer.CHANNEL_OFF then
error('Invalid channel status "' .. ChannelStatus .. '" specified.', 2)
end
local NumRetries = checkParam(Args, "num_retries", "number", true, 10)
local Interval = checkParam(Args, "interval", "number", true, 100) -- milliseconds
local Status = checkParam(Args, "status", "userdata", true)
local Live = checkLiveParam(Args, true)
-- Always return true when live is set to false, the rationale being that
-- some applications will likely treat a return value of false as an error
-- case.
if not Live then
return true
end
-- The remaining code is wrapped in an anonymous function and called
-- through pcall in case the Status that has been given is invalid or a
-- /status request fails.
local Success, Result, LastChannelStatus = pcall(function()
-- If an existing response to a /status request has been provided
-- then check this first for the channel status, but only if the
-- node tree is non-empty. (It will be empty if the function call it
-- was returned from had live set to false.)
if Status and #Status > 0 then
local Channel = ChannelFinder(Status)
if Channel and Channel.Status:nodeValue() == ChannelStatus then
return true
end
end
local LastChannelStatus
for RetryCount = 1, NumRetries do
Status = xml.parse(makeApiRequest("/status", {}, true))
local Channel = ChannelFinder(Status)
if Channel then
LastChannelStatus = Channel.Status:nodeValue()
if LastChannelStatus == ChannelStatus then
return true
end
end
if RetryCount < NumRetries then
-- no point sleeping on the last iteration
util.sleep(Interval)
end
end
return false, LastChannelStatus
end)
if not Success then
error(Result, 2)
end
return Result, LastChannelStatus
end
function Obj:cloneChannel(Args)
checkSelfParam(self)
checkParamTable(Args)
local Name, Guid = checkForNameOrGuid(Args)
-- If another server has been specified then we'll attempt to clone the
-- channnel there. Otherwise the clone will be made locally, in which case
-- a new name for the channel needs to be specified since it will be
-- invalid if it has a duplicate name.
local Other = checkParam(Args, "other", "table", true)
-- Validate that the table has the function we need to complete the clone.
-- This isn't a very rigorous check, but it's good enough.
if Other and (type(Other.addChannel) ~= "function" or
type(Other.isLive) ~= "function") then
error('The argument given for the "other" parameter is not a valid ' ..
"server object.", 2)
end
local NewName = checkNonEmptyParam(Args, "new_name", "string", true)
if not NewName and not Other then
error("You must specify a new name for the channel when cloning locally.", 2)
end
-- Optional function argument that can be used to make modifications to
-- the channel configuration before it gets added to the target server.
local Configurator = checkParam(Args, "configurator", "function", true)
checkNonEmptyParam(Args, "sample_data", "string", true)
if Args.sample_data and Args.sample_data ~= 'append' and Args.sample_data ~= 'replace' then
error("Parameter \"sample_data\" must be \"append\" or \"replace\"", 2)
end
-- Use "nil" as the default live value so that the appropriate default
-- will get used in each function call when live has not been specified.
local Live = checkLiveParam(Args, nil)
local Success, Result = pcall(function()
local Config = self:getChannelConfig{name=Name, guid=Guid, live=Live}
local OldConfig = xml.parse(tostring(Config)) -- backup unmodified Config
if #Config > 0 then
if NewName then
Config.channel.name = NewName
end
if Configurator then
Configurator(Config)
end
end
local Salt, Target
if Other then
Salt = self:getServerSalt(Live)
Target = Other
else
Target = self
end
local TargetLive = Live and Target:isLive()
-- NOTE: We don't use source_password and destination_password
-- parameters here because they shouldn't be needed when cloning a
-- channel. If the clone is being made locally then the existing
-- passwords will work fine, and if the clone is made to a remote
-- server then the salt from the source server will be used to
-- handle encryption details.
local NewConfig = Target:addChannel{config=Config, salt=Salt, live=TargetLive}
if not TargetLive and #NewConfig == 0 then
NewConfig = Config -- helps us with annotations and auto-completion
end
-- Copy Translator project(s). Each channel may have between 0 and 3
-- Translator projects.
local Projects = getTranslatorProjects(OldConfig, NewConfig)
for i,Project in ipairs(Projects) do
-- For now we just pick a reasonable (timestamp-based) milestone name if
-- we're not copying a specific milestone. In the future cloneChannel()
-- could be enhanced to take a new_milestone_name parameter to pass along,
-- much like we do with sample_data.
local NewMilestoneName = Project.new.Milestone or (tostring(os.time())..' - imported')
self:updateProject{
source_guid=Project.old.Guid,
destination_guid=Project.new.Guid,
new_milestone_name=NewMilestoneName,
other=Other,
source_milestone_name=Project.old.Milestone,
sample_data=Args.sample_data,
live=Live
}
end
return NewConfig
end)
if not Success then
error(Result, 2)
end
return Result
end
function Obj:getServerSalt(Args)
checkSelfParam(self)
if type(Args) == "boolean" then
Args = {live=Args}
else
Args = checkParamTable(Args, true)
end
local Live = checkLiveParam(Args, true)
if Data.Live and Live then
return makeApiRequest("/get_server_salt", {}, Live)
end
-- If the request would've been sent non-live then we return nil instead
-- of the empty string returned by makeApiRequest() since addChannel{}
-- will treat an empty string passed in for the salt parameter as an error.
return nil
end
function Obj:updateChannel(Args)
checkSelfParam(self)
if type(Args) == "userdata" then
Args = {config=Args}
else
checkParamTable(Args)
checkParam(Args, "config", "userdata")
checkNonEmptyParam(Args, "source_password", "string", true)
checkNonEmptyParam(Args, "destination_password", "string", true)
end
local Live = checkLiveParam(Args, false)
return xml.parse(makeApiRequest("/update_channel",
{config=tostring(Args.config),
source_password=Args.source_password,
destination_password=Args.destination_password,
compact="true"},
Live, net.http.post))
end
function Obj:exportProject(Args)
checkSelfParam(self)
if type(Args) == "string" then
Args = {guid=Args}
else
checkParamTable(Args)
checkNonEmptyParam(Args, "guid", "string")
checkNonEmptyParam(Args, "milestone_name", "string", true)
if Args.sample_data ~= nil and type(Args.sample_data) ~= "boolean" then
error("Parameter \"sample_data\" must be a boolean", 2)
elseif Args.sample_data == false then
Args.sample_data = nil
else
Args.sample_data = 'true'
end
checkNonEmptyParam(Args, "destination_file", "string", true)
end
local Live = checkLiveParam(Args, true)
if Data.Live and Live then
local Base64ZipContents = makeApiRequest("/export_project",
{guid=Args.guid, milestone_name=Args.milestone_name, sample_data=Args.sample_data},
Live)
if not Args.destination_file then
return Base64ZipContents
else
local ZipContents = filter.base64.dec(Base64ZipContents)
local ZipFile = io.open(Args.destination_file, 'w+b')
ZipFile:write(ZipContents)
ZipFile:close()
return Args.destination_file
end
end
end
function Obj:importProject(Args)
checkSelfParam(self)
checkParamTable(Args)
checkNonEmptyParam(Args, "guid", "string")
checkNonEmptyParam(Args, "project", "string", true)
checkNonEmptyParam(Args, "source_file", "string", true)
if not Args.project and not Args.source_file then
error("One of parameters \"project\" or \"source_file\" is required.", 2)
elseif Args.project and Args.source_file then
error("Only one of parameters \"project\" or \"source_file\" is allowed.", 2)
end
checkNonEmptyParam(Args, "sample_data", "string", true)
if Args.sample_data and Args.sample_data ~= 'append' and Args.sample_data ~= 'replace' then
error("Parameter \"sample_data\" must be \"append\" or \"replace\"", 2)
end
local Live = checkLiveParam(Args, false)
if Data.Live and Live then
local Base64ZipContents = Args.project
if not Base64ZipContents then
local ZipFile = io.open(Args.source_file, "rb")
local ZipContents = ZipFile:read("*all")
ZipFile:close()
Base64ZipContents = filter.base64.enc(ZipContents)
end
makeApiRequest("/import_project",
{guid=Args.guid, project=Base64ZipContents, sample_data=Args.sample_data},
Live)
end
return Args.guid
end
function Obj:saveProjectMilestone(Args)
checkSelfParam(self)
checkParamTable(Args)
checkNonEmptyParam(Args, "guid", "string")
checkNonEmptyParam(Args, "milestone_name", "string")
local Live = checkLiveParam(Args, false)
if Data.Live and Live then
makeApiRequest("/save_project_milestone",
{guid=Args.guid, milestone_name=Args.milestone_name},
Live)
end
return Args.milestone_name
end
function Obj:updateProject(Args)
checkSelfParam(self)
checkParamTable(Args)
checkNonEmptyParam(Args, "source_guid", "string")
checkNonEmptyParam(Args, "destination_guid", "string")
checkNonEmptyParam(Args, "new_milestone_name", "string")
checkNonEmptyParam(Args, "source_milestone_name", "string", true)
checkNonEmptyParam(Args, "sample_data", "string", true)
if Args.sample_data and Args.sample_data ~= 'append' and Args.sample_data ~= 'replace' then
error("Parameter \"sample_data\" must be \"append\" or \"replace\"", 2)
end
local Other = checkParam(Args, "other", "table", true)
-- Validate that the table has the function we need to complete the update.
-- This isn't a very rigorous check, but it's good enough.
if Other and (type(Other.importProject) ~= "function" or
type(Other.saveProjectMilestone) ~= "function" or
type(Other.isLive) ~= "function") then
error('The argument given for the "other" parameter is not a valid ' ..
"server object.", 2)
end
local Live = checkLiveParam(Args, false)
local Target = Other or self
local TargetLive = Live and Target:isLive()
local Project = self:exportProject{
guid=Args.source_guid,
milestone_name=Args.source_milestone_name,
sample_data=(Args.sample_data and true),
live=Live
}
if not Live then Project = 'empty' end -- keep annotations working
Target:importProject{
guid=Args.destination_guid,
project=Project,
sample_data=Args.sample_data,
live=TargetLive
}
return Target:saveProjectMilestone{
guid=Args.destination_guid,
milestone_name=Args.new_milestone_name,
live=TargetLive
}
end
function Obj:isLive()
return Data.Live
end
-- We need to set help data on the object here before returning because the
-- usage of closures means that each server object has different copies of
-- the same functions.
if iguana.isTest() then
setHelpData(Obj, ObjectHelp)
end
return Obj
end
-- Note: A recursive algorithm that clones any type of XML node could be
-- implemented here, but I don't think this is really needed for the typical
-- use case of the module.
function iguanaServer.cloneXmlNode(Args)
checkParamTable(Args)
local Source = checkParam(Args, "source", "userdata")
local Dest = checkParam(Args, "dest", "userdata")
local SourceType = Source:nodeType()
if SourceType ~= xml.ELEMENT then
error("Expected " .. xml.ELEMENT .. " for source node, got " ..
SourceType .. ".", 2)
end
local NewNode = Dest:append(SourceType, Source:nodeName())
-- Copy the attributes of the source node onto the new one.
for i=1, #Source do
local Child = Source[i]
local ChildType = Child:nodeType()
if ChildType ~= xml.ATTRIBUTE then
error("Expected " .. xml.ATTRIBUTE .. " for child node, got " ..
ChildType .. ".", 2)
end
NewNode:append(ChildType, Child:nodeName()):setInner(Child:nodeValue())
end
return NewNode
end
if iguana.isTest() then
setHelpData(iguanaServer, ModuleHelp)
end
return iguanaServer
Description
Provides programmatic access to various operations that can be performed on Iguana channels.
Usage Details
The iguanaServer.lua module contains functions used to manipulate channels on local or remote Iguana instances. This module is a Lua wrapper for our HTTP Channel API.
Manipulate channels on local or remote Iguana instances:
- addChannel : Adds a new channel to the Iguana server
- cloneChannel : Clones a channel within the same Iguana server or to a remote one
- exportProject : Retrieves a zip file containing the contents of a Translator project
- getChannelConfig : Retrieves the configuration for a channel serialized as XML
- getDefaultConfig : Returns the default configuration for a channel with the specified components
- getServerSalt : Retrieves the salt used by the Iguana server for encryption purposes
- importProject : Sets the contents of a Translator project to the contents of a project zip file
- isLive : Returns true if the connection to the Iguana server is “live”
- listChannels : Returns a XML report with information on the server itself and the channels within it
- pollChannelStatus : Continuously polls the server until the specified channel reaches a particular status, or until the number of retries is exceeded
- removeChannel : Removes a channel from the Iguana server
- saveProjectMilestone : Saves a milestone for the specified Translator project
- startAllChannels : Starts all channels in the Iguana server
- startChannel : Starts a channel in the Iguana server
- stopAllChannels : Stops all channels in the Iguana server
- stopChannel : Stops a channel in the Iguana server
- updateChannel : Updates the configuration of an existing channel
- updateProject : Copies a source Translator project to a destination Translator project
- versionInfo : Returns a table containing version information for the Iguana server
Here is some example code:
-- Get the configuration of the channel named "My Channel".
Server:getChannelConfig{name="My Channel"}
-- Add a new LLP Listener -> To Translator channel with
-- the default configuration.
local Config = Server:getDefaultConfig{
source=iguanaServer.LLP_LISTENER,
destination=iguanaServer.TO_TRANSLATOR
}
Config.channel.name = "My Channel"
Server:addChannel(Config)
More Information