Hello @Rob Schoenmakers 

I am experiencing the exact same issue. I currently have an open case with the WRC, but we haven't found a solution yet.

From our testing, the issue seems strictly isolated to SFTP retrieval via EnsLib.RecordMap.Service.FTPService. If I retrieve the exact same .CSV file locally using a EnsLib.RecordMap.Service.FileService, I have no encoding issues whatsoever.

The baffling part is that this behavior is inconsistent across environments: the exact same production works perfectly on other instances (like an IRIS Community container). Even InterSystems Support deployed my production on their local instance, connected to my SFTP server, and could not reproduce the encoding error.

If anyone has found a fix or a workaround, I'm all ears! :D

Best regards,

Hi Sylvain.

In System  > Globals  > View Global Data > ^IRIS.Temp global I can see :
 

1:  ^IRIS.Temp("Secrets","clientid") = ""
2:  ^IRIS.Temp("Secrets","clientsecret") = ""


The %ZSTART seems to work but not retrieving the values.

You will find just below the Business Operation that tries to use the clientid and clientsecret :

Class HTTP.PostOperation.GetToken Extends Ens.BusinessOperation
{
    Parameter ADAPTER = "EnsLib.HTTP.OutboundAdapter";
    Parameter SETTINGS = "clientid, clientsecret, databasereference, audience, granttype, server";
  // Paramètres configurables depuis la production

    /// Adresse du serveur cible 
    Property server As %String;
    /// Identification du credential clientid à utiliser pour faire la requête vers l'API
    Property clientid As %String;
    /// Identification du credential clientsecret à utiliser permettant la requête vers l'API
    Property clientsecret As %String;
    /// audience permettant la requête vers l'API
    Property audience As %String;
    /// référence de la database associée à l'établissement de santé concerné
    Property databasereference As %String;
    /// granttype permettant la requête vers l'API
    Property granttype As %String [ InitialExpression = "client_credentials" ];

    Method OnMessage(pRequest As HTTP.request, Output pResponse As HTTP.response) As %Status
    {
        Set pResponse = ##class(HTTP.response).%New()
        /// 1. Purge des anciens tokens expirés
        Do ##class(INTEROP1.TokenCache).PurgeExpired()

        /// 2. Vérifie si un token valide est déjà en cache
        Set token = ""
        Set success = ##class(INTEROP1.TokenCache).GetValidToken("TokenCache", .token)
        If success {
            Write "Token récupéré depuis le cache", !
            Set pResponse.Token = token
            Set pResponse.Log = "Token récupéré depuis le cache"
            Quit $$$OK
        }

        Write "Aucun token valide trouvé, appel HTTP en cours...", !

        /// 3. Appel HTTP POST pour récupérer un nouveau token
        Set httpRequest = ##class(%Net.HttpRequest).%New()
        Set httpRequest.Server = ..server
        Set httpRequest.Https = 1
        Set httpRequest.SSLConfiguration = "default"
        Set httpRequest.ContentType = "application/json"
        // Construction du corps de la requête
        Set jsonObject = ##class(%DynamicObject).%New()
        /// Si injection env. variable 
        Set clientid = $Get(^IRIS.Temp("Secrets", "APIclientid"),"")
        Set clientsecret = $Get(^IRIS.Temp("Secrets", "APIclientsecret"),"")
        /// Set clientid = $System.Util.GetEnviron("APIclientid")     
        /// Set clientsecret = $System.Util.GetEnviron("APIclientsecret")    
        Do jsonObject.%Set("client_id", clientid)
        Do jsonObject.%Set("client_secret", clientsecret)
        Do jsonObject.%Set("audience", ..audience)
        Do jsonObject.%Set("grant_type", ..granttype)
        Do jsonObject.%Set("database_reference", ..databasereference)

        Set requestPayload = jsonObject.%ToJSON()
           
        Do httpRequest.EntityBody.Write(requestPayload)

        Set status = httpRequest.Post("//v1/token")

        If $$$ISERR(status) {
            Write "Erreur durant l'appel HTTP", !
            Quit status
        }

        /// 4. Lecture de la réponse
        Set rawJSON = httpRequest.HttpResponse.Data.Read()
        Set jsondyn = ##class(%DynamicObject).%FromJSON(rawJSON)
        Set token = jsondyn.%Get("access_token")
        Set expiresIn = jsondyn.%Get("expires_in")

        Set pResponse.RawJSON = rawJSON
        Set pResponse.json = jsondyn
        Set pResponse.Token = token

        /// 5. Enregistre le token dans la base avec expiration
        Do ##class(INTEROP1.TokenCache).SaveToken("TokenCache", token, expiresIn)
        If pResponse.Token '= ""
            Write "Nouveau token stocké dans le cache", !
            Set pResponse.Log = "Nouveau token stocké dans le cache"
        If pResponse.Token = ""
            Write "Impossible de récupérer le token", !
            Set pResponse.Log = "Impossible de récupérer le token"

        Quit $$$OK
    }
}

FYI, I have also tried this in the terminal and it does not seem to work :
(Before doing so, I tried to add a new line in the %ZSTART :

Set ^IRIS.Temp("Secrets","TEST") = "Hello"
%SYS>do ^%ZSTART

%SYS>zw ^IRIS.Temp("Secrets")
^IRIS.Temp("Secrets","TEST")="Hello"
^IRIS.Temp("Secrets","lifenclientid")=""
^IRIS.Temp("Secrets","lifenclientsecret")=""

The environment variables have been setup in my docker container and can be found in the docker-compose.yml :
 

....
Env": [
			"APIclientid=testclientid",
			"APIclientsecret=testclientsecret",
			"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/irisowner/bin",
			"ISC_PACKAGE_IRISGROUP=irisowner",
			"ISC_PACKAGE_IRISUSER=irisowner",
			"ISC_PACKAGE_MGRGROUP=irisowner",
			"ISC_PACKAGE_MGRUSER=irisowner",
			"IRISSYS=/home/irisowner/irissys",
			"TINI_VERSION=v0.19.0",
			"ISC_PACKAGE_INSTANCENAME=IRIS",
			"ISC_PACKAGE_INSTALLDIR=/usr/irissys",
			"PYTHONPATH=/usr/irissys/mgr/python",
			"LANG=en_US.UTF-8",
			"LANGUAGE=en_US.UTF-8",
			"LC_ALL=en_US.UTF-8"
		],
....

Maybe I have not correctly setup the environment variable and this is why this is not working ?

Kind regards,

 

Hi @Sylvain Guilbaud,

Thank you for your reply.
Basically you're saying that I need to store the secrets in a global at IRIS startup using a %ZSTART routine so I can retrieve them in a business operation, am I right ?

I have tried to do so with this routine :
 

ROUTINE %ZSTART

  QUIT ; 

SYSTEM
  Set ^IRIS.Temp("Secrets", "APIclientid") = $System.Util.GetEnviron("APIclientid")
  Set ^IRIS.Temp("Secrets", "APIclientsecret") = $System.Util.GetEnviron("APIclientsecret")

  QUIT

I have loaded it in the %SYS namespace and I can check that it has been successfuly imported when I go on the Management Portal -> System Explorer -> Routines.

Then, I set my credentials in my business operation this way to use then in a HTTP request :
 

Set APIclientid = $Get(^IRIS.Temp("Secrets", "APIclientid "),"")
Set APIclientsecret =  $Get(^IRIS.Temp("Secrets", "APIclientsecret"),"")

However, when I want to test my production, it does not seem to work, APIclientID and APIclientsecret still return empty values (while $System.Util.GetEnviron("APIclientid") returns the expected value)

Am I doing something wrong ?

Kind regards,

Quick update on the situation.
Instead of using an assign to use %JSONExportToStream I used a code action :

Then I have dispatched the whole Ens.StreamContainer "context.streamContainer" instead of the stream itself context.streamContainer.stream

And I finally managed to write the in the output file !!!


I'm not sure i'm doing it the best way but it worked!

Should I improve my whole production or is it OK the way it is ?

Kind regards!

Hi Luis,
Thank you for your reply.

I tried to the following Business Process but I still does not work.
Did I understand well your reply ?

AgendaOut being my BO (EnsLib.File.PassthroughOperation ; EnsLib.File.OutboundAdapter)
FHIRINTEROPPKG.HL7toRawJSON2 being my DTL transforming a HL7 Input to a Demo.MyApp.Messages.JSONEvent5 target class.

I have tried to use my production with this Business Process but I am encountering an issue at the Business Operation step :

Eduard, I also tried to import your method and use it in my business process but it did not work as well... Maybe i'm using it wrong? That is why I tried to do as mentioned just above by using %JSONExportToStream since I knew that my DTL produces a target class that Extends JSON Adaptor.

Thank you four help,
Kind regards