|
| 1 | + |
| 2 | + |
| 3 | +# Converting SOAP to JSON, and composing multiple requests into a single API |
| 4 | + |
| 5 | +This demo focuses on: |
| 6 | + |
| 7 | + * Taking an old SOAP API |
| 8 | + * Reworking the endpoints to have smaller, more resource-oriented APIs |
| 9 | + * Publishing as RESTful JSON |
| 10 | + |
| 11 | +After showing you the features of this demo, there's a short section breaking down exactly [how this all works](#how-this-all-works). |
| 12 | + |
| 13 | +This demo showcases the following Orbital features: |
| 14 | + |
| 15 | + * [Embedding Taxi within a SOAP WSDL](#embedding-taxi-in-soap) |
| 16 | + * [Exposing a TaxiQL query as an HTTP endpoint](#exposing-a-taxiql-query-as-an-api) |
| 17 | + |
| 18 | +If you like this demo, please [Give us a Star](https://github.com/orbitalapi/orbital) on Github! |
| 19 | + |
| 20 | +## Running this demo |
| 21 | + |
| 22 | +1. Clone this repo, and cd into this directory |
| 23 | + |
| 24 | +2. Run the following docker command to start Orbital, preconfigured with this project: |
| 25 | + |
| 26 | +```bash |
| 27 | +docker compose up -d |
| 28 | +``` |
| 29 | + |
| 30 | +## Things to try |
| 31 | +After 20 seconds or so, open http://localhost:9022, and you should see Orbital's UI. |
| 32 | + |
| 33 | +### Combining SOAP requests - Querying in the UI |
| 34 | +Head to the [Query Editor](http://localhost:9022/query/editor) and paste the following query: |
| 35 | + |
| 36 | +```taxi |
| 37 | +import com.orbitalhq.CurrencyName |
| 38 | +import com.orbitalhq.CountryFlagUrl |
| 39 | +import com.orbitalhq.CountryName |
| 40 | +
|
| 41 | +given { iso: IsoCountryCode = "NZ"} |
| 42 | +find { |
| 43 | + name: CountryName |
| 44 | + flag: CountryFlagUrl |
| 45 | + currency: CurrencyName |
| 46 | + } |
| 47 | +``` |
| 48 | + |
| 49 | +Click Run, and the query will execute. In the Results panel, on the Raw tab, you should see the following results: |
| 50 | + |
| 51 | +```json |
| 52 | +{ |
| 53 | + "name": "New Zealand", |
| 54 | + "flag": "http://www.oorsprong.org/WebSamples.CountryInfo/Flags/New_Zealand.jpg", |
| 55 | + "currency": "New Zealand Dollars" |
| 56 | +} |
| 57 | +``` |
| 58 | + |
| 59 | +Click on the Profiler to see lineage of the query: |
| 60 | + |
| 61 | + |
| 62 | + |
| 63 | +Clicking in the second tab shows the sequence diagram of calls: |
| 64 | + |
| 65 | + |
| 66 | + |
| 67 | +Clicking on any of the calls lets you drill into the actual traffic, and see the request / response payload. |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | +### Combining SOAP Requests - as an HTTP request |
| 72 | + |
| 73 | +Try opening a browser (or Postman) and sending requesting this url: |
| 74 | + |
| 75 | +http://localhost:9022/api/q/countrydata/NZ |
| 76 | + |
| 77 | +You'll get the following JSON back: |
| 78 | + |
| 79 | +```json |
| 80 | +[ |
| 81 | + { |
| 82 | + "name": "New Zealand", |
| 83 | + "flag": "http://www.oorsprong.org/WebSamples.CountryInfo/Flags/New_Zealand.jpg", |
| 84 | + "currency": "New Zealand Dollars", |
| 85 | + "capital": "Wellington" |
| 86 | + } |
| 87 | +] |
| 88 | +``` |
| 89 | + |
| 90 | +This works because we have the following Taxi saved query defined in `CountryData.query.taxi`: |
| 91 | + |
| 92 | +```taxi |
| 93 | +@HttpOperation(url = '/api/q/countrydata/{countryCode}', method = 'GET') |
| 94 | +query countrydata(@PathVariable("countryCode") countryCode : IsoCountryCode) { |
| 95 | + given { countryCode } |
| 96 | + find { |
| 97 | + name : CountryName |
| 98 | + flag: CountryFlagUrl |
| 99 | + currency: CurrencyName |
| 100 | + capital: CapitalCityName |
| 101 | + } |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +## How this all works |
| 106 | +Everything starts with our Docker Compose file, and the taxi project. |
| 107 | + |
| 108 | +### Things of note in the docker-compose file |
| 109 | +Our `docker-compose.yml` is pretty standard, containing: |
| 110 | + * [Orbital](https://hub.docker.com/r/orbitalhq/orbital) |
| 111 | + * [Postgres](https://hub.docker.com/_/postgres) |
| 112 | + |
| 113 | +Orbital is actually stateless when executing queries (which means is scales well), |
| 114 | +but uses a database for things like History and Profiling. |
| 115 | + |
| 116 | +There's also the following config, which is annotated |
| 117 | + |
| 118 | +```yaml |
| 119 | +OPTIONS: >- |
| 120 | + ## Capture the actual responses from remote calls, useful for debugging |
| 121 | + --vyne.analytics.persistRemoteCallResponses=true |
| 122 | + ## Capture the results of queries, useful for debugging. |
| 123 | + --vyne.analytics.persistResults=true |
| 124 | + ## Database connection options |
| 125 | + --vyne.db.username=orbital |
| 126 | + --vyne.db.password=changeme |
| 127 | + --vyne.db.host=postgres |
| 128 | +
|
| 129 | + ## Load the workspace.conf file in this repository |
| 130 | + --vyne.workspace.config-file=/opt/service/workspace/workspace.conf |
| 131 | +``` |
| 132 | +
|
| 133 | +### Our Taxi project |
| 134 | +Orbital is using a [taxi](https://taxilang.org) project which adds [semantic metadata](https://orbitalhq.com/blog/2023-05-22-semantic-metadata-101) |
| 135 | +to our [SOAP WSDL](#embedding-taxi-in-soap). |
| 136 | +
|
| 137 | +Taxi metadata assigns tags to fields, so we can understand how data relates. Once that's present, Orbital |
| 138 | +can use the WSDL to call the required SOAP services we need, based on the data we ask for. |
| 139 | +
|
| 140 | +Our Taxi file in `taxi/src/CountryInfoDefinitions.taxi` defines a few simple tags: |
| 141 | + |
| 142 | +```taxi |
| 143 | +namespace com.orbitalhq |
| 144 | +
|
| 145 | +type CurrencyIsoCode inherits String |
| 146 | +type CurrencyName inherits String |
| 147 | +``` |
| 148 | + |
| 149 | +These get embedded in our WSDL, and then used later when we write a query |
| 150 | + |
| 151 | +### Embedding Taxi in Soap |
| 152 | +Taxi can be embedded directly into SOAP WSDLs, to declare the semantic meaning of each field: |
| 153 | + |
| 154 | +For example: |
| 155 | + |
| 156 | +```xml |
| 157 | + <xs:complexType name="tCurrency"> |
| 158 | + <xs:sequence> |
| 159 | + <xs:element name="sISOCode" type="xs:string" |
| 160 | +> taxi:type="com.orbitalhq.CurrencyIsoCode"/> |
| 161 | + <xs:element name="sName" type="xs:string" |
| 162 | +> taxi:type="com.orbitalhq.CurrencyName"/> |
| 163 | + </xs:sequence> |
| 164 | + </xs:complexType> |
| 165 | +``` |
| 166 | + |
| 167 | +See the actual annotated SOAP WSDL in [CountryInfoServiceSpec.wsdl](/wsdl/CountryInfoServiceSpec.wsdl). |
| 168 | + |
| 169 | +Read more about this in the [docs](https://orbitalhq.com/docs/describing-data-sources/soap) |
| 170 | + |
| 171 | +### Querying for data |
| 172 | +Once the WSDL is annotated with our Taxi metadata, we can use it to write a query, as we saw when we [queried from the ui](#combining-soap-requests---querying-in-the-ui). |
| 173 | + |
| 174 | +```taxi |
| 175 | +given { iso: IsoCountryCode = "NZ"} |
| 176 | +find { |
| 177 | + // Each field comes from a different SOAP service. |
| 178 | + // But taxi keeps this nice and succinct, composing the services |
| 179 | + // together we need on demand |
| 180 | + name: CountryName |
| 181 | + flag: CountryFlagUrl |
| 182 | + currency: CurrencyName |
| 183 | + } |
| 184 | +``` |
| 185 | + |
| 186 | +Try adding the `captial city` to the request. Open the profiler, and you'll see an additional |
| 187 | +request to another SOAP endpoint to grab the data: |
| 188 | + |
| 189 | +```diff |
| 190 | +import com.orbitalhq.CurrencyName |
| 191 | +import com.orbitalhq.CountryFlagUrl |
| 192 | +import com.orbitalhq.CountryName |
| 193 | +
|
| 194 | +given { iso: IsoCountryCode = "NZ"} |
| 195 | +find { |
| 196 | + name: CountryName |
| 197 | + flag: CountryFlagUrl |
| 198 | + currency: CurrencyName |
| 199 | ++ captial: CapitalCityName |
| 200 | + } |
| 201 | +``` |
| 202 | + |
| 203 | +### Exposing a TaxiQL query as an API |
| 204 | +This project exposes a query that combines multiple SOAP operations together: |
| 205 | + |
| 206 | +```taxi |
| 207 | +@HttpOperation(url = '/api/q/countrydata/{countryCode}', method = 'GET') |
| 208 | +query countrydata(@PathVariable("countryCode") countryCode : IsoCountryCode) { |
| 209 | + given { countryCode } |
| 210 | + find { |
| 211 | + name : CountryName |
| 212 | + flag: CountryFlagUrl |
| 213 | + currency: CurrencyName |
| 214 | + capital: CapitalCityName |
| 215 | + } |
| 216 | +} |
| 217 | +``` |
| 218 | + |
| 219 | +This query is available at `http://localhost:9022/api/q/countrydata/NZ` or `http://localhost:9022/api/q/countrydata/AUD` (or whatever country code interests you) |
| 220 | + |
| 221 | +What's more, there's even a OpenAPI spec that's been automatically generated for us, available at http://localhost:9022/api/q/meta/countrydata/oas |
| 222 | + |
| 223 | +Read more about Orbital creating OpenAPI specs in the [docs](https://orbitalhq.com/docs/querying/queries-as-endpoints#open-api). |
| 224 | + |
| 225 | +## Summary |
| 226 | +Orbital is combining multiple SOAP requests together for us, and packaging them together as a simple REST API. |
| 227 | + |
| 228 | +There's no code involved, just a little sprinkle of semantic metadata! |
| 229 | + |
| 230 | +This is only scratching the surface of what we can do. Orbital can also stitch this data together with other APIs (such as [REST](https://orbitalhq.com/docs/describing-data-sources/open-api) / [gRPC](https://orbitalhq.com/docs/describing-data-sources/protobuf)), call [serverless functions](https://orbitalhq.com/docs/describing-data-sources/aws-services#lambda), query our [Databases](https://orbitalhq.com/docs/describing-data-sources/databases), or [Kafka queues](https://orbitalhq.com/docs/describing-data-sources/connect-kafka-topic). |
| 231 | + |
| 232 | +If you liked this demo, remember to [Star our Github Repo](https://github/orbitalapi/orbital), and [get started](https://oribtalhq.com/docs) with Orbital now. |
| 233 | + |
0 commit comments