Question
· Jun 9, 2023

How can I change a production setting programmatically?

Hi folks!

How can I change the production setting programmatically?

I have a production that is a solution that uses some api-keys, which are the parameters of Business Operations but of course cannot be hard-coded into the source code.

E.g. here is the example of such a production that runs a connection of Telegram and ChatGPT.

And it can be installed as:

zpm "install telegram-gpt"

But now one needs to setup the key manually before using the production, having the following setting:

I'd like to set up it programmatically so one could install it as:

zpm "install telegram-gpt -D Token=sometoken"

How can I make it work?

Product version: IRIS 2023.1
Discussion (18)1
Log in or sign up to continue

Thanks @Evgeny Shvarov for the challenge.

I was digging through the ZPM code this afternoon. What I discovered is the need to implement AbstractInstaller and include in your module resources.

Here is a class that implements loading System Default Settings via ZPM command (when configuration phase is included by a ZPM activity)

 Class alwo.ChangeProductionSetting Extends %ZPM.AbstractInstaller
{
 Parameter Version = 1.0;

Parameter MaintenanceVersion = 5;

 ClassMethod OnConfigureComponent(pNamespace As %String, pVerbose As %Boolean = 0, ByRef pVars) As %Status
{
set tSC=$$$OK
set tSettingName=""
set (tProductionName,tItemName,tHostClassName,tTarget)="*"
set tValue=""
set varName=""
for {
  set varName=$O(pVars("zpm",varName),1,varValue)
  quit:varName=""
  if $ZCVT(varName,"U")="PRODUCTIONNAME" {
    set tProductionName=varValue
  } elseif $ZCVT(varName,"U")="TARGET" {
    set tTarget=varValue
  } elseif $ZCVT(varName,"U")="SETTINGNAME" {
    set tSettingName=varValue
  } elseif $ZCVT(varName,"U")="ITEMNAME" {
    set tItemName=varValue
  } elseif $ZCVT(varName,"U")="VALUE" {
    set tValue=varValue
  } elseif $ZCVT(varName,"U")="HOSTCLASSNAME" {
    set tHostClassName=varValue
  }
}

set existingId=tProductionName_"||"_tItemName_"||"_tHostClassName_"||"_tSettingName 

if tSettingName'="" {
  if ##class(Ens.Config.DefaultSettings).%ExistsId(existingId) {
     set oDefaultSetting=##class(Ens.Config.DefaultSettings).%OpenId(existingId,1)
  } else {
    set oDefaultSetting=##class(Ens.Config.DefaultSettings).%New()
    set oDefaultSetting.ProductionName=tProductionName
    set oDefaultSetting.ItemName=tItemName
    set oDefaultSetting.HostClassName=tHostClassName
    set oDefaultSetting.SettingName=tSettingName
  }
  // Assumes use of whitespace in settingValue is as intended
  set oDefaultSetting.SettingValue=tValue
  set tSC=oDefaultSetting.%Save()
}
Quit tSC
}    
}

Next in my ZPM module definition I added the tag at same level as Resource tags:

<Resource Name="alwo.TestProduction.CLS"/>
<Resource .... />
<InstallerClass>alwo.ChangeProductionSetting</InstallerClass>

In the zpm install command I could use:

load -v \some\localfolder\AddSetting -DProductionName=alwo.TestProduction -DHostClassName="EnsLib.MsgRouter.RoutingEngineST" -DItemName=TestProcess -DTarget=Item -DSettingName=RetryInterval -DValue=25

If you change your mind about the setting or want to add a second setting you can use for example:

module-action alwo-changeproductionsetting configure -only -DProductionName=alwo.TestProduction -DHostClassName="EnsLib.MsgRouter.RoutingEngineST" -DItemName=TestProcess -DTarget=Item -DSettingName=RetryInterval -DValue=25

For the arguments the only mandatory ones are:

  • -DSettingName
  • -DValue

When the others are not specified they simply go in as wildcard "*" against the System Default Settings.

BTW: Need to delete the "settable" values from the Production XData configuration in distribution, so that the default is applied.

ie: If looking at Production Configuration in Management Portal and a production Item setting is displaying with the Black font label, it means this value is masking the System Default Setting.

If you don't want to pass so many D parameters then some of the values could be hardcoded for project as Parameters in the InstallerClass.

 Parameter DProductionName="A Production Name";

Parameter DTarget="Item";

Parameter DItemName="Telegram.BusinessOperation";

Parameter DSettingName="Token";

Then would would be closer to original requirement:

zpm "install telegram-gpt -D Token=sometoken"

Hope this helps.

Kind regards,

Alex

Hi,

Do you know that every component in a production has a OnInit() method that is called when the component starts?
You can use this method to set the value of the parameter.

For example:

Method OnInit() As %Status
{
    Set ..#Token = $System.Util.GetEnviron("Token")
    Quit $$$OK
}

May be it's more elegant to do so than update the parameter in the Production file.

FYI, that what i do in IOP (Interoperability On Python) to set the value of the parameter of any component in a production.

def on_init(self):
    self.my_param = os.environ.get("MY_PARAM", "default_value")

Hi,

In iris.script add the following line:

zpm "install iris-deploy-tools -v" 

In TgGptProduction.cls remove the settings of Token and ApiKey

<Item Name="Telegram.InboundService" Category="" ClassName="Telegram.LongPollingService" PoolSize="1" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
    <Setting Target="Adapter" Name="SSLConfig">tg</Setting>
    <Setting Target="Host" Name="Target">GPTRouter</Setting>
</Item>
....
<Item Name="St.OpenAi.BO.Api.Connect" Category="" ClassName="St.OpenAi.BO.Api.Connect" PoolSize="1" Enabled="true" Foreground="false" Comment="" LogTraceEvents="false" Schedule="">
    <Setting Target="Host" Name="Organization"></Setting>
    <Setting Target="Adapter" Name="SSLConfig">tg</Setting>
</Item>

In Setup.cls, extends the class to St.Tools.Deploy and modify the loop to add the default configuration.

Class shvarov.telegramgpt.Setup Extends St.Tools.Deploy
{
ClassMethod Init(TgToken As %String, GPTKey As %String) As %Status
{
    set st=$$$OK
    set production="shvarov.telegramgpt.i14y.TgGptProduction"
    for item="Telegram.InboundService","Telegram.OutboundOperation" {
        set st = ..AddDefaultSetting("*",item,,"Token",TgToken)
        quit:$$$ISERR(st)
    }
    set item="St.OpenAi.BO.Api.Connect"
    set st = ..AddDefaultSetting("*",item,,"ApiKey",GPTKey)
    return st
}
....
}

I hope you find it useful

@Kurro Lopez !

I overlooked your package and introduced my own "one-class" package to handle this.

So the Setup.Init() method is not a loop anymore :)

ClassMethod Init(TgToken As %String, GPTKey As %String) As %Status
{
    set st=$$$OK
    set production="shvarov.telegramgpt.i14y.TgGptProduction"

    for item="Telegram.InboundService","Telegram.OutboundOperation" {
        set st=##class(shvarov.i14y.Settings).SetValue(production,item,"Token",TgToken)
        quit:$$$ISERR(st)
    }
    set item="St.OpenAi.BO.Api.Connect"
    set st=##class(shvarov.i14y.Settings).SetValue(production,item,"ApiKey",GPTKey)
    return st
}