-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(docs): add steps to call SOAP services #2588
Merged
+181
−77
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,68 +11,165 @@ Services, gRPC microservices, or other forms of APIs. | |
|
||
To facilitate calling other APIs or web services, we introduce | ||
`@loopback/service-proxy` module to provide a common set of interfaces for | ||
interacting with backend services. | ||
interacting with backend services. You can create a `Service` class to provide a | ||
common set of interfaces for interacting with backend services. | ||
|
||
## Installation | ||
There are 3 major steps: | ||
|
||
``` | ||
$ npm install --save @loopback/service-proxy | ||
1. **Add a datasource**: specify the service you're trying to connect | ||
2. **Add a service**: define how the operations/methods in the external APIs | ||
will be mapped to the service methods. | ||
3. **Add a controller**: expose the REST APIs that will call the service methods | ||
|
||
## Add a datasource | ||
|
||
Add a datasource using the [Datasource generator](DataSource-generator.md) and | ||
select the corresponding connector. | ||
|
||
### Datasource for SOAP web service | ||
|
||
For calling SOAP web services, you also need to know the URL of the SOAP web | ||
service endpoint and its corresponding WSDL file. | ||
|
||
```sh | ||
$ lb4 datasource | ||
? Datasource name: ds | ||
? Select the connector for ds: SOAP webservices (supported by StrongLoop) | ||
? URL to the SOAP web service endpoint: http://calculator-webservice.mybluemix.net/calculator | ||
? HTTP URL or local file system path to the WSDL file: http://calculator-webservice.mybluemix.net/calculator?wsdl | ||
? Expose operations as REST APIs: Yes | ||
? Maps WSDL binding operations to Node.js methods: | ||
``` | ||
|
||
## Usage | ||
For the last option `Maps WSDL binding operations to Node.js methods`, specify | ||
the JSON in the format of: | ||
|
||
### Define a data source for the service backend | ||
```json | ||
servicMethodName: { | ||
"service": "<WSDL service name>", | ||
"port": "<WSDL port name>", | ||
"operation": "<WSDL operation name>" | ||
} | ||
``` | ||
|
||
```ts | ||
import {juggler} from '@loopback/service-proxy'; | ||
|
||
const ds: juggler.DataSource = new juggler.DataSource({ | ||
name: 'GoogleMapGeoCode', | ||
connector: 'rest', | ||
options: { | ||
headers: { | ||
accept: 'application/json', | ||
'content-type': 'application/json', | ||
If you have more than one operations to map, it might be easier to edit the | ||
DataSource JSON after it's been created. See below for the example of the | ||
mapping of the WSDL binding operations and Node.js methods. | ||
|
||
```json | ||
{ | ||
"name": "ds", | ||
"connector": "soap", | ||
"url": "http://calculator-webservice.mybluemix.net/calculator", | ||
"wsdl": "http://calculator-webservice.mybluemix.net/calculator?wsdl", | ||
"remotingEnabled": true, | ||
// ADD THIS SNIPPET | ||
"operations": { | ||
"add": { | ||
"service": "CalculatorService", //WSDL service name | ||
"port": "CalculatorPort", //WSDL port name | ||
"operation": "add" //WSDL operation name | ||
}, | ||
"subtract": { | ||
"service": "CalculatorService", | ||
"port": "CalculatorPort", | ||
"operation": "subtract" | ||
} | ||
} | ||
// END OF THE SNIPPET | ||
} | ||
``` | ||
|
||
For details, you can refer to the SOAP connector's operations property: | ||
https://github.com/strongloop/loopback-connector-soap#operations-property | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will change it to https://loopback.io/doc/en/lb3/SOAP-connector.html#operations-property. |
||
|
||
### Datasource for REST service | ||
|
||
When calling REST services, select `REST services` for connector. We'll leave | ||
the default for the last 3 prompts. | ||
|
||
```sh | ||
$ lb4 datasource | ||
? Datasource name: restds | ||
? Select the connector for restds: REST services (supported by StrongLoop) | ||
? Base URL for the REST service: https://swapi.co/api/ | ||
? Default options for the request: | ||
? An array of operation templates: | ||
? Use default CRUD mapping: No | ||
``` | ||
|
||
The next step is to edit the DataSource JSON file for `options` and | ||
`operations`. | ||
|
||
The REST connector uses the | ||
[request module](https://www.npmjs.com/package/request) as the HTTP client. You | ||
can configure the same options as for the `request()` function. See details in | ||
[this documentation page](https://loopback.io/doc/en/lb3/REST-connector.html#configure-options-for-request). | ||
|
||
The `template` object specifies the REST API invocation as a JSON template. You | ||
can find more details in the | ||
[Defining a custom method using a template page](https://loopback.io/doc/en/lb3/REST-connector.html#defining-a-custom-method-using-a-template). | ||
|
||
```json | ||
{ | ||
"name": "restds", | ||
"connector": "rest", | ||
"baseURL": "https://swapi.co/api/", | ||
"crud": false, | ||
"options": { | ||
"headers": { | ||
"accept": "application/json", | ||
"content-type": "application/json" | ||
} | ||
}, | ||
operations: [ | ||
"operations": [ | ||
{ | ||
template: { | ||
method: 'GET', | ||
url: 'http://maps.googleapis.com/maps/api/geocode/{format=json}', | ||
query: { | ||
address: '{street},{city},{zipcode}', | ||
sensor: '{sensor=false}', | ||
}, | ||
responsePath: '$.results[0].geometry.location[0]', | ||
}, | ||
functions: { | ||
geocode: ['street', 'city', 'zipcode'], | ||
"template": { | ||
"method": "GET", | ||
"url": "https://swapi.co/api/people/{personId}" | ||
}, | ||
}, | ||
], | ||
}); | ||
"functions": { | ||
"getCharacter": ["personId"] | ||
} | ||
} | ||
] | ||
} | ||
``` | ||
|
||
Install the REST connector used by the new datasource: | ||
Refer to the detailed information in [configure the options] and [templates]. | ||
|
||
``` | ||
$ npm install --save loopback-connector-rest | ||
``` | ||
## Add a service | ||
|
||
Add a service using the [Service generator](Service-generator.md) and specify | ||
the DataSource that you just created. | ||
|
||
### Define the methods that map to the operations | ||
|
||
### Declare the service interface | ||
In the Service interface, define the methods that map to the operations of your | ||
external service. | ||
|
||
To promote type safety, we recommend you to declare data types and service | ||
interfaces in TypeScript and use them to access the service proxy. | ||
|
||
```ts | ||
interface GeoCode { | ||
lat: number; | ||
lng: number; | ||
export interface CalculatorService { | ||
add(args: CalculatorParameters): Promise<AddResponse>; | ||
subtract(args: CalculatorParameters): Promise<SubtractResponse>; | ||
} | ||
|
||
interface GeoService { | ||
geocode(street: string, city: string, zipcode: string): Promise<GeoCode>; | ||
export interface AddResponse { | ||
result: { | ||
value: number; | ||
}; | ||
} | ||
export interface SubtractResponse { | ||
result: { | ||
value: number; | ||
}; | ||
} | ||
export interface CalculatorParameters { | ||
intA: number; | ||
intB: number; | ||
} | ||
``` | ||
|
||
|
@@ -88,38 +185,49 @@ export interface GenericService { | |
} | ||
``` | ||
|
||
To reference the `GenericService`: | ||
## Add a Controller | ||
|
||
Add a controller using the [Controller generator](Controller-generator.md) with | ||
the `Empty Controller` option. | ||
|
||
### Inject the Service in the constructor | ||
|
||
```ts | ||
import {GenericService} from '@loopback/service-proxy'; | ||
constructor( | ||
@inject('services.CalculatorService') | ||
protected calculatorService: CalculatorService, | ||
) {} | ||
``` | ||
|
||
**NOTE**: We'll introduce tools in the future to generate TypeScript service | ||
interfaces from service specifications such as OpenAPI spec. | ||
### Add the REST endpoints | ||
|
||
### Declare service proxies for your controller | ||
|
||
If your controller needs to interact with backend services, declare such | ||
dependencies using `@serviceProxy` on constructor parameters or instance | ||
properties of the controller class. | ||
This will be similar to how you normally add a REST endpoint in a Controller. | ||
The only difference is you'll be calling the methods that you've exposed in the | ||
Service interface. | ||
|
||
```ts | ||
import {serviceProxy} from '@loopback/service-proxy'; | ||
|
||
export class MyController { | ||
@serviceProxy('geoService') | ||
private geoService: GeoService; | ||
} | ||
@get('/add/{intA}/{intB}') | ||
async add( | ||
@param.path.integer('intA') intA: number, | ||
@param.path.integer('intB') intB: number, | ||
): Promise<AddResponse> { | ||
//Preconditions | ||
|
||
return await this.calculatorService.add(<CalculatorParameters>{ | ||
intA, | ||
intB, | ||
}); | ||
} | ||
``` | ||
|
||
### Get an instance of your controller | ||
## More examples | ||
|
||
```ts | ||
context.bind('controllers.MyController').toClass(MyController); | ||
const myController = await context.get<MyController>( | ||
'controllers.MyController', | ||
); | ||
``` | ||
- SOAP web service tutorial: | ||
https://loopback.io/doc/en/lb4/soap-calculator-tutorial.html | ||
- REST service tutorial: | ||
https://loopback.io/doc/en/lb4/todo-tutorial-geocoding-service.html | ||
|
||
## Testing your application | ||
|
||
### Make service proxies easier to test | ||
|
||
|
@@ -149,22 +257,18 @@ export class GeoServiceProvider implements Provider<GeoService> { | |
} | ||
``` | ||
|
||
In your application, apply | ||
[ServiceMixin](http://apidocs.loopback.io/@loopback%2fdocs/service-proxy.html#ServiceMixin) | ||
and use `app.serviceProvider` API to create binding for the geo service proxy. | ||
### Troubleshooting | ||
|
||
```ts | ||
app.serviceProvider(GeoServiceProvider); | ||
``` | ||
|
||
Finally, modify the controller to receive our new service proxy in the | ||
constructor: | ||
If you get the error about the | ||
`app.serviceProvider() function is needed for ServiceBooter`, make sure you | ||
apply | ||
[ServiceMixin](http://apidocs.loopback.io/@loopback%2fdocs/service-proxy.html#ServiceMixin) | ||
to your Application class in the `application.ts`. | ||
|
||
```ts | ||
export class MyController { | ||
@inject('services.GeoService') | ||
private geoService: GeoService; | ||
} | ||
export class MyLoopBackApplication extends BootMixin( | ||
ServiceMixin(RepositoryMixin(RestApplication)), | ||
) | ||
``` | ||
|
||
Please refer to | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@strongloop/loopback-maintainers , do I need to add this? I tried without this snippet and it seems to be working fine. I initially thought that by specifying the
operations
here, there's less code I need to write in the Service class.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
operations
is for customizing the method names. See https://loopback.io/doc/en/lb3/SOAP-connector.html#operations-property.Otherwise, the method name is built from serviceName + portName + operationName
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @raymondfeng. The problem I'm having now is that the method name I'm setting here doesn't seem to be used for generating the methods inside the service interface, i.e. no matter what I set under the
operations
attribute, it's not being referenced in the code. Is it where it's being used?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not automatically update
CalculatorService
interface but the method names of the service proxy are impacted. We have yet to generate strongly-typed interface from the SOAP WS.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.