I don't think so.
FHIR is an API rest. FHIR has not been designed to apply deduplication and data merge business rules. This is the role of HealthShare not the role of an FHIR endpoint.
- Log in to post comments
I don't think so.
FHIR is an API rest. FHIR has not been designed to apply deduplication and data merge business rules. This is the role of HealthShare not the role of an FHIR endpoint.
It seems that 2022.x have the same capability that 2023.x, I mean by that 2022.x support conditional Update and Create.
Then if you want to update only that is needed to be updated you have to go with JSON Patch :
PATCH https://localhost:4443/fhir/r4/Organization?identifier=HEAD
Accept: application/fhir+json
Content-Type: application/json-patch+json
[
{
"op": "add",
"path": "/telecom/0",
"value": {
"system": "phone",
"value": "some-other-phone"
}
}
]Result :
GET https://localhost:4443/fhir/r4/Organization?identifier=HEAD&_elements=telecom
Accept: application/fhir+json{
"resourceType": "Bundle",
"id": "9962a9aa-668b-11ee-8f79-0242ac160003",
"type": "searchset",
"timestamp": "2023-10-09T10:05:38Z",
"total": 1,
"link": [
{
"relation": "self",
"url": "https://localhost:4443/fhir/r4/Organization?_elements=telecom&identifier=HEAD"
}
],
"entry": [
{
"fullUrl": "https://localhost:4443/fhir/r4/Organization/1",
"resource": {
"resourceType": "Organization",
"telecom": [
{
"system": "phone",
"value": "some-other-phone"
},
{
"system": "phone",
"value": "some-other-phone"
}
],
"meta": {
"tag": [
{
"system": "http://terminology.hl7.org/CodeSystem/v3-ObservationValue",
"code": "SUBSETTED",
"display": "Resource content reduced because _elements search parameter"
}
]
}
},
"search": {
"mode": "match"
}
}
]
}Attention JSON patch only work with direct access to the resource, if you need json path in a bundle you have to pass it as a base64 :
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [
{
"fullUrl": "Patient/1",
"resource": {
"resourceType": "Binary",
"contentType": "application/json-patch+json",
"data": "WyB7ICJvcCI6InJlcGxhY2UiLCAicGF0aCI6Ii9hY3RpdmUiLCAidmFsdWUiOmZhbHNlIH0gXQ=="
},
"request": {
"method": "PATCH",
"url": "Patient/1"
}
}
]
}
Example of a json path in a bundle for the patient 1 with this patch :
[ { "op":"replace", "path":"/active", "value":false } ]{
"entry":
[
{
"request":
{
"method": "PUT",
"url": "Organization?identifier=EFG"
},
"resource":
{
"active": true,
"identifier":
[
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "EFG"
}
],
"name": "Allgemeine Einrichtung EFG.",
"partOf":
{
"display": "HEAD",
"reference": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd"
},
"resourceType": "Organization",
"telecom":
[
{
"system": "phone",
"value": "some-phonenr"
},
{
"system": "fax",
"value": "some-faxnr"
}
],
"type":
[
{
"coding":
[
{
"code": "2",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "KL",
"display": "Klinik m. Poliklinik",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
]
}
},
{
"request":
{
"method": "PUT",
"url": "Organization?identifier=AEAP"
},
"resource":
{
"active": true,
"identifier":
[
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "AEAP"
}
],
"name": "Allgemeine Einrichtung AEAP",
"partOf":
{
"display": "HEAD",
"reference": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd"
},
"resourceType": "Organization",
"telecom":
[
{
"system": "phone",
"value": "some-phonenr"
},
{
"system": "fax",
"value": "some-faxnr"
}
],
"type":
[
{
"coding":
[
{
"code": "2",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "KL",
"display": "Klinik m. Poliklinik",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
]
}
},
{
"fullUrl": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd",
"request":
{
"method": "PUT",
"url": "Organization?identifier=HEAD"
},
"resource":
{
"active": true,
"address":
[
{
"line":
[
"Some street 2"
],
"type": "postal",
"use": "work"
}
],
"identifier":
[
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "HEAD"
}
],
"name": "Some Hospital",
"partOf":
{
"display": "NULL"
},
"resourceType": "Organization",
"telecom":
[
{
"system": "phone",
"value": "some-phonenr"
}
],
"type":
[
{
"coding":
[
{
"code": "0",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "SHOS",
"display": "Some Hospital",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
]
}
}
],
"id": "214a4092-eeb9-440d-b645-0ec3b5b30bbe",
"resourceType": "Bundle",
"type": "transaction"
}Here is your bundle with conditional update instead of conditional create.
Result on IRIS 2023.2 :
First call :
{
"resourceType": "Bundle",
"id": "409996a4-644b-11ee-8e3a-0242ac160003",
"type": "transaction-response",
"timestamp": "2023-10-06T13:07:35Z",
"entry": [
{
"response": {
"status": "201",
"location": "https://localhost:4443/fhir/r4/Organization/2",
"etag": "W/\"1\"",
"lastModified": "2023-10-06T13:07:35Z"
}
},
{
"response": {
"status": "201",
"location": "https://localhost:4443/fhir/r4/Organization/3",
"etag": "W/\"1\"",
"lastModified": "2023-10-06T13:07:35Z"
}
},
{
"fullUrl": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd",
"response": {
"status": "201",
"location": "https://localhost:4443/fhir/r4/Organization/1",
"etag": "W/\"1\"",
"lastModified": "2023-10-06T13:07:35Z"
}
}
]
}as you can see we have 201 (mean created)
Second call :
{
"resourceType": "Bundle",
"id": "ccc5c35a-644b-11ee-8f77-0242ac160003",
"type": "transaction-response",
"timestamp": "2023-10-06T13:08:47Z",
"entry": [
{
"response": {
"status": "200",
"location": "https://localhost:4443/fhir/r4/Organization/2",
"etag": "W/\"2\"",
"lastModified": "2023-10-06T13:08:48Z"
}
},
{
"response": {
"status": "200",
"location": "https://localhost:4443/fhir/r4/Organization/3",
"etag": "W/\"2\"",
"lastModified": "2023-10-06T13:08:48Z"
}
},
{
"fullUrl": "urn:uuid:87a8f88a-5223-11ee-8c2c-005056b163cd",
"response": {
"status": "200",
"location": "https://localhost:4443/fhir/r4/Organization/1",
"etag": "W/\"2\"",
"lastModified": "2023-10-06T13:08:47Z"
}
}
]
}we can see 200 mean ok/updated.
Get all organization :
{
"resourceType": "Bundle",
"id": "56ccfcb4-644a-11ee-8f78-0242ac160003",
"type": "searchset",
"timestamp": "2023-10-06T13:09:50Z",
"total": 3,
"link": [
{
"relation": "self",
"url": "https://localhost:4443/fhir/r4/Organization"
}
],
"entry": [
{
"fullUrl": "https://localhost:4443/fhir/r4/Organization/1",
"resource": {
"active": true,
"address": [
{
"line": [
"Some street 2"
],
"type": "postal",
"use": "work"
}
],
"identifier": [
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "HEAD"
}
],
"name": "Some Hospital",
"partOf": {
"display": "NULL"
},
"resourceType": "Organization",
"telecom": [
{
"system": "phone",
"value": "some-phonenr"
}
],
"type": [
{
"coding": [
{
"code": "0",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "SHOS",
"display": "Some Hospital",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
],
"id": "1",
"meta": {
"lastUpdated": "2023-10-06T13:08:47Z",
"versionId": "2"
}
},
"search": {
"mode": "match"
}
},
{
"fullUrl": "https://localhost:4443/fhir/r4/Organization/2",
"resource": {
"active": true,
"identifier": [
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "EFG"
}
],
"name": "Allgemeine Einrichtung EFG.",
"partOf": {
"display": "HEAD",
"reference": "Organization/1"
},
"resourceType": "Organization",
"telecom": [
{
"system": "phone",
"value": "some-phonenr"
},
{
"system": "fax",
"value": "some-faxnr"
}
],
"type": [
{
"coding": [
{
"code": "2",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "KL",
"display": "Klinik m. Poliklinik",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
],
"id": "2",
"meta": {
"lastUpdated": "2023-10-06T13:08:48Z",
"versionId": "2"
}
},
"search": {
"mode": "match"
}
},
{
"fullUrl": "https://localhost:4443/fhir/r4/Organization/3",
"resource": {
"active": true,
"identifier": [
{
"system": "https://fhir.domain.de/identifiers/domain-orgid",
"value": "AEAP"
}
],
"name": "Allgemeine Einrichtung AEAP",
"partOf": {
"display": "HEAD",
"reference": "Organization/1"
},
"resourceType": "Organization",
"telecom": [
{
"system": "phone",
"value": "some-phonenr"
},
{
"system": "fax",
"value": "some-faxnr"
}
],
"type": [
{
"coding": [
{
"code": "2",
"system": "https://fhir.domain.de/CodeSystem/domain-orglevel"
},
{
"code": "KL",
"display": "Klinik m. Poliklinik",
"system": "https://fhir.domain.de/CodeSystem/domain-orgtype"
}
]
}
],
"id": "3",
"meta": {
"lastUpdated": "2023-10-06T13:08:48Z",
"versionId": "2"
}
},
"search": {
"mode": "match"
}
}
]
}As you can see "partOf" are referencing internal id of HEAD.
TIP :
To quickly test on a fresh server, you can wipe data like this (only do it on dev server, your laptop, never in production !!! )
Set appKey = "/fhir/r4"
Set strategy = ##class(HS.FHIRServer.API.InteractionsStrategy).GetStrategyForEndpoint(appKey)
Set options("deleteDataOnly") = 1
Do strategy.Delete(.options)Try to follow the article, the last part is about the ingress, this will help you to expose the webgateway and have access to the portal.
Right now, I can't help you on specific features of EKS.
Hi Roy,
I can't tell about the "seed: path" but how to play with the Web Gateway sidecar you can follow this example :
https://community.intersystems.com/post/local-k8s-deployment-fhir-server
TL; DR :
In your topology definition :
## deploy webgateway (web server) nodes
webgateway:
image: k3d-registry.localhost:5000/intersystems/webgateway:2023.1.1.380.0-linux-amd64
type: apache
replicas: 1
applicationPaths:
- /csp/sys
- /fhir/r4
alternativeServers: LoadBalancing
loginSecret:
name: iris-webgateway-secret The iris-webgateway-secret :
kubectl create secret generic iris-webgateway-secret --from-literal='username=CSPSystem' --from-literal='password=SYS'The config file for iris :
[Actions]
ModifyService:Name=%Service_CallIn,Enabled=1,AutheEnabled=16
ModifyUser:Name=SuperUser,ChangePassword=0,PasswordHash=a31d24aecc0bfe560a7e45bd913ad27c667dc25a75cbfd358c451bb595b6bd52bd25c82cafaa23ca1dd30b3b4947d12d3bb0ffb2a717df29912b743a281f97c1,0a4c463a2fa1e7542b61aa48800091ab688eb0a14bebf536638f411f5454c9343b9aa6402b4694f0a89b624407a5f43f0a38fc35216bb18aab7dc41ef9f056b1,10000,SHA512
ModifyUser:Name=CSPSystem,ChangePassword=0,PasswordHash=a31d24aecc0bfe560a7e45bd913ad27c667dc25a75cbfd358c451bb595b6bd52bd25c82cafaa23ca1dd30b3b4947d12d3bb0ffb2a717df29912b743a281f97c1,0a4c463a2fa1e7542b61aa48800091ab688eb0a14bebf536638f411f5454c9343b9aa6402b4694f0a89b624407a5f43f0a38fc35216bb18aab7dc41ef9f056b1,10000,SHA512To generate PasswordHash :
docker run --rm -it containers.intersystems.com/intersystems/passwordhash:1.1 -algorithm SHA512 -workfactor 10000Add the configmap for iris :
kubectl create cm iriscluster-config --from-file common.cpf the topology of IRIS + Webgateway :
apiVersion: intersystems.com/v1alpha1
kind: IrisCluster
metadata:
name: sample
spec:
## provide InterSystems IRIS license key if required
# licenseKeySecret:
# name: iris-key-secret
## specify files used to customize the configurations of
## InterSystems IRIS nodes, including passwordHash parameter
## to set the default password, securing InterSystems IRIS
configSource:
name: iriscluster-config
## topology: defines node types to be deployed; only "data:" is required
topology:
data:
image: k3d-registry.localhost:5000/iris-oauth-fhir-iris:latest
## deploy webgateway (web server) nodes
webgateway:
image: k3d-registry.localhost:5000/intersystems/webgateway:2023.1.1.380.0-linux-amd64
type: apache
replicas: 1
applicationPaths:
- /csp/sys
- /fhir/r4
alternativeServers: LoadBalancing
loginSecret:
name: iris-webgateway-secret It's really impressive and easy to use.
May be that can be next generation iris terminal.
Love it, I will definitely use it !
I bet that the issue is related to the fact that recently IRIS 2023.2 was released. This version removed this method InstallFoundation from this class HS.HC.Util.Installer. This was a private method and it was not documented. But it was widely used by the community to install FHIR server.
TL;DR :
Change :
do ##class(HS.HC.Util.Installer).InstallFoundation()
to
Do ##class(HS.Util.Installer.Foundation).Install(namespace)
cf : https://community.intersystems.com/post/installfoundation-method-missing-iris-20232
Wow, neat work, I can see that in your source code that you have pre build grafana dashboard, can you share screenshot of them ?
after DP-API support for Python, now Hibernete support for Java !
Great job @Dmitry Maslennikov and @Yuri Marx
In theory you are all set. Just take for example the dockerfile.
You can also read the pom.xml file to take some insperation.
For example take a look how the jdbc driver is added to the project.
<dependency>
<groupId>intersystems</groupId>
<artifactId>jdbc</artifactId>
<version>3.3.0</version>
<scope>system</scope>
<systemPath>${pom.basedir}/lib/intersystems-jdbc-3.3.0.jar</systemPath>
</dependency>
To sum up, you need obviously the jdbc drive (you have one in the repo), the hibernate dialect, you also have one in the repo, for the hibernete dialet you can also have a look to the article of yuri : https://community.intersystems.com/post/using-new-intersystems-iris-hibernate-6-dialect-springboot-project.
Have fun with Iris and quarkus.
Hi, you will find here an example of Quarkus + IRIS + Hibernete :
yes, it is, i fixed the tutorial, thanks
Thanks and fixed.
BTW, now the new version of this project is in python :
import time
import os
import json
import iris
from FhirInteraction import Interaction, Strategy, OAuthInteraction
from google.oauth2 import id_token
from google.auth.transport import requests
import requests as rq
# The following is an example of a custom OAuthInteraction class that
class CustomOAuthInteraction(OAuthInteraction):
client_id = None
last_time_verified = None
time_interval = 5
def clear_instance(self):
self.token_string = None
self.oauth_client = None
self.base_url = None
self.username = None
self.token_obj = None
self.scopes = None
self.verify_search_results = None
def set_instance(self, token:str,oauth_client:str,base_url:str,username:str):
self.clear_instance()
if not token or not oauth_client:
# the token or oauth client is not set, skip the verification
return
global_time = iris.gref('^FHIR.OAuth2.Time')
if global_time[token[0:50]]:
self.last_time_verified = global_time[token[0:50]]
if self.last_time_verified and (time.time() - self.last_time_verified) < self.time_interval:
# the token was verified less than 5 seconds ago, skip the verification
return
self.token_string = token
self.oauth_client = oauth_client
self.base_url = base_url
self.username = username
# try to set the client id
try:
# first get the var env GOOGLE_CLIENT_ID is not set then None
self.client_id = os.environ.get('GOOGLE_CLIENT_ID')
# if not set, then by the secret.json file
if not self.client_id:
with open(os.environ.get('ISC_OAUTH_SECRET_PATH'),encoding='utf-8') as f:
data = json.load(f)
self.client_id = data['web']['client_id']
except FileNotFoundError:
pass
try:
self.verify_token(token)
except Exception as e:
self.clear_instance()
raise e
# token is valid, set the last time verified to now
global_time[token[0:50]]=time.time()
def verify_token(self,token:str):
# check if the token is an access token or an id token
if token.startswith('ya29.'):
self.verify_access_token(token)
else:
self.verify_id_token(token)
def verify_access_token(self,token:str):
# verify the access token is valid
# get with a timeout of 5 seconds
response = rq.get(f"https://www.googleapis.com/oauth2/v3/tokeninfo?access_token={token}",timeout=5)
try:
response.raise_for_status()
except rq.exceptions.HTTPError as e:
# the token is not valid
raise e
def verify_id_token(self,token:str):
# Verify the token and get the user info
idinfo = id_token.verify_oauth2_token(token, requests.Request(), self.client_id)
if idinfo['iss'] not in ['accounts.google.com', 'https://accounts.google.com']:
raise ValueError('Wrong issuer.')
def get_introspection(self)->dict:
return {}
def get_user_info(self,basic_auth_username:str,basic_auth_roles:str)->dict:
return {"Username":basic_auth_username,"Roles":basic_auth_roles}
def verify_resource_id_request(self,resource_type:str,resource_id:str,required_privilege:str):
pass
def verify_resource_content(self,resource_dict:dict,required_privilege:str,allow_shared_resource:bool):
pass
def verify_history_instance_response(self,resource_type:str,resource_dict:dict,required_privilege:str):
pass
def verify_delete_request(self,resource_type:str,resource_id:str,required_privilege:str):
pass
def verify_search_request(self,
resource_type:str,
compartment_resource_type:str,
compartment_resource_id:str,
parameters:'iris.HS.FHIRServer.API.Data.QueryParameters',
required_privilege:str):
pass
def verify_system_level_request(self):
pass
class CustomStrategy(Strategy):
def on_get_capability_statement(self, capability_statement):
# Example : del resources Account
capability_statement['rest'][0]['resource'] = [resource for resource in capability_statement['rest'][0]['resource'] if resource['type'] != 'Account']
return capability_statement
class CustomInteraction(Interaction):
def on_before_request(self, fhir_service, fhir_request, body, timeout):
#Extract the user and roles for this request
#so consent can be evaluated.
self.requesting_user = fhir_request.Username
self.requesting_roles = fhir_request.Roles
def on_after_request(self, fhir_service, fhir_request, fhir_response, body):
#Clear the user and roles between requests.
self.requesting_user = ""
self.requesting_roles = ""
def post_process_read(self, fhir_object):
#Evaluate consent based on the resource and user/roles.
#Returning 0 indicates this resource shouldn't be displayed - a 404 Not Found
#will be returned to the user.
return self.consent(fhir_object['resourceType'],
self.requesting_user,
self.requesting_roles)
def post_process_search(self, rs, resource_type):
#Iterate through each resource in the search set and evaluate
#consent based on the resource and user/roles.
#Each row marked as deleted and saved will be excluded from the Bundle.
rs._SetIterator(0)
while rs._Next():
if not self.consent(rs.ResourceType,
self.requesting_user,
self.requesting_roles):
#Mark the row as deleted and save it.
rs.MarkAsDeleted()
rs._SaveRow()
def consent(self, resource_type, user, roles):
#Example consent logic - only allow users with the role '%All' to see
#Observation resources.
if resource_type == 'Observation':
if '%All' in roles:
return True
else:
return False
else:
return TrueBased on https://community.intersystems.com/post/iris-fhir-python-strategy
That how i read Stream and Write stream with Embedded Python :
Read Stream :
def stream_to_string(stream)-> str:
string = ""
stream.Rewind()
while not stream.AtEnd:
string += stream.Read(1024)
return stringWrite Stream :
def string_to_stream(string:str):
stream = iris.cls('%Stream.GlobalCharacter')._New()
n = 1024
chunks = [string[i:i+n] for i in range(0, len(string), n)]
for chunk in chunks:
stream.Write(chunk)
return streamHere is an example code to modify or create a production XData block, i guess you can adapt to you needs :
ClassMethod CreateProduction(
package As %String = "test",
name As %String = "AutoCreatedProduction",
xdata As %CharacterStream) As %Status
{
#Dim produtionClassName As %String = package _ "." _ name
If ('$ZName(produtionClassName, 4))
{
Return $System.Status.Error(5001, "Invalid Production package or name.")
}
#Dim productionDefinition As %Dictionary.ClassDefinition
// Check if the production already exists
If (##class(%Dictionary.ClassDefinition).%ExistsId(produtionClassName))
{
// Open the production
set productionDefinition = ##class(%Dictionary.ClassDefinition).%OpenId(produtionClassName)
}
Else
{
// Create the production definition
set productionDefinition = ##Class(%Dictionary.ClassDefinition).%New()
}
//
Set productionDefinition.Name = produtionClassName
Set productionDefinition.Super = "Ens.Production"
Set productionDefinition.ClassVersion = 25
//
// Check if the XData Definition already exists
If (##Class(%Dictionary.XDataDefinition).%ExistsId(produtionClassName_"||ProductionDefinition"))
{
// delete the XData Definition
$$$ThrowOnError(##Class(%Dictionary.XDataDefinition).%DeleteId(produtionClassName_"||ProductionDefinition"))
}
#Dim xdataDefinition As %Dictionary.XDataDefinition = ##Class(%Dictionary.XDataDefinition).%New()
//
Set xdataDefinition.Name = "ProductionDefinition"
//
Do xdataDefinition.Data.CopyFrom(xdata)
//
// Insert XData Definition into Production Definition
Do productionDefinition.XDatas.Insert(xdataDefinition)
//
#Dim statusCode As %Status = productionDefinition.%Save()
//
If ($System.Status.IsError(statusCode))
{
Return statusCode
}
// Compile the production class
return $System.OBJ.Compile(produtionClassName,"k-d")
}SELECT COUNT(*)
from Ens.MessageHeader
WHERE SourceConfigName = 'EPIC_SIU_IN'
AND TO_NUMBER(SessionId) = %ID
AND %ID >= (SELECT TOP 1 %ID FROM Ens.MessageHeader a WHERE TimeCreated >= '2016-07-27 05:00:00.000' ORDER BY TimeCreated ASC)
AND %ID <= (SELECT TOP 1 %ID FROM Ens.MessageHeader a WHERE TimeCreated < '2016-07-28 05:00:00.000' ORDER BY TimeCreated DESC)Just change the date here and see how fast it is.
The idea is to use index and the fastest index in MessageHeader is %ID.
🎉, great news !
I join Sergei on latest tag in addition of latest-cd and latest-em.
What I have done on one project, is to copy past the code and use it.
I haven't used ZPM.
Here is an example : https://github.com/grongierisc/RestToDicom
If you want mock ObjectScript classes use this :
https://github.com/GendronAC/InterSystems-UnitTest-Mocking
I had used it, it work quiet well.
If you want mock ObjectScript classes use this :
https://github.com/GendronAC/InterSystems-UnitTest-Mocking
I had used it, it work quiet well.
Hi,
Have a look at this class (https://github.com/grongierisc/RestToDicom/blob/master/src/RestToDicom/…).
It's from ENSDEMO, it's kind of a mock that fake an DICOM modality and create a fake response for ECHO and C-FIND-RQ. It can be a good start.
Yes without any issue, you can mix it as much as you want it.
You have a full example here : https://github.com/grongierisc/RestToDicom/
What a neat article, very useful and with a lot of details.
Thanks :)
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")
Good idea, it environment variable is not found switch to settings value.
Hi Evegny,
You can do it in the following way:
$system.Util.GetEnviron("MY_ENV_VAR")os.environ['MY_ENV_VAR']iop (interoperabilty on python) to import production in python way :settings.py
import os
PRODUCTIONS = [
{
'shvarov.i14y.Production': {
"Item": [
{
"@Name": "shvarov.i14y.ChatOperation",
"@ClassName": "Telegram.BusinessOperation",
"@Category": "Reddit",
"@PoolSize": "1",
"@Enabled": "true",
"@Foreground": "false",
"@Comment": "",
"@LogTraceEvents": "false",
"@Schedule": "",
"Setting": [
{
"@Target": "Adapter",
"@Name": "SSLConfig",
"#text": "default"
},
{
"@Target": "Adapter",
"@Name": "Token",
"#text": os.environ['TELEGRAM_TOKEN']
}
]
}
]
}
}
]
and then in the terminal:
$ iop -M settings.py
More information about iop and settings.py here
Hi Evgeny,
Thanks for your feedback.
The -x option is for status, because -s is already used to start a production.
The -e option is for export, and the export can be imported with the -m option.
-m option stand for migrate, because I took the inspiration from the migrate command form django.
I hope it's more clear now.
But if you think, -i for import and -e for export is more clear, I can change it.
Same for -s and -x. What do you think can be short for status?
Awesome, how do you position this tool compared to irissqlcli if it had a web version?
I love it <3, for the more you are using the community driver DB-API :)