Dev #65
29
mocks/README.md
Normal file
29
mocks/README.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
In this directory are some files to mock an auction house to test WebSub local.
|
||||||
|
|
||||||
|
To run a local WebSubHub instance
|
||||||
|
|
||||||
|
1. Start a mongodb in docker:
|
||||||
|
|
||||||
|
- docker run -d -p 27017:27017 -p 28017:28017 -e AUTH=no tutum/mongodb
|
||||||
|
|
||||||
|
2. Install a local hub
|
||||||
|
|
||||||
|
- yarn global add websub-hub
|
||||||
|
|
||||||
|
3. Run the hub localy
|
||||||
|
|
||||||
|
- websub-hub -l info -m mongodb://localhost:27017/hub
|
||||||
|
|
||||||
|
Create an example subscription
|
||||||
|
|
||||||
|
- node auction-house/subscriber.js
|
||||||
|
|
||||||
|
Create an example auctionhouse
|
||||||
|
|
||||||
|
- node auction-house/auctions.js
|
||||||
|
|
||||||
|
Publish to the hub
|
||||||
|
|
||||||
|
- node auction-house/publisher.js
|
||||||
|
|
||||||
|
Mostly inspired by: https://github.com/hemerajs/websub-hub
|
39
mocks/auction-house/auctions.js
Normal file
39
mocks/auction-house/auctions.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// Require the framework and instantiate it
|
||||||
|
const fastify = require('fastify')({ logger: true })
|
||||||
|
|
||||||
|
// Declare a route
|
||||||
|
fastify.get('/auctions', async (request, reply) => {
|
||||||
|
console.log('content provided')
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
content_text: 'This is a second item.',
|
||||||
|
url: 'https://example.org/second-item'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
content_html: '<p>Hello, world!</p>',
|
||||||
|
url: 'https://example.org/initial-post'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
fastify.get('/websub', async (request, reply) => {
|
||||||
|
console.log('content provided')
|
||||||
|
|
||||||
|
return {
|
||||||
|
topic: 'http://localhost:3100/auctions'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Run the server!
|
||||||
|
const start = async () => {
|
||||||
|
try {
|
||||||
|
await fastify.listen(3100)
|
||||||
|
} catch (err) {
|
||||||
|
fastify.log.error(err)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
start()
|
17
mocks/auction-house/publisher.js
Normal file
17
mocks/auction-house/publisher.js
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
const axios = require('axios').default
|
||||||
|
|
||||||
|
// Run the server!
|
||||||
|
const start = async () => {
|
||||||
|
await axios
|
||||||
|
.post('http://localhost:3000/publish', {
|
||||||
|
'hub.mode': 'publish',
|
||||||
|
'hub.url': 'http://localhost:3100/auctions'
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
console.log(response.data)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
start()
|
42
mocks/auction-house/subscriber.js
Normal file
42
mocks/auction-house/subscriber.js
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
// Require the framework and instantiate it
|
||||||
|
const fastify = require('fastify')({ logger: true })
|
||||||
|
const axios = require('axios').default
|
||||||
|
|
||||||
|
// Declare a route
|
||||||
|
fastify.get('/auction-created', async (request, reply) => {
|
||||||
|
console.log('subscription verified', request.query)
|
||||||
|
console.log(request.query)
|
||||||
|
return request.query
|
||||||
|
})
|
||||||
|
|
||||||
|
fastify.post('/auction-created', async (request, reply) => {
|
||||||
|
console.log('received blog content', request.body)
|
||||||
|
reply.send()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Run the server!
|
||||||
|
const start = async () => {
|
||||||
|
// subscribe to the feed
|
||||||
|
|
||||||
|
try {
|
||||||
|
await fastify.listen(3200)
|
||||||
|
} catch (err) {
|
||||||
|
fastify.log.error(err)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
await axios
|
||||||
|
.post('http://localhost:3000', {
|
||||||
|
'hub.callback': 'http://localhost:3200/auction-created',
|
||||||
|
'hub.mode': 'subscribe',
|
||||||
|
'hub.topic': 'http://localhost:3100/auctions',
|
||||||
|
'hub.ws': false
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
console.log(response.data)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
start()
|
|
@ -58,6 +58,17 @@
|
||||||
<artifactId>validation-api</artifactId>
|
<artifactId>validation-api</artifactId>
|
||||||
<version>1.1.0.Final</version>
|
<version>1.1.0.Final</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.json</groupId>
|
||||||
|
<artifactId>json</artifactId>
|
||||||
|
<version>20210307</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-devtools</artifactId>
|
||||||
|
<scope>runtime</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ch.unisg.tapas.common.AuctionHouseResourceDirectory;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.eclipse.paho.client.mqttv3.MqttException;
|
import org.eclipse.paho.client.mqttv3.MqttException;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@ -26,17 +27,21 @@ public class TapasAuctionHouseApplication {
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
SpringApplication tapasAuctioneerApp = new SpringApplication(TapasAuctionHouseApplication.class);
|
SpringApplication tapasAuctioneerApp = new SpringApplication(TapasAuctionHouseApplication.class);
|
||||||
|
|
||||||
// We will use these bootstrap methods in Week 6:
|
|
||||||
// bootstrapMarketplaceWithWebSub();
|
|
||||||
// bootstrapMarketplaceWithMqtt();
|
|
||||||
|
|
||||||
tapasAuctioneerApp.run(args);
|
tapasAuctioneerApp.run(args);
|
||||||
|
|
||||||
|
// We will use these bootstrap methods in Week 6:
|
||||||
|
|
||||||
|
// bootstrapMarketplaceWithMqtt();
|
||||||
|
bootstrapMarketplaceWithWebSub();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discovers auction houses and subscribes to WebSub notifications
|
* Discovers auction houses and subscribes to WebSub notifications
|
||||||
*/
|
*/
|
||||||
private static void bootstrapMarketplaceWithWebSub() {
|
private static void bootstrapMarketplaceWithWebSub() {
|
||||||
|
System.out.println("HAHA");
|
||||||
List<String> auctionHouseEndpoints = discoverAuctionHouseEndpoints();
|
List<String> auctionHouseEndpoints = discoverAuctionHouseEndpoints();
|
||||||
LOGGER.info("Found auction house endpoints: " + auctionHouseEndpoints);
|
LOGGER.info("Found auction house endpoints: " + auctionHouseEndpoints);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,18 @@
|
||||||
package ch.unisg.tapas.auctionhouse.adapter.common.clients;
|
package ch.unisg.tapas.auctionhouse.adapter.common.clients;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscribes to the WebSub hubs of auction houses discovered at run time. This class is instantiated
|
* Subscribes to the WebSub hubs of auction houses discovered at run time. This class is instantiated
|
||||||
|
@ -9,7 +21,23 @@ import java.net.URI;
|
||||||
*/
|
*/
|
||||||
public class WebSubSubscriber {
|
public class WebSubSubscriber {
|
||||||
|
|
||||||
|
// TODO get this somehow from properties file. But on clue how to do this with static variables
|
||||||
|
static String WEBSUB_HUB_ENDPOINT = "http://localhost:3000";
|
||||||
|
static String AUCTION_HOUSE_ENDPOINT = "http://localhost:8086";
|
||||||
|
|
||||||
|
Logger logger = Logger.getLogger(WebSubSubscriber.class.getName());
|
||||||
|
|
||||||
public void subscribeToAuctionHouseEndpoint(URI endpoint) {
|
public void subscribeToAuctionHouseEndpoint(URI endpoint) {
|
||||||
|
// TODO decide with other groups about auction house endpoint uri to discover websub topics
|
||||||
|
// and replace the hardcoded one with it
|
||||||
|
String topic = discoverWebSubTopic("http://localhost:3100/websub");
|
||||||
|
|
||||||
|
if (topic == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeToWebSub(topic);
|
||||||
|
|
||||||
// TODO Subscribe to the auction house endpoint via WebSub:
|
// TODO Subscribe to the auction house endpoint via WebSub:
|
||||||
// 1. Send a request to the auction house in order to discover the WebSub hub to subscribe to.
|
// 1. Send a request to the auction house in order to discover the WebSub hub to subscribe to.
|
||||||
// The request URI should depend on the design of the Auction House HTTP API.
|
// The request URI should depend on the design of the Auction House HTTP API.
|
||||||
|
@ -25,4 +53,61 @@ public class WebSubSubscriber {
|
||||||
// - W3C WebSub Recommendation: https://www.w3.org/TR/websub/
|
// - W3C WebSub Recommendation: https://www.w3.org/TR/websub/
|
||||||
// - the implementation notes of the WebSub hub you are using to distribute events
|
// - the implementation notes of the WebSub hub you are using to distribute events
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String discoverWebSubTopic(String endpoint) {
|
||||||
|
HttpClient client = HttpClient.newHttpClient();
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(endpoint))
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.GET()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
if (response.statusCode() == HttpStatus.OK.value()) {
|
||||||
|
// TODO decide with other groups about response structure and replace the hardcoded
|
||||||
|
// uri with response uri
|
||||||
|
JSONObject jsonObject = new JSONObject(response.body());
|
||||||
|
System.out.println(jsonObject);
|
||||||
|
return jsonObject.getString("topic");
|
||||||
|
} else {
|
||||||
|
logger.log(Level.SEVERE, "Could not find a websub uri");
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void subscribeToWebSub(String topic) {
|
||||||
|
HttpClient client = HttpClient.newHttpClient();
|
||||||
|
|
||||||
|
String body = new JSONObject()
|
||||||
|
.put("hub.callback", AUCTION_HOUSE_ENDPOINT + "/auction-started")
|
||||||
|
.put("hub.mode", "subscribe")
|
||||||
|
.put("hub.topic", topic)
|
||||||
|
.put("hub.ws", false)
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(WEBSUB_HUB_ENDPOINT))
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofString(body))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package ch.unisg.tapas.auctionhouse.adapter.in.messaging.websub;
|
package ch.unisg.tapas.auctionhouse.adapter.in.messaging.websub;
|
||||||
|
|
||||||
import ch.unisg.tapas.auctionhouse.application.handler.AuctionStartedHandler;
|
import ch.unisg.tapas.auctionhouse.application.handler.AuctionStartedHandler;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,6 +17,21 @@ public class AuctionStartedEventListenerWebSubAdapter {
|
||||||
public AuctionStartedEventListenerWebSubAdapter(AuctionStartedHandler auctionStartedHandler) {
|
public AuctionStartedEventListenerWebSubAdapter(AuctionStartedHandler auctionStartedHandler) {
|
||||||
this.auctionStartedHandler = auctionStartedHandler;
|
this.auctionStartedHandler = auctionStartedHandler;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Controller which listens to auction-started callbacks
|
||||||
|
* @return 200 OK
|
||||||
|
**/
|
||||||
|
@PostMapping(path = "/auction-started")
|
||||||
|
public ResponseEntity<Void> handleExecutorAddedEvent(@RequestBody String payload) {
|
||||||
|
|
||||||
//TODO
|
// Payload should be a JSONArray with auctions
|
||||||
|
JSONArray jsonArray = new JSONArray(payload);
|
||||||
|
for (Object auction : jsonArray) {
|
||||||
|
System.out.println(auction);
|
||||||
|
// TODO logic to call handleAuctionStartedEvent()
|
||||||
|
// auctionStartedHandler.handleAuctionStartedEvent(auctionStartedEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ResponseEntity<>(HttpStatus.OK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package ch.unisg.tapas.auctionhouse.adapter.in.messaging.websub;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class validates the subscription intent from the websub hub
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
public class ValidateIntentWebSubAdapter {
|
||||||
|
|
||||||
|
@Value("${application.environment}")
|
||||||
|
private String environment;
|
||||||
|
|
||||||
|
@GetMapping(path = "/auction-started")
|
||||||
|
public ResponseEntity<String> handleExecutorAddedEvent(@RequestParam("hub.challenge") String challenge) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Different implementation depending on local development or production
|
||||||
|
if (environment.equalsIgnoreCase("development")) {
|
||||||
|
HttpHeaders headers = new HttpHeaders();
|
||||||
|
headers.add("Content-Type", "application/json");
|
||||||
|
String body = new JSONObject()
|
||||||
|
.put("hub.challenge", challenge)
|
||||||
|
.toString();
|
||||||
|
return new ResponseEntity<>(body, headers, HttpStatus.OK);
|
||||||
|
} else {
|
||||||
|
return new ResponseEntity<>(challenge, HttpStatus.OK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,12 +4,16 @@ import ch.unisg.tapas.auctionhouse.application.port.out.AuctionStartedEventPort;
|
||||||
import ch.unisg.tapas.auctionhouse.domain.Auction;
|
import ch.unisg.tapas.auctionhouse.domain.Auction;
|
||||||
import ch.unisg.tapas.auctionhouse.domain.AuctionStartedEvent;
|
import ch.unisg.tapas.auctionhouse.domain.AuctionStartedEvent;
|
||||||
import ch.unisg.tapas.common.ConfigProperties;
|
import ch.unisg.tapas.common.ConfigProperties;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
import java.net.http.HttpRequest;
|
import java.net.http.HttpRequest;
|
||||||
|
@ -17,6 +21,8 @@ import java.net.http.HttpResponse;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -30,8 +36,38 @@ public class PublishAuctionStartedEventWebSubAdapter implements AuctionStartedEv
|
||||||
@Autowired
|
@Autowired
|
||||||
private ConfigProperties config;
|
private ConfigProperties config;
|
||||||
|
|
||||||
|
@Value("${auctionhouse.uri}")
|
||||||
|
private String auctionHouseUri;
|
||||||
|
|
||||||
|
@Value("${websub.hub.uri}")
|
||||||
|
private String webSubHubUri;
|
||||||
|
|
||||||
|
Logger logger = Logger.getLogger(PublishAuctionStartedEventWebSubAdapter.class.getName());
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void publishAuctionStartedEvent(AuctionStartedEvent event) {
|
public void publishAuctionStartedEvent(AuctionStartedEvent event) {
|
||||||
// TODO
|
HttpClient client = HttpClient.newHttpClient();
|
||||||
|
|
||||||
|
String body = new JSONObject()
|
||||||
|
.put("hub.url", auctionHouseUri + "/auctions")
|
||||||
|
.put("hub.mode", "publish")
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(webSubHubUri))
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofString(body))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
client.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,3 +6,7 @@ websub.hub.publish=https://websub.appspot.com/
|
||||||
group=tapas-group-tutors
|
group=tapas-group-tutors
|
||||||
auction.house.uri=https://tapas-auction-house.86-119-34-23.nip.io/
|
auction.house.uri=https://tapas-auction-house.86-119-34-23.nip.io/
|
||||||
tasks.list.uri=https://tapas-tasks.86-119-34-23.nip.io/
|
tasks.list.uri=https://tapas-tasks.86-119-34-23.nip.io/
|
||||||
|
|
||||||
|
application.environment=development
|
||||||
|
auctionhouse.uri=http://localhost:8086
|
||||||
|
websub.hub.uri=http://localhost:3000
|
||||||
|
|
Loading…
Reference in New Issue
Block a user