Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ target
/RUNNING_PID
/htmlReport
*.iml
.DS_Store
15 changes: 7 additions & 8 deletions app/actors/SourcesInfoActor.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@
public class SourcesInfoActor extends AbstractBehavior<SourcesInfoActor.Message> {
/** HTTP client used to call external APIs (e.g., NewsAPI) and fetch JSON responses. */
private final WSClient wsClient;
/** API key read from configuration and attached to outbound API requests. */
private final String apiKey;
/** Cache to store list of sources information from the News Api */
private SourcesInfo cachedInfo = null;

Expand All @@ -35,9 +33,11 @@ public interface Message {}
*/
public static class GetSourcesInfo implements Message {
public final ActorRef<Response> replyTo;
public final String apiKey;

public GetSourcesInfo(ActorRef<Response> replyTo) {
public GetSourcesInfo(String apiKey, ActorRef<Response> replyTo) {
this.replyTo = replyTo;
this.apiKey = apiKey;
}
}

Expand All @@ -64,14 +64,13 @@ public SourcesInfoFailed(String error) {
}
}

public static Behavior<Message> create(WSClient wsClient, String apiKey) {
return Behaviors.setup(context -> new SourcesInfoActor(context, wsClient, apiKey));
public static Behavior<Message> create(WSClient wsClient) {
return Behaviors.setup(context -> new SourcesInfoActor(context, wsClient));
}

private SourcesInfoActor(ActorContext<Message> context, WSClient wsClient, String apiKey) {
private SourcesInfoActor(ActorContext<Message> context, WSClient wsClient) {
super(context);
this.wsClient = wsClient;
this.apiKey = apiKey;
}

@Override
Expand All @@ -91,7 +90,7 @@ private Behavior<Message> onGetSourcesInfo(GetSourcesInfo msg) {

getContext().getLog().info("SourcesInfoActor processing request for all sources.");

SourcesInfo sourcesInfoModel = new SourcesInfo(this.apiKey);
SourcesInfo sourcesInfoModel = new SourcesInfo(msg.apiKey);
CompletionStage<Void> future = sourcesInfoModel.fetchSourcesInfo(wsClient);

future.whenComplete((ignored, error) -> {
Expand Down
2 changes: 1 addition & 1 deletion app/actors/SupervisorActor.java
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ public static Behavior<Message> create(WSClient wsClient) {
);
// create sourceInfo actor
ActorRef<SourcesInfoActor.Message> sourcesInfoActor = context.spawn(
SourcesInfoActor.create(wsClient, apiKey),
SourcesInfoActor.create(wsClient),
"sourcesInfoActor"
);
context.getLog().info("sourceActor and sourcesInfoActor spawned and ready");
Expand Down
2 changes: 0 additions & 2 deletions app/actors/UserActor.java
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ private static class SearchInstance {
* @return behavior for handling user-specific messages
* @author Nattamon Paiboon
*/
// TODO rm cache
public static Behavior<Message> create(String id, ActorRef<NewsActor.Message> newsActor, ActorRef<ReadabilityActor.Message> rActor, ActorRef<SentimentAnalyzerActor.Command> sentimentActor) {
return Behaviors.setup(context -> new UserActor(id, context, newsActor, rActor, sentimentActor).behavior());
}
Expand All @@ -207,7 +206,6 @@ public static Behavior<Message> create(String id, ActorRef<NewsActor.Message> ne
* @param rActor reference to the ReadbailityActor for getting readability
* @author Nattamon Paiboon
*/
// TODO rm cache
private UserActor(String id, ActorContext<Message> context, ActorRef<NewsActor.Message> newsActor, ActorRef<ReadabilityActor.Message> rActor, ActorRef<SentimentAnalyzerActor.Command> sentimentActor) {
this.id = id;
this.context = context;
Expand Down
10 changes: 2 additions & 8 deletions app/controllers/HomeController.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ public class HomeController extends Controller {
/** API key read from configuration and attached to outbound API requests. */
private final String apiKey;

/** Asynchronous cache used to store session history and source data with a TTL. */
private final AsyncCacheApi asyncCache;

private final ActorSystem<Void> actorSystem;
private final ActorRef<SupervisorActor.Message> supervisorActor;

Expand All @@ -57,17 +54,14 @@ public class HomeController extends Controller {
*
* @author Luan Tran, Nattamon Paiboon
* @param ws the WSClient used to send HTTP requests to the News API
* @param asyncCache the cache used to store user search history and source information
* @param config the application's configuration, which contains the API key
*/
@Inject
public HomeController(WSClient ws, AsyncCacheApi asyncCache, Config config, ActorSystem<Void> actorSystem) {
public HomeController(WSClient ws, Config config, ActorSystem<Void> actorSystem) {
this.ws = ws;
this.asyncCache = asyncCache;
this.apiKey = config.getString("api.key");

this.actorSystem = actorSystem;
// TODO rm cache
this.supervisorActor = actorSystem.systemActorOf(
SupervisorActor.create(ws), "SupervisorActor", Props.empty()
);
Expand Down Expand Up @@ -145,7 +139,7 @@ public CompletionStage<Result> source(String identifier, String sourceName) {
// Ask SourcesInfoActor for the list of all sources
return AskPattern.ask(
sourcesInfoActor,
(ActorRef<SourcesInfoActor.Response> replyTo) -> new SourcesInfoActor.GetSourcesInfo(replyTo),
(ActorRef<SourcesInfoActor.Response> replyTo) -> new SourcesInfoActor.GetSourcesInfo(this.apiKey, replyTo),
Duration.ofSeconds(10),
actorSystem.scheduler()
).thenCompose(infoResponse -> {
Expand Down
1 change: 1 addition & 0 deletions conf/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ api {
# "test"
"a482d6d109894277b9ec6a6d2f4e83e9",
"1002d5e24b1a4a0b92719ed6281694a9",
"c3f75cdb287647839659a5d323e7d01a"
]
}
14 changes: 7 additions & 7 deletions test/actors/SourcesInfoActorTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,10 @@ public void testGetSourcesInfo_Success() {
when(mockResponse.getStatus()).thenReturn(200);
when(mockResponse.asJson()).thenReturn(json);

ActorRef<SourcesInfoActor.Message> actor = testKit.spawn(SourcesInfoActor.create(mockWsClient, "key"));
ActorRef<SourcesInfoActor.Message> actor = testKit.spawn(SourcesInfoActor.create(mockWsClient));
TestProbe<SourcesInfoActor.Response> probe = testKit.createTestProbe();

actor.tell(new SourcesInfoActor.GetSourcesInfo(probe.getRef()));
actor.tell(new SourcesInfoActor.GetSourcesInfo("key", probe.getRef()));

SourcesInfoActor.SourcesInfoLoaded response = probe.expectMessageClass(SourcesInfoActor.SourcesInfoLoaded.class);
assertNotNull(response.sourcesInfo);
Expand All @@ -90,10 +90,10 @@ public void testGetSourcesInfo_Failure() {
when(mockRequest.get()).thenReturn(CompletableFuture.completedFuture(mockResponse));
when(mockResponse.getStatus()).thenReturn(500); // Server error

ActorRef<SourcesInfoActor.Message> actor = testKit.spawn(SourcesInfoActor.create(mockWsClient, "key"));
ActorRef<SourcesInfoActor.Message> actor = testKit.spawn(SourcesInfoActor.create(mockWsClient));
TestProbe<SourcesInfoActor.Response> probe = testKit.createTestProbe();

actor.tell(new SourcesInfoActor.GetSourcesInfo(probe.getRef()));
actor.tell(new SourcesInfoActor.GetSourcesInfo("key", probe.getRef()));

SourcesInfoActor.SourcesInfoFailed response = probe.expectMessageClass(SourcesInfoActor.SourcesInfoFailed.class);
assertNotNull(response.error);
Expand All @@ -115,15 +115,15 @@ public void testGetSourcesInfo_Caching() {
when(mockResponse.getStatus()).thenReturn(200);
when(mockResponse.asJson()).thenReturn(json);

ActorRef<SourcesInfoActor.Message> actor = testKit.spawn(SourcesInfoActor.create(mockWsClient, "key"));
ActorRef<SourcesInfoActor.Message> actor = testKit.spawn(SourcesInfoActor.create(mockWsClient));
TestProbe<SourcesInfoActor.Response> probe = testKit.createTestProbe();

// 1st Call
actor.tell(new SourcesInfoActor.GetSourcesInfo(probe.getRef()));
actor.tell(new SourcesInfoActor.GetSourcesInfo("key", probe.getRef()));
probe.expectMessageClass(SourcesInfoActor.SourcesInfoLoaded.class);

// 2nd Call: Hits Cache
actor.tell(new SourcesInfoActor.GetSourcesInfo(probe.getRef()));
actor.tell(new SourcesInfoActor.GetSourcesInfo("key", probe.getRef()));
probe.expectMessageClass(SourcesInfoActor.SourcesInfoLoaded.class);

// Verify API called ONCE
Expand Down