Skip to content

Commit 317859b

Browse files
committed
Prepare to migrate to remote state
1 parent 2586f4e commit 317859b

30 files changed

+477
-7378
lines changed

java/pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@
4545
<version>1.11.438</version>
4646
<scope>compile</scope>
4747
</dependency>
48+
<dependency>
49+
<groupId>com.amazonaws</groupId>
50+
<artifactId>aws-java-sdk-secretsmanager</artifactId>
51+
<version>1.11.414</version>
52+
<scope>compile</scope>
53+
</dependency>
4854
<dependency>
4955
<groupId>com.fasterxml.jackson.core</groupId>
5056
<artifactId>jackson-databind</artifactId>
@@ -65,6 +71,11 @@
6571
<artifactId>jackson-datatype-joda</artifactId>
6672
<version>2.4.5</version>
6773
</dependency>
74+
<dependency>
75+
<groupId>com.google.firebase</groupId>
76+
<artifactId>firebase-admin</artifactId>
77+
<version>6.5.0</version>
78+
</dependency>
6879
</dependencies>
6980

7081
<build>

java/src/main/java/com/maths22/laundryviewapi/LaundryViewEndpoint.java

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
77
import com.fasterxml.jackson.databind.DeserializationFeature;
88
import com.fasterxml.jackson.databind.ObjectMapper;
9+
import com.mashape.unirest.http.Unirest;
910
import com.maths22.laundryviewapi.data.LaundryRoom;
1011
import com.maths22.laundryviewapi.data.RoomMachineStatus;
1112
import com.maths22.laundryviewapi.data.School;
@@ -16,6 +17,7 @@
1617
import java.io.IOException;
1718
import java.io.InputStream;
1819
import java.io.OutputStream;
20+
import java.util.HashMap;
1921
import java.util.List;
2022
import java.util.Map;
2123
import java.util.concurrent.TimeUnit;
@@ -71,8 +73,13 @@ public static void main(String args[]) throws IOException {
7173
public void handleRequest(InputStream is, OutputStream os, Context ctx) throws IOException {
7274
// LambdaLogger logger = ctx.getLogger();
7375

74-
APIGatewayProxyRequestEvent req = om.readValue(is, APIGatewayProxyRequestEvent.class);
75-
LvRequest request = om.readValue(req.getBody(), LvRequest.class);
76+
HashMap req = om.readValue(is, HashMap.class);
77+
if("Scheduled Event".equals(req.get("detail-type"))) {
78+
new NotificationManager().handleRequest();
79+
return;
80+
}
81+
Unirest.setTimeouts(3000, 10000);
82+
LvRequest request = om.readValue((String)req.get("body"), LvRequest.class);
7683
Object ret = null;
7784
switch(request.getMethod()) {
7885
case "findSchools":
@@ -82,7 +89,21 @@ public void handleRequest(InputStream is, OutputStream os, Context ctx) throws I
8289
ret = findLaundryRooms((String) request.getArgs().get("schoolId"));
8390
break;
8491
case "machineStatus":
85-
ret = findLaundryRooms((String) request.getArgs().get("roomId"));
92+
ret = machineStatus((String) request.getArgs().get("roomId"));
93+
break;
94+
95+
case "registerMachine":
96+
ret = registerNotification(
97+
(String) request.getArgs().get("machineId"),
98+
(String) request.getArgs().get("requesterId")
99+
);
100+
break;
101+
102+
case "unregisterMachine":
103+
ret = unregisterNotification(
104+
(String) request.getArgs().get("machineId"),
105+
(String) request.getArgs().get("requesterId")
106+
);
86107
break;
87108
}
88109

@@ -123,16 +144,24 @@ public RoomMachineStatus machineStatus(String roomId) {
123144
DynamoCache cache = new DynamoCache();
124145
cache.setTtl(TimeUnit.MINUTES.toSeconds(1));
125146

126-
if(cache != null) {
127-
TimeStampedObject<RoomMachineStatus> entry = (TimeStampedObject<RoomMachineStatus>) cache.get("machineStatus?roomId=" + roomId);
147+
TimeStampedObject<RoomMachineStatus> entry = (TimeStampedObject<RoomMachineStatus>) cache.get("machineStatus?roomId=" + roomId);
128148

129-
if(entry != null && Minutes.minutesBetween(entry.getTime(), new DateTime()).isLessThan(Minutes.minutes(1))) {
130-
return entry.getObject();
131-
}
149+
if(entry != null && Minutes.minutesBetween(entry.getTime(), new DateTime()).isLessThan(Minutes.minutes(1))) {
150+
return entry.getObject();
132151
}
133152
RoomMachineStatus ret = new MachineStatus().lookup(roomId);
134153
cache.put("machineStatus?roomId=" + roomId,
135154
new TimeStampedObject<>(ret, new DateTime()));
136155
return ret;
137156
}
157+
158+
public String registerNotification(String machineId, String requesterId) {
159+
new NotificationManager().register(machineId, requesterId);
160+
return "ok";
161+
}
162+
163+
public String unregisterNotification(String machineId, String requesterId) {
164+
new NotificationManager().unregister(machineId, requesterId);
165+
return "ok";
166+
}
138167
}

java/src/main/java/com/maths22/laundryviewapi/MachineStatus.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@
1717
*/
1818
public class MachineStatus {
1919

20-
private CloseableHttpClient redirectClient = HttpClients.custom()
21-
.disableRedirectHandling()
22-
.build();
23-
2420
/**
2521
* Method handling HTTP GET requests. The returned object will be sent to
2622
* the client as "text/plain" media type.
@@ -30,13 +26,13 @@ public class MachineStatus {
3026
*/
3127
public RoomMachineStatus lookup(String roomId) {
3228
HttpResponse<JsonNode> response;
33-
Unirest.setHttpClient(redirectClient);
3429
try {
3530
response = Unirest.get("https://laundryview.com/api/currentRoomData")
3631
.queryString("location", roomId)
3732
.asJson();
3833
} catch (UnirestException e) {
3934
e.printStackTrace();
35+
4036
throw new RuntimeException("LaundryView Request Failed");
4137
}
4238

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package com.maths22.laundryviewapi;
2+
3+
import com.amazonaws.client.builder.AwsClientBuilder;
4+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
5+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
6+
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
7+
import com.amazonaws.services.dynamodbv2.document.Item;
8+
import com.amazonaws.services.dynamodbv2.document.Table;
9+
import com.amazonaws.services.dynamodbv2.document.spec.DeleteItemSpec;
10+
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
11+
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
12+
import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest;
13+
import com.amazonaws.services.secretsmanager.model.GetSecretValueResult;
14+
import com.google.auth.oauth2.GoogleCredentials;
15+
import com.google.firebase.FirebaseApp;
16+
import com.google.firebase.FirebaseOptions;
17+
import com.google.firebase.messaging.*;
18+
import com.maths22.laundryviewapi.data.Machine;
19+
import com.maths22.laundryviewapi.data.RoomMachineStatus;
20+
import com.maths22.laundryviewapi.data.Status;
21+
22+
import java.io.ByteArrayInputStream;
23+
import java.io.IOException;
24+
import java.nio.file.Files;
25+
import java.nio.file.Paths;
26+
import java.util.Calendar;
27+
import java.util.HashMap;
28+
import java.util.Map;
29+
import java.util.Optional;
30+
31+
public class NotificationManager {
32+
private Table ddbTable;
33+
34+
public NotificationManager() {
35+
AmazonDynamoDB client;
36+
if(System.getenv("dynamo-local") != null) {
37+
client = AmazonDynamoDBClientBuilder.standard()
38+
.withEndpointConfiguration(
39+
new AwsClientBuilder.EndpointConfiguration("http://ddblocal.docker:8000", "us-west-2"))
40+
.build();
41+
} else {
42+
client = AmazonDynamoDBClientBuilder.standard()
43+
.build();
44+
}
45+
DynamoDB db = new DynamoDB(client);
46+
ddbTable = db.getTable(System.getenv("notification_table"));
47+
48+
}
49+
50+
51+
public void register(String machineId, String requesterId) {
52+
Calendar cal = Calendar.getInstance();
53+
cal.add(Calendar.HOUR, 12);
54+
long expiry = cal.getTimeInMillis() / 1000L;
55+
Item item = new Item()
56+
.withPrimaryKey("RequesterId", requesterId,
57+
"MachineId", machineId)
58+
.withNumber("TimeToExist", expiry);
59+
ddbTable.putItem(item);
60+
61+
}
62+
63+
public void unregister(String machineId, String requesterId) {
64+
DeleteItemSpec spec = new DeleteItemSpec()
65+
.withPrimaryKey(
66+
"MachineId",
67+
machineId,
68+
"RequesterId",
69+
requesterId);
70+
71+
72+
ddbTable.deleteItem(spec);
73+
}
74+
75+
private static String getGoogleKey() {
76+
if(System.getenv("googlekey-local") != null) {
77+
byte[] encoded = new byte[0];
78+
try {
79+
encoded = Files.readAllBytes(Paths.get(System.getenv("googlekey-local")));
80+
} catch (IOException e) {
81+
e.printStackTrace();
82+
}
83+
return new String(encoded);
84+
}
85+
String secretName = System.getenv("firebase_secret_name");
86+
87+
// Create a Secrets Manager client
88+
AWSSecretsManager client = AWSSecretsManagerClientBuilder.standard()
89+
.build();
90+
91+
String secret;
92+
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest()
93+
.withSecretId(secretName);
94+
GetSecretValueResult getSecretValueResult;
95+
96+
getSecretValueResult = client.getSecretValue(getSecretValueRequest);
97+
98+
secret = getSecretValueResult.getSecretString();
99+
return secret;
100+
}
101+
102+
static boolean fbInitialized = false;
103+
104+
private static void initFirebase() {
105+
if(fbInitialized) return;
106+
107+
FirebaseOptions options;
108+
try {
109+
options = new FirebaseOptions.Builder()
110+
.setCredentials(GoogleCredentials.fromStream(new ByteArrayInputStream( getGoogleKey().getBytes() )))
111+
112+
// .setDatabaseUrl("https://numeric-drummer-119720.firebaseio.com/")
113+
.build();
114+
} catch (IOException e) {
115+
e.printStackTrace();
116+
throw new RuntimeException(e);
117+
}
118+
119+
FirebaseApp.initializeApp(options);
120+
fbInitialized = true;
121+
}
122+
123+
public static void main(String args[]) {
124+
new NotificationManager().handleRequest();
125+
}
126+
127+
public void handleRequest() {
128+
initFirebase();
129+
130+
Map<String, RoomMachineStatus> statuses = new HashMap<>();
131+
for(Item item : ddbTable.scan()) {
132+
try {
133+
String[] pieces = item.getString("MachineId").split("\\|");
134+
String lrName = pieces[0];
135+
String lrId = pieces[1];
136+
String machineId = pieces[2];
137+
RoomMachineStatus stat = statuses.get(lrId);
138+
if (stat == null) {
139+
stat = new LaundryViewEndpoint().machineStatus(lrId);
140+
statuses.put(lrId, stat);
141+
}
142+
String type = "WASHER";
143+
Optional<Machine> machineOpt = stat.getWashers().stream().filter((m) -> m.getId().equals(machineId)).findFirst();
144+
if (!machineOpt.isPresent()) {
145+
type = "DRYER";
146+
machineOpt = stat.getDryers().stream().filter((m) -> m.getId().equals(machineId)).findFirst();
147+
}
148+
if (!machineOpt.isPresent()) continue;
149+
150+
Machine mac = machineOpt.get();
151+
System.out.println(mac.getId() + " " + mac.getStatus());
152+
if (mac.getStatus().equals(Status.AVAILBLE) || mac.getStatus().equals(Status.DONE)) {
153+
Message message = Message.builder()
154+
.setAndroidConfig(AndroidConfig.builder()
155+
.setNotification(AndroidNotification.builder()
156+
.setTitle(type.equals("WASHER") ? "Washing Machine Done" : "Dryer Done")
157+
.setBody(lrName + ": Machine #" + mac.getNumber())
158+
.setIcon("washing_machine")
159+
.setSound("default")
160+
.setColor("#000075")
161+
.build()).build())
162+
// .setToken(item.getString("RequesterId"))
163+
// .build();
164+
//
165+
// FirebaseMessaging.getInstance().send(message);
166+
//
167+
// message = Message.builder()
168+
.putData("completed", item.getString("MachineId"))
169+
.setToken(item.getString("RequesterId"))
170+
.build();
171+
172+
try {
173+
FirebaseMessaging.getInstance().send(message);
174+
} catch (FirebaseMessagingException e) {
175+
e.printStackTrace();
176+
}
177+
ddbTable.deleteItem("RequesterId", item.getString("RequesterId"), "MachineId", item.getString("MachineId"));
178+
}
179+
} catch (Exception e) {
180+
// We want to keep processing the other alerts
181+
e.printStackTrace();
182+
}
183+
}
184+
}
185+
}

js/.babelrc

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)