I am trying to mock an external (REST) server used by my system under test.
I am choosing MockServer (http://www.mock-server.com/) for mocking the external REST server.
I am running mock server standalone as in:
$ java -jar ./mockserver-netty-5.3.0-jar-with-dependencies.jar
-serverPort 1080 -proxyPort 1090 -proxyRemotePort 80 -proxyRemoteHost www.mock-server.com 2018-05-23 14:05:57,703 INFO o.m.m.MockServer
MockServer started on port: 1080 2018-05-23 14:05:57,747 INFO
o.m.p.d.DirectProxy MockServer started on port: 1090
I am not sure, having read the documentation, where I should define the expectations (viz., the responses the mock should yield based on incoming requests).
Can anyone explain?
Thanx,
R
It can be done by PUT, ie:
curl -v -X PUT "http://localhost:1080/expectation" -d '{
"httpRequest" : {
"path" : "/some/path"
},
"httpResponse" : {
"body" : "some_response_body"
}
}'
More info https://www.mock-server.com/mock_server/creating_expectations.html and go for REST API type of expectation
I used Postman to create the expectation. For creating expectations send a PUT request to http://localhost:portnumber/mockserver/expectation.
You can check the expectation and logs using this URL http://localhost:portnumber/mockserver/dashboard in the browser.
Related
I build a stream with an http source and a sink dataflow-launcher to execute a spring batch task named batchPY546Task
To launch this task i set the localFilePath=path-of-the-file parameter.
So in the documentation, with the http source it's possible to pass informations thru the payload.
https://github.com/spring-cloud-stream-app-starters/tasklauncher-dataflow/blob/master/spring-cloud-starter-stream-sink-task-launcher-dataflow/README.adoc
{
"name":"foo",
"deploymentProps": {"key1":"val1","key2":"val2"},
"args":["--debug", "--foo", "bar"]
}
I try many syntaxes :
curl http://localhost:57110 -H"Content-Type:application/json" -d '{"name":"batchPy546Task", "args":{"localFilePath=/tmp/remote-files1/BLM-54.00.01_Multicontrat_Creation_IDCRT011-b.xml"}}'
and all are wrongs
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of java.util.ArrayList<java.lang.Object> out of START_OBJECT token
at [Source: (byte[])"{"name":"batchPy546Task", "args":{"localFilePath=/tmp/remote-files1/BLM-54.00.01_Multicontrat_Creation_IDCRT011-b.xml"}}"; line: 1, column: 34] (through reference chain: org.springframework.cloud.stream.app.task.launcher.dataflow.sink.LaunchRequest["args"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:59) ~[jackson-databind-2.10.2.jar!/:2.10.2]
How can I pass the parameter lcalFilePath to my task ?
Versions :
dataflow server 14.2
skipper server 2.3.2
datafmow auncher is up to date and compatible with dataflow server 2.4.2.
Regards
Problem solved with this syntax :
curl http://localhost:57110 -H"Content-Type:application/json" -d '{"name":"batchPy546Task", "args":["localFilePath=/tmp/remote-files1/BLM-54.00.01_Multicontrat_Creation_IDCRT011-b.xml"]}'
args must be a json list "args":["localFilePath=/tmp/..."]
I am trying to submit a transaction to Hyperledger Sawtooth v1.0.1 using javascript to a validator running on localhost. The code for the post request is as below:
request.post({
url: constants.API_URL + '/batches',
body: batchListBytes,
headers: { 'Content-Type': 'application/octet-stream' }
}, (err, response) => {
if (err) {
console.log(err);
return cb(err)
}
console.log(response.body);
return cb(null, response.body);
});
The transaction gets processed when submitted from an backend nodejs application, but it returns an OPTIONS http://localhost:8080/batches 405 (Method Not Allowed) error when submitted from client. These are the options that I have tried:
Inject Access-Control-Allow-* headers into the response using an extension: The response still gives the same error
Remove the custom header to bypass preflight request: This makes the validator throw an error as shown:
...
sawtooth-rest-api-default | KeyError: "Key not found: 'Content-Type'"
sawtooth-rest-api-default | [2018-03-15 08:07:37.670 ERROR web_protocol] Error handling request
sawtooth-rest-api-default | Traceback (most recent call last):
...
The unmodified POST request from the browser gets the following response headers from the validator:
HTTP/1.1 405 Method Not Allowed
Content-Type: text/plain; charset=utf-8
Allow: GET,HEAD,POST
Content-Length: 23
Date: Thu, 15 Mar 2018 08:42:01 GMT
Server: Python/3.5 aiohttp/2.3.2
So, I guess OPTIONS method is not handled in the validator. A GET request for the state goes through fine when the CORS headers are added. This issue was also not faced in Sawtooth v0.8.
I am using docker to start the validator, and the commands to start it are a slightly modified version of those given in the LinuxFoundationX: LFS171x course. The relevant commands are below:
bash -c \"\
sawadm keygen && \
sawtooth keygen my_key && \
sawset genesis -k /root/.sawtooth/keys/my_key.priv && \
sawadm genesis config-genesis.batch && \
sawtooth-validator -vv \
--endpoint tcp://validator:8800 \
--bind component:tcp://eth0:4004 \
--bind network:tcp://eth0:8800
Can someone please guide me as to how to solve this problem?
CORS issues are always the best.
What is CORS?
Your browser trying to protect users from bring directed to a page they think is the frontend for an API, but is actually fraudulent. Anytime a web page tries to access an API on a different domain, that API will need to explicitly give the webpage permission, or the browser will block the request. This is why you can query the API from Node.js (no browser), and can put the REST API address directly into your address bar (same domain). However, trying to go from localhost:3000 to localhost:8008 or from file://path/to/your/index.html to localhost:8008 is going to get blocked.
Why doesn't the Sawtooth REST API handle OPTIONS requests?
The Sawtooth REST API does not know the domain you are going to run your web page from, so it can't whitelist it explicitly. It is possible to whitelist all domains, but this obviously destroys any protection CORS might give you. Rather than try to weigh the costs and benefits of this approach for all Sawtooth users everywhere, the decision was made to make the REST API as lightweight and security agnostic as possible. Any developer using it would be expected to put it behind a proxy server, and they can make whatever security decisions they need on that proxy layer.
So how do you fix it?
You need to setup a proxy server that will put the REST API and your web page on the same domain. There is no quick configuration option for this. You will have to set up an actual server. Obviously there are lots of ways to do this. If you are already familiar with Node, you could serve the page from Node.js, and then have the Node server proxy the API calls. If you are already running all of the Sawtooth components with docker-compose though, it might be easier to use Docker and Apache.
Setting up an Apache Proxy with Docker
Create your Dockerfile
In the same directory as your web app create a text file called "Dockerfile" (no extension). Then make it look like this:
FROM httpd:2.4
RUN echo "\
LoadModule proxy_module modules/mod_proxy.so\n\
LoadModule proxy_http_module modules/mod_proxy_http.so\n\
ProxyPass /api http://rest-api:8008\n\
ProxyPassReverse /api http://rest-api:8008\n\
RequestHeader set X-Forwarded-Path \"/api\"\n\
" >>/usr/local/apache2/conf/httpd.conf
This is going to do a couple of things. First it will pull down the httpd module from DockerHub, which is just a simple static server. Then we are using a bit of bash to add five lines to Apache's configuration file. These five lines import the proxy modules, tell Apache that we want to proxy http://rest-api:8008 to the /api route, and set the X-Forwarded-Path header so the REST API can properly build response URLs. Make sure that rest-api matches the actual name of the Sawtooth REST API service in your docker compose file.
Modify your docker compose file
Now, to the docker compose YAML file you are running Sawtooth through, you want to add a new property under the services key:
services:
my-web-page:
build: ./path/to/web/dir/
image: my-web-page
container_name: my-web-page
volumes:
- ./path/to/web/dir/public/:/usr/local/apache2/htdocs/
expose:
- 80
ports:
- '8000:80'
depends_on:
- rest-api
This will build your Dockerfile located at ./path/to/web/dir/Dockerfile (relative to the docker compose file), and run it with its default command, which is to start up Apache. Apache will serve whatever files are located in /usr/local/apache2/htdocs/, so we'll use volumes to link the path to your web files on your host machine (i.e. ./path/to/web/dir/public/), to that directory in the container. This is basically an alias, so if you update your web app later, you don't need to restart this docker container to see the changes. Finally, ports will take the server, which is at port 80 inside the container, and forward it out to localhost:8000.
Running it all
Now you should be able to run:
docker-compose -f path/to/your/compose-file.yaml up
And it will start up your Apache server along with the Sawtooth REST API and validator and any other services you defined. If you go to http://localhost:8000, you should see your web page, and if you go to http://localhost:8000/api/blocks, you should see a JSON representation of the blocks on chain. More importantly you should be able to make the request from your web app:
request.post({
url: 'api/batches',
body: batchListBytes,
headers: { 'Content-Type': 'application/octet-stream' }
}, (err, response) => console.log(response) );
Whew. Sorry for the long response, but I'm not sure if it is possible to solve CORS any faster. Hopefully this helps.
The transaction Header should have details like, address of the block where it would be save. Here is example which I have used and is working fine for me :
String payload = "create,0001,BLockchain CPU,Black,5000";
logger.info("Sending payload as - "+ payload);
String payloadBytes = Utils.hash512(payload.getBytes()); // --fix for invaluid payload seriqalization
ByteString payloadByteString = ByteString.copyFrom(payload.getBytes());
String address = getAddress(IDEM, ITEM_ID); // get unique address for input and output
logger.info("Sending address as - "+ address);
TransactionHeader txnHeader = TransactionHeader.newBuilder().clearBatcherPublicKey()
.setBatcherPublicKey(publicKeyHex)
.setFamilyName(IDEM) // Idem Family
.setFamilyVersion(VER)
.addInputs(address)
.setNonce("1")
.addOutputs(address)
.setPayloadSha512(payloadBytes)
.setSignerPublicKey(publicKeyHex)
.build();
ByteString txnHeaderBytes = txnHeader.toByteString();
byte[] txnHeaderSignature = privateKey.signMessage(txnHeaderBytes.toString()).getBytes();
String value = Signing.sign(privateKey, txnHeader.toByteArray());
Transaction txn = Transaction.newBuilder().setHeader(txnHeaderBytes).setPayload(payloadByteString)
.setHeaderSignature(value).build();
BatchHeader batchHeader = BatchHeader.newBuilder().clearSignerPublicKey().setSignerPublicKey(publicKeyHex)
.addTransactionIds(txn.getHeaderSignature()).build();
ByteString batchHeaderBytes = batchHeader.toByteString();
byte[] batchHeaderSignature = privateKey.signMessage(batchHeaderBytes.toString()).getBytes();
String value_batch = Signing.sign(privateKey, batchHeader.toByteArray());
Batch batch = Batch.newBuilder()
.setHeader(batchHeaderBytes)
.setHeaderSignature(value_batch)
.setTrace(true)
.addTransactions(txn)
.build();
BatchList batchList = BatchList.newBuilder()
.addBatches(batch)
.build();
ByteString batchBytes = batchList.toByteString();
String serverResponse = Unirest.post("http://localhost:8008/batches")
.header("Content-Type", "application/octet-stream")
.body(batchBytes.toByteArray())
.asString()
.getBody();
I'm developing a little project with microservices, I want to configure Kong as the API-Gateway for my structure but I'm not able to forward incoming requests from Kong to my services.
For example, I have a docker container running locally on address 172.18.0.15 and port 8006. I can access the api just fine at 172.18.0.15:8006/compute, I would like to configure kong in order to be able to make the same call at localhost:8001/compute. Am I missing something? Should api be present in my url in order for Kong to discover my api? This is the configuration that I've added to Kong, I basically just added all REST methods and set url:port as the upstream-url field.
{
"data": [
{
"created_at": 1514479841682,
"http_if_terminated": false,
"https_only": false,
"id": "9cf925a5-2c27-4dbc-ae2a-e70fba94ef53",
"methods": [
"GET",
"POST",
"PUT",
"DELETE"
],
"name": "compute",
"preserve_host": false,
"retries": 5,
"strip_uri": true,
"upstream_connect_timeout": 60000,
"upstream_read_timeout": 60000,
"upstream_send_timeout": 60000,
"upstream_url": "http://172.18.0.15:8006"
}
],
"total": 1
}
This is not working for me: when I try to access the API through localhost, the request times out with a 499 error. Any help is highly appreciated.
EDIT: I've tried adding my API by setting the URI field instead. I've inspected using docker logs kong and it looks like kong is definetely accessing the correct address, but is timing out for some unknown reason:
2017/12/28 17:55:25 [error] 47#0: *103 upstream timed out (110: Connection timed out) while connecting to upstream, client: 172.17.0.1, server: kong, request: "GET /compute?origin=20,20&destination=20,20 HTTP/1.1", upstream: "http://172.18.0.15:8006/compute?origin=20,20&destination=20,20", host: "localhost:8000"
However, if I access the same address using curl it works just fine:
# curl http://172.18.0.15:8006/compute?origin=20,20\&destination=20,20
[]
I highly doubt anybody else will encounter the same issue but the problem was in my Docker configuration, I forgot to put the api gateway on the same subnet as the services. Fixing this solved my issue.
I'm building a Rails API, and I'm writing a test that sending a curl request works. This seems to be a testing issue, because an actual curl request works:
$ curl -X POST -d temperature=68 localhost:3000/temperature_readings.json
# => {"status":200}
Here's the controller method:
def create
TemperatureReading.create(temperature: params[:temperature])
render json: { status: 200 }
end
Here's the test:
context 'works via a curl request' do
it 'works' do
system "curl -X POST -d 'temperature=68' #{temperature_readings_url(format: 'json')}"
expect(TemperatureReading.last.temperature).to eq(68)
end
end
I'm getting an error from curl that test.host does not resolve:
curl: (6) Could not resolve host: test.host
That makes sense, because when I call temperature_readings_url(format: 'json') from within pry in that method, I get http://test.host/temperatures.json.
Is there a better way to test that my controller can successfully handle a curl request and create the correct record with the correct attributes?
What I've Tried
I made sure that protect_from_forgery with: :null_session is set in ApplicationController so that it doesn't fail with an exception when it can't find the token
I tried adding -H 'Content-Type:application/json' to the curl request to make sure it's hitting the json format in the controller. But it really seems to boil down to the fact that the working URL in the testing environment is test.host.
So I explicitly set request.host to localhost:3000 in the test, which felt a little hacky. But then of course it just posted to my dev database and not my test database, so the test still failed.
Your app doesn't know its host outside the request context, so you have to provide it anyway. This is also hacky:
temperature_readings_url(format: 'json', host: 'localhost:3000').
But you can define it globally in config/enviroments/test.rb:
Rails.application.routes.default_url_options[:host] = 'localhost:3000'
so in test enviroment all url helpers will return localhost:3000 as host by default.
I am writing a command-line script in Dart. What's the easiest way to access (and GET) an HTTP resource?
Use the http package for easy command-line access to HTTP resources. While the core dart:io library has the primitives for HTTP clients (see HttpClient), the http package makes it much easier to GET, POST, etc.
First, add http to your pubspec's dependencies:
name: sample_app
description: My sample app.
dependencies:
http: any
Install the package. Run this on the command line or via Dart Editor:
pub install
Import the package:
// inside your app
import 'package:http/http.dart' as http;
Make a GET request. The get() function returns a Future.
http.get('http://example.com/hugs').then((response) => print(response.body));
It's best practice to return the Future from the function that uses get():
Future getAndParse(String uri) {
return http.get('http://example.com/hugs')
.then((response) => JSON.parse(response.body));
}
Unfortunately, I couldn't find any formal docs. So I had to look through the code (which does have good comments): https://code.google.com/p/dart/source/browse/trunk/dart/pkg/http/lib/http.dart
this is the shortest code i could find
curl -sL -w "%{http_code} %{url_effective}\\n" "URL" -o /dev/null
Here, -s silences curl's progress output, -L follows all redirects as before, -w prints the report using a custom format, and -o redirects curl's HTML output to /dev/null.
Here are the other special variables available in case you want to customize the output some more:
url_effective
http_code
http_connect
time_total
time_namelookup
time_connect
time_pretransfer
time_redirect
time_starttransfer
size_download
size_upload
size_header
size_request
speed_download
speed_upload
content_type
num_connects
num_redirects
ftp_entry_path