iris-pex-embedded-python 3.5.5b4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. grongier/__init__.py +0 -0
  2. grongier/cls/Grongier/PEX/BusinessOperation.cls +8 -0
  3. grongier/cls/Grongier/PEX/BusinessProcess.cls +13 -0
  4. grongier/cls/Grongier/PEX/BusinessService.cls +8 -0
  5. grongier/cls/Grongier/PEX/Common.cls +10 -0
  6. grongier/cls/Grongier/PEX/Director.cls +10 -0
  7. grongier/cls/Grongier/PEX/Duplex/Operation.cls +4 -0
  8. grongier/cls/Grongier/PEX/Duplex/Process.cls +13 -0
  9. grongier/cls/Grongier/PEX/Duplex/Service.cls +4 -0
  10. grongier/cls/Grongier/PEX/InboundAdapter.cls +8 -0
  11. grongier/cls/Grongier/PEX/Message.cls +13 -0
  12. grongier/cls/Grongier/PEX/OutboundAdapter.cls +8 -0
  13. grongier/cls/Grongier/PEX/PickleMessage.cls +13 -0
  14. grongier/cls/Grongier/PEX/PrivateSession/Duplex.cls +8 -0
  15. grongier/cls/Grongier/PEX/PrivateSession/Message/Ack.cls +14 -0
  16. grongier/cls/Grongier/PEX/PrivateSession/Message/Poll.cls +14 -0
  17. grongier/cls/Grongier/PEX/PrivateSession/Message/Start.cls +14 -0
  18. grongier/cls/Grongier/PEX/PrivateSession/Message/Stop.cls +14 -0
  19. grongier/cls/Grongier/PEX/Test.cls +10 -0
  20. grongier/cls/Grongier/PEX/Utils.cls +10 -0
  21. grongier/cls/Grongier/Service/WSGI.cls +4 -0
  22. grongier/pex/__init__.py +24 -0
  23. grongier/pex/__main__.py +4 -0
  24. grongier/pex/_business_host.py +1 -0
  25. grongier/pex/_cli.py +4 -0
  26. grongier/pex/_common.py +1 -0
  27. grongier/pex/_director.py +1 -0
  28. grongier/pex/_utils.py +1 -0
  29. grongier/pex/wsgi/handlers.py +104 -0
  30. iop/__init__.py +25 -0
  31. iop/__main__.py +4 -0
  32. iop/_async_request.py +67 -0
  33. iop/_business_host.py +256 -0
  34. iop/_business_operation.py +75 -0
  35. iop/_business_process.py +224 -0
  36. iop/_business_service.py +63 -0
  37. iop/_cli.py +247 -0
  38. iop/_common.py +334 -0
  39. iop/_debugpy.py +187 -0
  40. iop/_decorators.py +49 -0
  41. iop/_director.py +301 -0
  42. iop/_dispatch.py +136 -0
  43. iop/_generator_request.py +30 -0
  44. iop/_inbound_adapter.py +34 -0
  45. iop/_iris.py +8 -0
  46. iop/_log_manager.py +100 -0
  47. iop/_message.py +40 -0
  48. iop/_message_validator.py +49 -0
  49. iop/_outbound_adapter.py +23 -0
  50. iop/_private_session_duplex.py +103 -0
  51. iop/_private_session_process.py +41 -0
  52. iop/_remote.py +91 -0
  53. iop/_serialization.py +199 -0
  54. iop/_utils.py +671 -0
  55. iop/cls/IOP/BusinessOperation.cls +35 -0
  56. iop/cls/IOP/BusinessProcess.cls +156 -0
  57. iop/cls/IOP/BusinessService.cls +40 -0
  58. iop/cls/IOP/Common.cls +569 -0
  59. iop/cls/IOP/Director.cls +70 -0
  60. iop/cls/IOP/Duplex/Operation.cls +29 -0
  61. iop/cls/IOP/Duplex/Process.cls +229 -0
  62. iop/cls/IOP/Duplex/Service.cls +9 -0
  63. iop/cls/IOP/Generator/Message/Ack.cls +31 -0
  64. iop/cls/IOP/Generator/Message/Poll.cls +31 -0
  65. iop/cls/IOP/Generator/Message/Start.cls +15 -0
  66. iop/cls/IOP/Generator/Message/StartPickle.cls +15 -0
  67. iop/cls/IOP/Generator/Message/Stop.cls +32 -0
  68. iop/cls/IOP/InboundAdapter.cls +22 -0
  69. iop/cls/IOP/Message/JSONSchema.cls +125 -0
  70. iop/cls/IOP/Message.cls +754 -0
  71. iop/cls/IOP/OutboundAdapter.cls +36 -0
  72. iop/cls/IOP/PickleMessage.cls +58 -0
  73. iop/cls/IOP/PrivateSession/Duplex.cls +260 -0
  74. iop/cls/IOP/PrivateSession/Message/Ack.cls +32 -0
  75. iop/cls/IOP/PrivateSession/Message/Poll.cls +32 -0
  76. iop/cls/IOP/PrivateSession/Message/Start.cls +31 -0
  77. iop/cls/IOP/PrivateSession/Message/Stop.cls +48 -0
  78. iop/cls/IOP/Projection.cls +49 -0
  79. iop/cls/IOP/Service/Remote/Handler.cls +30 -0
  80. iop/cls/IOP/Service/Remote/Rest/v1.cls +97 -0
  81. iop/cls/IOP/Service/WSGI.cls +310 -0
  82. iop/cls/IOP/Test.cls +85 -0
  83. iop/cls/IOP/Utils.cls +503 -0
  84. iop/cls/IOP/Wrapper.cls +58 -0
  85. iop/wsgi/handlers.py +104 -0
  86. iris_pex_embedded_python-3.5.5b4.dist-info/METADATA +91 -0
  87. iris_pex_embedded_python-3.5.5b4.dist-info/RECORD +91 -0
  88. iris_pex_embedded_python-3.5.5b4.dist-info/WHEEL +5 -0
  89. iris_pex_embedded_python-3.5.5b4.dist-info/entry_points.txt +2 -0
  90. iris_pex_embedded_python-3.5.5b4.dist-info/licenses/LICENSE +21 -0
  91. iris_pex_embedded_python-3.5.5b4.dist-info/top_level.txt +2 -0
@@ -0,0 +1,30 @@
1
+ from typing import Any, Optional, Union
2
+
3
+ from . import _iris
4
+ from ._dispatch import dispatch_serializer
5
+
6
+ class _GeneratorRequest:
7
+ """Generator class to interetate over responses from a request.
8
+ This class is used to handle the responses from a request in a generator-like manner."""
9
+
10
+ def __init__(self, host: Any, target: str, request: Any,
11
+ timeout: int = -1, description: Optional[str] = None) -> None:
12
+ self.host = host
13
+ self.target = target
14
+ self.request = request
15
+
16
+ ack_response = self.host.send_request_sync(self.target, dispatch_serializer(self.request, is_generator=True),
17
+ timeout=timeout, description=description)
18
+
19
+ if ack_response is None or not ack_response._IsA("IOP.Generator.Message.Ack"):
20
+ raise RuntimeError("Failed to send request, no acknowledgment received.")
21
+
22
+ def __iter__(self):
23
+ return self
24
+
25
+ def __next__(self):
26
+ poll = _iris.get_iris().IOP.Generator.Message.Poll._New()
27
+ rsp = self.host.send_request_sync(self.target, poll)
28
+ if rsp is None or (hasattr(rsp, '_IsA') and rsp._IsA("IOP.Generator.Message.Stop")):
29
+ raise StopIteration("No more responses available.")
30
+ return rsp
@@ -0,0 +1,34 @@
1
+ from ._common import _Common
2
+
3
+ class _InboundAdapter(_Common):
4
+ """ Responsible for receiving the data from the external system, validating the data,
5
+ and sending it to the business service by calling the BusinessHost.ProcessInput() method.
6
+ """
7
+ BusinessHost = business_host = business_host_python = None
8
+
9
+ def on_task(self):
10
+ """ Called by the production framework at intervals determined by the business service CallInterval property.
11
+ It is responsible for receiving the data from the external system, validating the data, and sending it in a message to the business service OnProcessInput method.
12
+ The message can have any structure agreed upon by the inbound adapter and the business service.
13
+ """
14
+ return self.OnTask()
15
+
16
+ def _set_iris_handles(self, handle_current, handle_partner):
17
+ """ For internal use only. """
18
+ self.iris_handle = handle_current
19
+ self.BusinessHost = handle_partner
20
+ self.business_host = handle_partner
21
+ try:
22
+ self.business_host_python = handle_partner.GetClass()
23
+ except:
24
+ pass
25
+ return
26
+
27
+
28
+ def OnTask(self):
29
+ """ DEPRECATED : use on_task
30
+ Called by the production framework at intervals determined by the business service CallInterval property.
31
+ It is responsible for receiving the data from the external system, validating the data, and sending it in a message to the business service OnProcessInput method.
32
+ The message can have any structure agreed upon by the inbound adapter and the business service.
33
+ """
34
+ return
iop/_iris.py ADDED
@@ -0,0 +1,8 @@
1
+ import os
2
+ from typing import Optional
3
+
4
+ def get_iris(namespace: Optional[str]=None)->'iris': # type: ignore
5
+ if namespace:
6
+ os.environ['IRISNAMESPACE'] = namespace
7
+ import iris
8
+ return iris
iop/_log_manager.py ADDED
@@ -0,0 +1,100 @@
1
+ import logging
2
+
3
+ from . import _iris
4
+
5
+ class LogManager:
6
+ """Manages logging integration between Python's logging module and IRIS."""
7
+
8
+ @staticmethod
9
+ def get_logger(class_name: str, to_console: bool = False) -> logging.Logger:
10
+ """Get a logger instance configured for IRIS integration.
11
+
12
+ Args:
13
+ class_name: Name of the class logging the message
14
+ method_name: Optional name of the method logging the message
15
+ to_console: If True, log to the console instead of IRIS
16
+
17
+ Returns:
18
+ Logger instance configured for IRIS integration
19
+ """
20
+ logger = logging.Logger(f"{class_name}")
21
+
22
+ # Only add handler if none exists
23
+ if not logger.handlers:
24
+ handler = IRISLogHandler(to_console=to_console)
25
+ formatter = logging.Formatter('%(message)s')
26
+ handler.setFormatter(formatter)
27
+ logger.addHandler(handler)
28
+ # Set the log level to the lowest level to ensure all messages are sent to IRIS
29
+ logger.setLevel(logging.DEBUG)
30
+
31
+ return logger
32
+
33
+ class IRISLogHandler(logging.Handler):
34
+ """Custom logging handler that routes Python logs to IRIS logging system."""
35
+
36
+ def __init__(self, to_console: bool = False):
37
+ """Initialize the IRIS logging handler."""
38
+ super().__init__()
39
+ self.to_console = to_console
40
+
41
+ # Map Python logging levels to IRIS logging methods
42
+ self.level_map = {
43
+ logging.DEBUG: 5,
44
+ logging.INFO: 4,
45
+ logging.WARNING: 3,
46
+ logging.ERROR: 2,
47
+ logging.CRITICAL: 6,
48
+ }
49
+ # Map Python logging levels to IRIS logging Console level
50
+ self.level_map_console = {
51
+ logging.DEBUG: -1,
52
+ logging.INFO: 0,
53
+ logging.WARNING: 1,
54
+ logging.ERROR: 2,
55
+ logging.CRITICAL: 3,
56
+ }
57
+
58
+ def format(self, record: logging.LogRecord) -> str:
59
+ """Format the log record into a string.
60
+
61
+ Args:
62
+ record: The logging record to format
63
+
64
+ Returns:
65
+ Formatted log message
66
+ """
67
+ return record.getMessage()
68
+
69
+ def emit(self, record: logging.LogRecord) -> None:
70
+ """Route the log record to appropriate IRIS logging method.
71
+
72
+ Args:
73
+ record: The logging record to emit
74
+ """
75
+ # Extract class and method names with fallbacks
76
+ class_name = getattr(record, "class_name", record.name)
77
+ method_name = getattr(record, "method_name", record.funcName)
78
+
79
+ # Format message and get full method path
80
+ message = self.format(record)
81
+ method_path = f"{class_name}.{method_name}"
82
+
83
+ # Determine if console logging should be used
84
+ use_console = self.to_console or getattr(record, "to_console", False)
85
+
86
+ if use_console:
87
+ _iris.get_iris().cls("%SYS.System").WriteToConsoleLog(
88
+ message,
89
+ 0,
90
+ self.level_map_console.get(record.levelno, 0),
91
+ method_path
92
+ )
93
+ else:
94
+ log_level = self.level_map.get(record.levelno, 4)
95
+ _iris.get_iris().cls("Ens.Util.Log").Log(
96
+ log_level,
97
+ class_name,
98
+ method_name,
99
+ message
100
+ )
iop/_message.py ADDED
@@ -0,0 +1,40 @@
1
+ from typing import Any, Optional
2
+
3
+ from pydantic import BaseModel
4
+
5
+ class _Message:
6
+ """ The abstract class that is the superclass for persistent messages sent from one component to another.
7
+ This class has no properties or methods. Users subclass Message and add properties.
8
+ The IOP framework provides the persistence to objects derived from the Message class.
9
+ """
10
+ _iris_id: Optional[str] = None
11
+
12
+ def get_iris_id(self) -> Optional[str]:
13
+ """Get the IRIS ID of the message."""
14
+ return self._iris_id
15
+
16
+ class _PickleMessage(_Message):
17
+ """ The abstract class that is the superclass for persistent messages sent from one component to another.
18
+ This class has no properties or methods. Users subclass Message and add properties.
19
+ The IOP framework provides the persistence to objects derived from the Message class.
20
+ """
21
+ pass
22
+
23
+ class _PydanticMessage(BaseModel):
24
+ """Base class for Pydantic-based messages that can be serialized to IRIS."""
25
+
26
+ _iris_id: Optional[str] = None
27
+
28
+ def __init__(self, **data: Any):
29
+ super().__init__(**data)
30
+
31
+ def get_iris_id(self) -> Optional[str]:
32
+ """Get the IRIS ID of the message."""
33
+ return self._iris_id
34
+
35
+ class _PydanticPickleMessage(_PydanticMessage):
36
+ """Base class for Pydantic-based messages that can be serialized to IRIS."""
37
+
38
+ def __init__(self, **data: Any):
39
+ super().__init__(**data)
40
+
@@ -0,0 +1,49 @@
1
+ import dataclasses
2
+ from typing import Any, Type
3
+
4
+ from ._message import _Message, _PickleMessage, _PydanticPickleMessage, BaseModel
5
+
6
+
7
+ def is_message_instance(obj: Any) -> bool:
8
+ """Check if object is a valid Message instance."""
9
+ if isinstance(obj, BaseModel):
10
+ return True
11
+ if is_message_class(type(obj)):
12
+ if not dataclasses.is_dataclass(obj):
13
+ raise TypeError(f"{type(obj).__module__}.{type(obj).__qualname__} must be a dataclass")
14
+ return True
15
+ return False
16
+
17
+
18
+ def is_pickle_message_instance(obj: Any) -> bool:
19
+ """Check if object is a PickleMessage instance."""
20
+ if isinstance(obj, _PydanticPickleMessage):
21
+ return True
22
+ if is_pickle_message_class(type(obj)):
23
+ return True
24
+ return False
25
+
26
+
27
+ def is_iris_object_instance(obj: Any) -> bool:
28
+ """Check if object is an IRIS persistent object."""
29
+ return (obj is not None and
30
+ type(obj).__module__.startswith('iris') and
31
+ (obj._IsA("%Persistent") or obj._IsA("%Stream.Object")))
32
+ # Stream.Object are used for HTTP InboundAdapter/OutboundAdapter
33
+
34
+
35
+ def is_message_class(klass: Type) -> bool:
36
+ """Check if class is a Message type."""
37
+ if issubclass(klass, _Message):
38
+ return True
39
+ return False
40
+
41
+
42
+
43
+ def is_pickle_message_class(klass: Type) -> bool:
44
+ """Check if class is a PickleMessage type."""
45
+ if issubclass(klass, _PickleMessage):
46
+ return True
47
+ if issubclass(klass, _PydanticPickleMessage):
48
+ return True
49
+ return False
@@ -0,0 +1,23 @@
1
+ from ._common import _Common
2
+
3
+ class _OutboundAdapter(_Common):
4
+ """ Responsible for sending the data to the external system."""
5
+ BusinessHost = business_host = business_host_python = None
6
+
7
+ def on_keepalive(self):
8
+ """
9
+ > This function is called when the server sends a keepalive message
10
+ """
11
+ return
12
+
13
+ def _set_iris_handles(self, handle_current, handle_partner):
14
+ """ For internal use only. """
15
+ self.iris_handle = handle_current
16
+ self.BusinessHost = handle_partner
17
+ self.business_host = handle_partner
18
+ try:
19
+ self.business_host_python = handle_partner.GetClass()
20
+ except:
21
+ pass
22
+ return
23
+
@@ -0,0 +1,103 @@
1
+ import importlib
2
+
3
+ from ._business_host import _BusinessHost
4
+ from ._decorators import input_deserializer, input_serializer_param, output_serializer, input_serializer, output_deserializer
5
+ from ._dispatch import create_dispatch, dispach_message
6
+
7
+ class _PrivateSessionDuplex(_BusinessHost):
8
+
9
+ Adapter = adapter = None
10
+ _wait_for_next_call_interval = False
11
+ DISPATCH = []
12
+
13
+ def on_message(self, request):
14
+ """ Called when the business operation receives a message from another production component.
15
+ Typically, the operation will either send the message to the external system or forward it to a business process or another business operation.
16
+ If the operation has an adapter, it uses the Adapter.invoke() method to call the method on the adapter that sends the message to the external system.
17
+ If the operation is forwarding the message to another production component, it uses the SendRequestAsync() or the SendRequestSync() method
18
+
19
+ Parameters:
20
+ request: An instance of either a subclass of Message or of IRISObject containing the incoming message for the business operation.
21
+
22
+ Returns:
23
+ The response object
24
+ """
25
+ pass
26
+
27
+ @input_deserializer
28
+ @output_serializer
29
+ def _dispatch_on_message(self, request):
30
+ """ For internal use only. """
31
+ return dispach_message(self,request)
32
+
33
+ def _set_iris_handles(self, handle_current, handle_partner):
34
+ """ For internal use only. """
35
+ self.iris_handle = handle_current
36
+ if type(handle_partner).__module__.find('iris') == 0:
37
+ if (handle_partner._IsA("Grongier.PEX.InboundAdapter") or handle_partner._IsA("Grongier.PEX.OutboundAdapter")
38
+ or handle_partner._IsA("IOP.InboundAdapter") or handle_partner._IsA("IOP.OutboundAdapter")):
39
+ module = importlib.import_module(handle_partner.GetModule())
40
+ handle_partner = getattr(module, handle_partner.GetClassname())()
41
+ self.Adapter = self.adapter = handle_partner
42
+ return
43
+
44
+ @input_deserializer
45
+ @output_serializer
46
+ def _dispatch_on_process_input(self, request):
47
+ """ For internal use only. """
48
+ return self.on_process_input(request)
49
+
50
+ def on_process_input(self, message_input):
51
+ """ Receives the message from the inbond adapter via the PRocessInput method and is responsible for forwarding it to target business processes or operations.
52
+ If the business service does not specify an adapter, then the default adapter calls this method with no message
53
+ and the business service is responsible for receiving the data from the external system and validating it.
54
+
55
+ Parameters:
56
+ message_input: an instance of IRISObject or subclass of Message containing the data that the inbound adapter passes in.
57
+ The message can have any structure agreed upon by the inbound adapter and the business service.
58
+ """
59
+ return
60
+
61
+ @input_serializer_param(0,'document')
62
+ @output_deserializer
63
+ def send_document_to_process(self, document):
64
+ """ Send the specified message to the target business process or business operation synchronously.
65
+
66
+ Parameters:
67
+ target: a string that specifies the name of the business process or operation to receive the request.
68
+ The target is the name of the component as specified in the Item Name property in the production definition, not the class name of the component.
69
+ request: specifies the message to send to the target. The request is either an instance of a class that is a subclass of Message class or of IRISObject class.
70
+ If the target is a build-in ObjectScript component, you should use the IRISObject class. The IRISObject class enables the PEX framework to convert the message to a class supported by the target.
71
+ timeout: an optional integer that specifies the number of seconds to wait before treating the send request as a failure. The default value is -1, which means wait forever.
72
+ description: an optional string parameter that sets a description property in the message header. The default is None.
73
+ Returns:
74
+ the response object from target.
75
+ Raises:
76
+ TypeError: if request is not of type Message or IRISObject.
77
+ """
78
+
79
+ return self.iris_handle.dispatchSendDocumentToProcess(document)
80
+
81
+ @input_deserializer
82
+ @output_serializer
83
+ def _dispatch_on_private_session_started(self, source_config_name,self_generated):
84
+ """ For internal use only. """
85
+
86
+ return_object = self.on_private_session_started(source_config_name,self_generated)
87
+
88
+ return return_object
89
+
90
+ def on_private_session_started(self,source_config_name,self_generated):
91
+ pass
92
+
93
+ @input_deserializer
94
+ @output_serializer
95
+ def _dispatch_on_private_session_stopped(self, source_config_name,self_generated,message):
96
+ """ For internal use only. """
97
+
98
+ return_object = self.on_private_session_stopped(source_config_name,self_generated,message)
99
+
100
+ return return_object
101
+
102
+ def on_private_session_stopped(self,source_config_name,self_generated,message):
103
+ pass
@@ -0,0 +1,41 @@
1
+ from ._business_process import _BusinessProcess
2
+ from ._decorators import input_deserializer, output_serializer, input_serializer, output_deserializer
3
+
4
+ class _PrivateSessionProcess(_BusinessProcess):
5
+
6
+ @input_deserializer
7
+ @output_serializer
8
+ def _dispatch_on_document(self, host_object,source_config_name, request):
9
+ """ For internal use only. """
10
+ self._restore_persistent_properties(host_object)
11
+ return_object = self.on_document(source_config_name,request)
12
+ self._save_persistent_properties(host_object)
13
+ return return_object
14
+
15
+ def on_document(self,source_config_name,request):
16
+ pass
17
+
18
+
19
+ @input_deserializer
20
+ @output_serializer
21
+ def _dispatch_on_private_session_started(self, host_object, source_config_name,self_generated):
22
+ """ For internal use only. """
23
+ self._restore_persistent_properties(host_object)
24
+ return_object = self.on_private_session_started(source_config_name,self_generated)
25
+ self._save_persistent_properties(host_object)
26
+ return return_object
27
+
28
+ def on_private_session_started(self,source_config_name,self_generated):
29
+ pass
30
+
31
+ @input_deserializer
32
+ @output_serializer
33
+ def _dispatch_on_private_session_stopped(self, host_object, source_config_name,self_generated,message):
34
+ """ For internal use only. """
35
+ self._restore_persistent_properties(host_object)
36
+ return_object = self.on_private_session_stopped(source_config_name,self_generated,message)
37
+ self._save_persistent_properties(host_object)
38
+ return return_object
39
+
40
+ def on_private_session_stopped(self,source_config_name,self_generated,message):
41
+ pass
iop/_remote.py ADDED
@@ -0,0 +1,91 @@
1
+ # This module provides a REST API for remote I/O operations
2
+ # It uses Flask to handle incoming requests and route them to the appropriate I/O functions
3
+ # It should be able to help the migrate command of IoP Cli to work remotely:
4
+ # this means copy all the .py files from the current directory of (settings.py) to the remote server
5
+ # and run the api migrate from the remote server
6
+ # the default folder is based on the NAMESPACE variable in settings.py
7
+ from flask import Flask, request, jsonify
8
+
9
+ @app.route('/remote_io', methods=['POST'])
10
+ def remote_io():
11
+ data = request.json
12
+ if not data or 'operation' not in data:
13
+ return jsonify({'error': 'Invalid request'}), 400
14
+
15
+ operation = data['operation']
16
+ # Here you would implement the logic to handle the operation
17
+ # For example, you could call a function that performs the I/O operation
18
+ # and return the result as JSON.
19
+
20
+ # Placeholder response for demonstration purposes
21
+ response = {'status': 'success', 'operation': operation}
22
+ return jsonify(response), 200
23
+
24
+ # ClassMethod UploadPackage(
25
+ # namespace As %String,
26
+ # body As %DynamicArray) As %DynamicObject
27
+ # {
28
+ # // check for namespace existence and user permissions against namespace
29
+ # If '..NamespaceCheck(namespace) {
30
+ # Return ""
31
+ # }
32
+ # New $NAMESPACE
33
+ # Set $NAMESPACE = namespace
34
+
35
+ # //Create directory for custom packages
36
+ # Do ##class(%ZHSLIB.HealthShareMgr).GetDBNSInfo(namespace,.out)
37
+ # Set customPackagesPath = ##class(%Library.File).NormalizeDirectory("fhir_packages", out.globalsDatabase.directory)
38
+ # If '##class(%Library.File).DirectoryExists(customPackagesPath) {
39
+ # If '##class(%Library.File).CreateDirectory(customPackagesPath) {
40
+ # $$$ThrowStatus($$$ERROR($$$DirectoryCannotCreate, customPackagesPath))
41
+ # }
42
+ # }
43
+
44
+ # //Find package name
45
+ # Set iterator = body.%GetIterator()
46
+ # Set packageName = ""
47
+ # While iterator.%GetNext(, .fileObject ) {
48
+ # If fileObject.name = "package.json" {
49
+ # Set packageName = fileObject.data.name_"@"_fileObject.data.version
50
+ # }
51
+ # }
52
+ # If packageName = "" {
53
+ # Do ..%ReportRESTError($$$HTTP400,$$$ERROR($$$HSFHIRErrPackageNotFound))
54
+ # Return ""
55
+ # }
56
+
57
+ # Set packagePath = ##class(%Library.File).NormalizeDirectory(packageName, customPackagesPath)
58
+ # // If the package already exists then we must be meaning to re-load it. Delete files/directory/metadata and recreate fresh.
59
+ # If ##class(%Library.File).DirectoryExists(packagePath) {
60
+ # If '##class(%Library.File).RemoveDirectoryTree(packagePath) {
61
+ # $$$ThrowStatus($$$ERROR($$$DirectoryPermission , packagePath))
62
+ # }
63
+ # }
64
+ # If '##class(%Library.File).CreateDirectory(packagePath) {
65
+ # $$$ThrowStatus($$$ERROR($$$DirectoryCannotCreate, customPackagesPath))
66
+ # }
67
+ # Set pkg = ##class(HS.FHIRMeta.Storage.Package).FindById(packageName)
68
+ # If $ISOBJECT(pkg) {
69
+ # // Will fail and throw if the package is in-use or has dependencies preventing it from being deleted.
70
+ # Do ##class(HS.FHIRServer.ServiceAdmin).DeleteMetadataPackage(packageName)
71
+ # }
72
+ # Kill pkg
73
+
74
+ # //Unpack JSON objects
75
+ # Set iterator = body.%GetIterator()
76
+ # While iterator.%GetNext(.key , .fileObject ) {
77
+ # Set fileName = ##class(%Library.File).NormalizeFilename(fileObject.name,packagePath)
78
+ # Set fileStream = ##class(%Stream.FileCharacter).%New()
79
+ # Set fileStream.TranslateTable = "UTF8"
80
+ # $$$ThrowOnError(fileStream.LinkToFile(fileName))
81
+ # Do fileObject.data.%ToJSON(.fileStream)
82
+ # $$$ThrowOnError(fileStream.%Save())
83
+ # }
84
+
85
+ # //Import package
86
+ # Do ##class(HS.FHIRMeta.Load.NpmLoader).importPackages(packagePath)
87
+ # Set pkg = ..GetOnePackage(packageName, namespace)
88
+ # Do ..%SetStatusCode($$$HTTP201)
89
+ # Do ..%SetHeader("location", %request.Application _ "packages/" _ packageName _ "?namespace=" _ namespace)
90
+ # Return pkg
91
+ # }