Skip to content

Commit e25c422

Browse files
committed
added soap with xsd project
1 parent b6c0050 commit e25c422

7 files changed

Lines changed: 388 additions & 0 deletions

File tree

soap-with-xsd/README.md

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
![Orbital banner](../github-banner.png)
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+
![Query profiler](profiler.png)
62+
63+
Clicking in the second tab shows the sequence diagram of calls:
64+
65+
![Sequence diagram](sequence.png)
66+
67+
Clicking on any of the calls lets you drill into the actual traffic, and see the request / response payload.
68+
69+
![Call details](call-details.png)
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+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import com.orbitalhq.CapitalCityName
2+
import com.orbitalhq.CurrencyName
3+
import com.orbitalhq.CountryFlagUrl
4+
import com.orbitalhq.CountryName
5+
import com.orbitalhq.IsoCountryCode
6+
7+
@HttpOperation(url = '/api/q/countrydata/{countryCode}', method = 'GET')
8+
query countrydata(@PathVariable("countryCode") countryCode : IsoCountryCode) {
9+
given { countryCode }
10+
find {
11+
name : CountryName
12+
flag: CountryFlagUrl
13+
currency: CurrencyName
14+
capital: CapitalCityName
15+
}
16+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace com.orbitalhq {
2+
type CurrencyIsoCode inherits String
3+
type CurrencyName inherits String
4+
5+
type CountryName inherits String
6+
type IsoCountryCode inherits String
7+
type CapitalCityName inherits String
8+
type CountryFlagUrl inherits String
9+
}

soap-with-xsd/taxi.conf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
name: com.orbitalhq/soap-demo
2+
version: 0.2.0
3+
sourceRoot: src/
4+
additionalSources: {
5+
"@orbital/wsdl" = "wsdl/*.{wsdl,xsd}"
6+
}

soap-with-xsd/workspace.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
file {
3+
projects = [
4+
{
5+
path: ".",
6+
}
7+
]
8+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<definitions
3+
xmlns:xs="http://www.w3.org/2001/XMLSchema"
4+
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
5+
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
6+
xmlns:tns="http://www.oorsprong.org/websamples.countryinfo"
7+
xmlns:types="http://www.oorsprong.org/websamples.countryinfo"
8+
xmlns:taxi="http://taxilang.org/"
9+
xmlns="http://schemas.xmlsoap.org/wsdl/"
10+
name="CountryInfoService"
11+
targetNamespace="http://www.oorsprong.org/websamples.countryinfo">
12+
13+
<types>
14+
<xs:schema elementFormDefault="qualified">
15+
<!-- Import the schema containing the type definitions -->
16+
<xs:import namespace="http://www.oorsprong.org/websamples.countryinfo"
17+
schemaLocation="country-info-types.xsd"/>
18+
</xs:schema>
19+
</types>
20+
21+
<message name="FullCountryInfoSoapRequest">
22+
<part name="parameters" element="types:FullCountryInfo"/>
23+
</message>
24+
25+
<message name="FullCountryInfoSoapResponse">
26+
<part name="parameters" element="types:FullCountryInfoResponse"/>
27+
</message>
28+
29+
<portType name="CountryInfoServiceSoapType">
30+
<operation name="FullCountryInfo">
31+
<documentation>Returns a struct with all stored country information. Pass the ISO country code.</documentation>
32+
<input message="tns:FullCountryInfoSoapRequest"/>
33+
<output message="tns:FullCountryInfoSoapResponse"/>
34+
</operation>
35+
</portType>
36+
37+
<binding name="CountryInfoServiceSoapBinding" type="tns:CountryInfoServiceSoapType">
38+
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
39+
<operation name="FullCountryInfo">
40+
<soap:operation soapAction="" style="document"/>
41+
<input><soap:body use="literal"/></input>
42+
<output><soap:body use="literal"/></output>
43+
</operation>
44+
</binding>
45+
46+
<binding name="CountryInfoServiceSoapBinding12" type="tns:CountryInfoServiceSoapType">
47+
<soap12:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
48+
<operation name="FullCountryInfo">
49+
<soap12:operation soapAction="" style="document"/>
50+
<input><soap12:body use="literal"/></input>
51+
<output><soap12:body use="literal"/></output>
52+
</operation>
53+
</binding>
54+
55+
<service name="CountryInfoService">
56+
<documentation>This service provides country information. ISO codes are used for lookup.</documentation>
57+
<port name="CountryInfoServiceSoap" binding="tns:CountryInfoServiceSoapBinding">
58+
<soap:address location="http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso"/>
59+
</port>
60+
<port name="CountryInfoServiceSoap12" binding="tns:CountryInfoServiceSoapBinding12">
61+
<soap12:address location="http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso"/>
62+
</port>
63+
</service>
64+
65+
</definitions>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<xs:schema
3+
xmlns:xs="http://www.w3.org/2001/XMLSchema"
4+
xmlns:taxi="http://taxilang.org/"
5+
xmlns:tns="http://www.oorsprong.org/websamples.countryinfo"
6+
targetNamespace="http://www.oorsprong.org/websamples.countryinfo"
7+
elementFormDefault="qualified">
8+
9+
<xs:complexType name="tCountryInfo">
10+
<xs:sequence>
11+
<xs:element name="sISOCode" type="xs:string" taxi:createsType="com.test.IsoCountryCode"/>
12+
<xs:element name="sName" type="xs:string" taxi:createsType="com.test.CountryName"/>
13+
<xs:element name="sCapitalCity" type="xs:string" taxi:createsType="com.test.CapitalCityName"/>
14+
<xs:element name="sPhoneCode" type="xs:string"/>
15+
<xs:element name="sContinentCode" type="xs:string"/>
16+
<xs:element name="sCurrencyISOCode" type="xs:string"/>
17+
<xs:element name="sCountryFlag" type="xs:string"/>
18+
<xs:element name="Languages" type="tns:ArrayOftLanguage"/>
19+
</xs:sequence>
20+
</xs:complexType>
21+
22+
<xs:complexType name="tLanguage">
23+
<xs:sequence>
24+
<xs:element name="sISOCode" type="xs:string"/>
25+
<xs:element name="sName" type="xs:string"/>
26+
</xs:sequence>
27+
</xs:complexType>
28+
29+
<xs:complexType name="ArrayOftLanguage">
30+
<xs:sequence>
31+
<xs:element name="tLanguage" type="tns:tLanguage" minOccurs="0" maxOccurs="unbounded" nillable="true"/>
32+
</xs:sequence>
33+
</xs:complexType>
34+
35+
<xs:element name="FullCountryInfo">
36+
<xs:complexType>
37+
<xs:sequence>
38+
<xs:element name="sCountryISOCode" type="xs:string" taxi:createsType="com.test.IsoCountryCode"/>
39+
</xs:sequence>
40+
</xs:complexType>
41+
</xs:element>
42+
43+
<xs:element name="FullCountryInfoResponse">
44+
<xs:complexType>
45+
<xs:sequence>
46+
<xs:element name="FullCountryInfoResult" type="tns:tCountryInfo"/>
47+
</xs:sequence>
48+
</xs:complexType>
49+
</xs:element>
50+
51+
</xs:schema>

0 commit comments

Comments
 (0)