|
| 1 | +# Migration guide to Symphony BDK 2.0 |
| 2 | + |
| 3 | +This guide provides information about how to migrate from Symphony BDK 1.0 to BDK 2.0. Migration for the following topics will be detailed here: |
| 4 | +- Dependencies |
| 5 | +- Bot configuration |
| 6 | +- Symphony BDK entry point |
| 7 | +- BDK services |
| 8 | +- Event listeners |
| 9 | + |
| 10 | +## Dependencies |
| 11 | +To use the Python BDK 1.x, you had to use the |
| 12 | +[`sym-api-client-python` Pypi package](https://pypi.org/project/sym-api-client-python/) in your `requirements.txt` file. |
| 13 | +For the Python BDK 2.0, package name has been changed to [`symphony-bdk-python`](https://pypi.org/project/symphony-bdk-python/). |
| 14 | + |
| 15 | +## Bot configuration |
| 16 | +In order for bots to function, a configuration file is needed. Whereas Python BDK 1.x only supports JSON format, |
| 17 | +Python BDK 2.0 supports both JSON and YAML formats. |
| 18 | + |
| 19 | +Bot configuration for Python BDK 2.0 should have the following properties: |
| 20 | +- `host`: pod host name |
| 21 | +- `bot.username`: bot (or service account) username |
| 22 | +- `bot.privatekey.path`: path to bot private key file |
| 23 | + |
| 24 | +If your bot is deployed on premise, the following properties are required as well: |
| 25 | +- `agent`: on premise agent configuration |
| 26 | +- `keyManager`: on premise Key manager configuration |
| 27 | +- `proxy`: proxy configuration to reach the pod |
| 28 | +- `ssl.trustStore.path`: path to truststore file in PEM format |
| 29 | + |
| 30 | +> Click [here](./configuration.md) for more detailed documentation about BDK configuration. |
| 31 | +
|
| 32 | +### Minimal configuration example |
| 33 | + |
| 34 | +#### Using the BDK 1.x |
| 35 | +```json |
| 36 | +{ |
| 37 | + "sessionAuthHost": "acme.symphony.com", |
| 38 | + "keyAuthHost": "acme.symphony.com", |
| 39 | + "podHost": "acme.symphony.com", |
| 40 | + "agentHost": "acme.symphony.com", |
| 41 | + "botUsername": "bot-username", |
| 42 | + "botPrivateKeyPath": "/folder/to/private/key/", |
| 43 | + "botPrivateKeyName": "rsa-privatekey.pem", |
| 44 | + "truststorePath": "" |
| 45 | +} |
| 46 | +``` |
| 47 | + |
| 48 | +#### Using the BDK 2.0 |
| 49 | +In JSON: |
| 50 | +```json |
| 51 | +{ |
| 52 | + "host": "acme.symphony.com", |
| 53 | + "bot": { |
| 54 | + "username": "bot-username", |
| 55 | + "privateKey": { |
| 56 | + "path": "/folder/to/private/key/rsa-privatekey.pem" |
| 57 | + } |
| 58 | + } |
| 59 | +} |
| 60 | +``` |
| 61 | +Or in YAML: |
| 62 | +```yaml |
| 63 | +host: acme.symphony.com |
| 64 | +bot: |
| 65 | + username: bot-username |
| 66 | + privateKey: |
| 67 | + path: "/folder/to/private/key/rsa-privatekey.pem" |
| 68 | +``` |
| 69 | +
|
| 70 | +## Symphony BDK entry point |
| 71 | +
|
| 72 | +For the BDK 1.x, `SymBotClient` object acts as an entry point for all services, whereas for the BDK 2.0, it is the `SymphonyBdk` object. |
| 73 | +Whereas the BDK 1.x exposes synchronous methods, the BDK 2.0 exposes most of the service methods as `async` methods. |
| 74 | +Therefore, an `asyncio` loop is needed to use the BDK. |
| 75 | + |
| 76 | +Please check below for examples or check the [getting started](./getting_started.md) guide. |
| 77 | + |
| 78 | +## BDK services |
| 79 | +To illustrate the use of services, let's take an example of a bot reacting to *ping pong* messages. |
| 80 | + |
| 81 | +### Using the BDK 1.x |
| 82 | + |
| 83 | +```python |
| 84 | +from sym_api_client_python.auth.rsa_auth import SymBotRSAAuth |
| 85 | +from sym_api_client_python.clients.sym_bot_client import SymBotClient |
| 86 | +from sym_api_client_python.configure.configure import SymConfig |
| 87 | +from sym_api_client_python.listeners.im_listener import IMListener |
| 88 | +from sym_api_client_python.processors.sym_message_parser import SymMessageParser |
| 89 | +
|
| 90 | +
|
| 91 | +class PingPongListener(IMListener): |
| 92 | + def __init__(self, sym_bot_client): |
| 93 | + self.bot_client = sym_bot_client |
| 94 | + self.message_parser = SymMessageParser() |
| 95 | +
|
| 96 | + def on_im_message(self, im_message): |
| 97 | + stream_id = self.message_parser.get_stream_id(im_message) |
| 98 | + message_text = self.message_parser.get_text(im_message)[0] |
| 99 | +
|
| 100 | + if message_text == "/ping": |
| 101 | + self._send_message(stream_id, "pong") |
| 102 | + elif message_text == "/pong": |
| 103 | + self._send_message(stream_id, "ping") |
| 104 | + else: |
| 105 | + self._send_message(stream_id, "Sorry, I don't understand!") |
| 106 | +
|
| 107 | + def on_im_created(self, im_created): |
| 108 | + pass |
| 109 | +
|
| 110 | + def _send_message(self, stream_id, message): |
| 111 | + self.bot_client.get_message_client().send_msg(stream_id, dict(message=message)) |
| 112 | +
|
| 113 | +
|
| 114 | +def main(): |
| 115 | + # Load configuration |
| 116 | + configure = SymConfig('../resources/config.json') |
| 117 | + configure.load_config() |
| 118 | +
|
| 119 | + # authenticate |
| 120 | + auth = SymBotRSAAuth(configure) |
| 121 | + auth.authenticate() |
| 122 | +
|
| 123 | + bot_client = SymBotClient(auth, configure) |
| 124 | +
|
| 125 | + datafeed_event_service = bot_client.get_datafeed_event_service() |
| 126 | + datafeed_event_service.add_im_listener(PingPongListener(bot_client)) |
| 127 | +
|
| 128 | + print('Starting datafeed') |
| 129 | + try: |
| 130 | + datafeed_event_service.start_datafeed() |
| 131 | + except (KeyboardInterrupt, SystemExit): |
| 132 | + print('Stopping datafeed') |
| 133 | +
|
| 134 | +
|
| 135 | +if __name__ == "__main__": |
| 136 | + main() |
| 137 | +``` |
| 138 | + |
| 139 | +### Using the BDK 2.0 |
| 140 | + |
| 141 | +```python |
| 142 | +import asyncio |
| 143 | +
|
| 144 | +from symphony.bdk.core.config.loader import BdkConfigLoader |
| 145 | +from symphony.bdk.core.service.datafeed.real_time_event_listener import RealTimeEventListener |
| 146 | +from symphony.bdk.core.service.message.message_parser import get_text_content_from_message |
| 147 | +from symphony.bdk.core.symphony_bdk import SymphonyBdk |
| 148 | +from symphony.bdk.gen.agent_model.v4_initiator import V4Initiator |
| 149 | +from symphony.bdk.gen.agent_model.v4_message_sent import V4MessageSent |
| 150 | +
|
| 151 | +
|
| 152 | +class PingPongListener(RealTimeEventListener): |
| 153 | +
|
| 154 | + def __init__(self, message_service): |
| 155 | + self._message_service = message_service |
| 156 | +
|
| 157 | + async def on_message_sent(self, initiator: V4Initiator, event: V4MessageSent): |
| 158 | + message_text = get_text_content_from_message(event.message) |
| 159 | + stream_id = event.message.stream.stream_id |
| 160 | + if message_text == "/ping": |
| 161 | + await self._message_service.send_message(stream_id=stream_id, message="pong") |
| 162 | + elif message_text == "/pong": |
| 163 | + await self._message_service.send_message(stream_id=stream_id, message="ping") |
| 164 | + else: |
| 165 | + await self._message_service.send_message(stream_id=stream_id, message="Sorry, I don't understand!") |
| 166 | +
|
| 167 | +
|
| 168 | +async def run(): |
| 169 | + config = BdkConfigLoader.load_from_symphony_dir("config.yaml") |
| 170 | +
|
| 171 | + async with SymphonyBdk(config) as bdk: |
| 172 | + datafeed_loop = bdk.datafeed() |
| 173 | + datafeed_loop.subscribe(PingPongListener(bdk.messages())) |
| 174 | + await datafeed_loop.start() |
| 175 | +
|
| 176 | +
|
| 177 | +if __name__ == "__main__": |
| 178 | + try: |
| 179 | + print("Running datafeed example...") |
| 180 | + asyncio.run(run()) |
| 181 | + except KeyboardInterrupt: |
| 182 | + print("Ending datafeed example") |
| 183 | +``` |
| 184 | + |
| 185 | +## Event listeners |
| 186 | + |
| 187 | +### Using the BDK 1.x |
| 188 | +In the Python BDK 1.x, we have three main types of listeners: |
| 189 | +- for IM (1 to 1 conversation) |
| 190 | +- for MIM (room) |
| 191 | +- for Symphony elements |
| 192 | + |
| 193 | +There are also listener types for: |
| 194 | +- for connection requests |
| 195 | +- for wall posts |
| 196 | +- for message suppression |
| 197 | + |
| 198 | +See [datafeed_event_service](https://github.com/finos/symphony-bdk-python/blob/legacy/sym_api_client_python/datafeed_event_service.py) for more details. |
| 199 | + |
| 200 | +### Using the BDK 2.0 |
| 201 | +In the Python BDK 2.0, we have a [`RealTimeEventListener`](../_autosummary/symphony.bdk.core.service.datafeed.real_time_event_listener.RealTimeEventListener.html) |
| 202 | +type that listens to all events. Only events you are interested |
| 203 | +in needs to have the corresponding method overridden. See [datafeed](./datafeed.md) documentation for more information. |
| 204 | + |
| 205 | +The BDK 2.0 also provides a simple way to listen for `MESSAGESENT` events thanks to activities. |
| 206 | +See the [dedicated page](./activity-api.md) on how to use it. |
| 207 | + |
| 208 | +## Message parsing functions |
| 209 | + |
| 210 | +The Python BDK 1.x provides utility modules to parse elements and messages. |
| 211 | +The [sym_elements_parser module](https://github.com/finos/symphony-bdk-python/blob/legacy/sym_api_client_python/processors/sym_elements_parser.py) |
| 212 | +has no replacement in the BDK 2.0 since method `on_symphony_elements_action` of |
| 213 | +[`RealTimeEventListener`](../_autosummary/symphony.bdk.core.service.datafeed.real_time_event_listener.RealTimeEventListener.html) |
| 214 | +already returned a structured object of type `V4SymphonyElementsAction`. |
| 215 | + |
| 216 | +However, the [sym_elements_parser](https://github.com/finos/symphony-bdk-python/blob/legacy/sym_api_client_python/processors/sym_elements_parser.py) |
| 217 | +is replaced by [message_parser](../_autosummary/symphony.bdk.core.service.message.message_parser.html#module-symphony.bdk.core.service.message.message_parser) |
| 218 | +which contains methods to extract cashtags, emojis, hashtags, mentions and the text content. |
| 219 | + |
| 220 | +## Models |
| 221 | +Models names have been changed in Python BDK 2.0. They actually follow the models in the openapi specification of |
| 222 | +[Symphony's public API](https://github.com/symphonyoss/symphony-api-spec). Field names in Python classes correspond to |
| 223 | +the field names in API's JSON payloads. This requires to change some variable types in your legacy bots. |
| 224 | + |
| 225 | +Whereas most of the objects used in the Python BDK 1.x are Python dictionaries, the Python BDK 2.0 leverages objects |
| 226 | +generated from the openapi specifications. All public methods exposed by the BDK have type hints so that you can easily |
| 227 | +know which types are used as parameters or returned. You can also check the generated documentation [here](../_autosummary/symphony.bdk.core.html). |
0 commit comments