Skip to content

Commit ee8b19a

Browse files
committed
Merge branch 'master' of github.com:Simon-Initiative/authoring-server into dependabot/maven/org.apache.tika-tika-core-1.22
2 parents 0369ae3 + 950fcb2 commit ee8b19a

43 files changed

Lines changed: 939 additions & 297 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/CI.yml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Java CI
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
- 'SPRINT-*'
8+
pull_request:
9+
branches:
10+
- '*'
11+
jobs:
12+
build:
13+
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v1
18+
- name: Set up JDK
19+
uses: actions/setup-java@v1
20+
with:
21+
java-version: 11.0.x
22+
- name: Copy env settings
23+
run: cp service.example.envs service.envs
24+
- name: Build with Maven
25+
run: mvn clean test

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@
180180
<dependency>
181181
<groupId>mysql</groupId>
182182
<artifactId>mysql-connector-java</artifactId>
183-
<version>8.0.11</version>
183+
<version>8.0.16</version>
184184
</dependency>
185185

186186
<!-- Test -->

service.example.envs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,7 @@ svn_projects=https://example.edu/svn/content/editor/projects/dev-local/
3030
#dataset_db=jdbc:mysql://example.edu:3306?useTimezone=true&serverTimezone=UTC&user=xxxxxxx&password=xxxxxxxxx
3131
dataset_db=jdbc:mysql://example.edu:3306?user=xxxxxxx&password=xxxxxxxx
3232

33+
slack_alert_hook=none
34+
3335
# Java runtime options
3436
JAVA_OPTS=-Xms64m -Xmx512m -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true -Djboss.modules.system.pkgs=org.jboss.byteman -Djava.awt.headless=true -Dxml.catalog.ignoreMissing=true -Dxml.catalog.files=/oli/dtd/catalog.xml -Dxml.catalog.verbosity=1 -Dxml.catalog.prefer=public -Dxml.catalog.staticCatalog=yes -Dxml.catalog.allowPI=yes -Dxml.catalog.className=com.sun.org.apache.xml.internal.resolver.Resolver

src/main/java/edu/cmu/oli/assessment/builders/Assessment2Transform.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ public Element transformToUnified(Element root) {
7575
select.detach();
7676
element.setAttribute(select);
7777
}
78+
Attribute grading = next.getAttribute("grading");
79+
if (grading != null) {
80+
grading.detach();
81+
element.setAttribute(grading);
82+
}
7883
});
7984
}
8085
if (inputs.hasNext()) {
@@ -87,7 +92,7 @@ public Element transformToUnified(Element root) {
8792
}
8893

8994
});
90-
} else if (inputRefs.hasNext()) {
95+
} else if (inputRefs.hasNext() && !next.getName().equalsIgnoreCase("image_hotspot")) {
9196
List<Element> refs = new ArrayList<>();
9297
inputRefs.forEach(element -> refs.add(element));
9398
for (int i = 0; i < refs.size(); i++) {
@@ -100,7 +105,7 @@ public Element transformToUnified(Element root) {
100105
ty.setAttribute(select);
101106
}
102107
}
103-
} else {
108+
} else if (!next.getName().equalsIgnoreCase("image_hotspot")) {
104109
Element input = next.getChild("input");
105110
if (input == null) {
106111
input = new Element(next.getName());
@@ -190,6 +195,14 @@ public Element transformFromUnified(Element root) {
190195
case "image_hotspot":
191196
questionType.forEach(element -> {
192197
element.setName("image_input");
198+
element.getAttributes().forEach(attribute -> {
199+
if (attribute.getName().equalsIgnoreCase("select") ||
200+
attribute.getName().equalsIgnoreCase("grading")) {
201+
attribute.detach();
202+
((Element) element.getParent()).setAttribute(attribute);
203+
}
204+
});
205+
193206
});
194207
break;
195208
default:

src/main/java/edu/cmu/oli/assessment/evaluators/MatcherByType.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
* @author Raphael Gachuhi
4343
*/
4444
public class MatcherByType {
45-
4645
JsonObject interaction;
4746

4847
public MatcherByType(JsonObject interaction) {

src/main/java/edu/cmu/oli/assessment/validators/AssessmentV2Validator.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,10 @@ private JsonObject parseResponseCriteria(Element matchElem, JsonObject q) {
891891
String match = matchElem.getAttributeValue("match");
892892

893893
JsonObject responseCriteria = new JsonObject();
894-
responseCriteria.addProperty("interactionId", i.get("id").getAsString());
894+
// interactionId maybe null for cases where x-oli-inline-assessment is custom dnd activity
895+
if(i != null) {
896+
responseCriteria.addProperty("interactionId", i.get("id").getAsString());
897+
}
895898
responseCriteria.addProperty("match", match);
896899
//:FIXME: find way to validate match criteria
897900
// try {

src/main/java/edu/cmu/oli/content/AppUtils.java

Lines changed: 84 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import edu.cmu.oli.content.models.persistance.entities.ContentPackage;
77
import edu.cmu.oli.content.models.persistance.entities.ErrorLevel;
88
import edu.cmu.oli.content.models.persistance.entities.Resource;
9+
import edu.cmu.oli.content.resource.builders.Xml2Json;
910
import org.apache.tika.Tika;
1011
import org.apache.xml.resolver.tools.CatalogResolver;
1112
import org.jdom2.Attribute;
@@ -22,22 +23,20 @@
2223
import javax.json.JsonValue;
2324
import javax.naming.InitialContext;
2425
import javax.naming.NamingException;
25-
26+
import javax.ws.rs.client.ClientBuilder;
27+
import javax.ws.rs.client.Entity;
28+
import javax.ws.rs.client.WebTarget;
29+
import javax.ws.rs.core.MediaType;
30+
import javax.ws.rs.core.Response;
2631
import java.io.*;
2732
import java.nio.file.Files;
2833
import java.nio.file.Path;
2934
import java.security.MessageDigest;
3035
import java.security.NoSuchAlgorithmException;
3136
import java.security.SecureRandom;
32-
import java.util.ArrayList;
33-
import java.util.HashMap;
34-
import java.util.List;
35-
import java.util.Map;
36-
import java.util.Optional;
37-
import java.util.Random;
38-
import java.util.UUID;
37+
import java.util.*;
38+
import java.util.concurrent.ConcurrentHashMap;
3939
import java.util.stream.Collectors;
40-
import edu.cmu.oli.content.resource.builders.Xml2Json;
4140

4241
/**
4342
* @author Raphael Gachuhi
@@ -92,7 +91,7 @@ public static String inputStreamToString(InputStream input) throws IOException {
9291
}
9392

9493
public static void addToPackageError(ContentPackage contentPackage, String message, String source,
95-
ErrorLevel level) {
94+
ErrorLevel level) {
9695
if (contentPackage.getErrors() == null) {
9796
JsonObject errors = new JsonObject();
9897
errors.addProperty("contentPackageErrors", contentPackage.getId() + "_" + contentPackage.getVersion());
@@ -129,13 +128,13 @@ public static void addToResourceError(Resource resource, String message, String
129128

130129
private static boolean isQuestionWithParts(Element element) {
131130
if (element.getName().equals("multiple_choice")
132-
|| element.getName().equals("ordering")
133-
|| element.getName().equals("short_answer")
134-
|| element.getName().equals("essay")
135-
|| element.getName().equals("numeric")
136-
|| element.getName().equals("text")
137-
|| element.getName().equals("fill_in_the_blank")
138-
|| element.getName().equals("question")) {
131+
|| element.getName().equals("ordering")
132+
|| element.getName().equals("short_answer")
133+
|| element.getName().equals("essay")
134+
|| element.getName().equals("numeric")
135+
|| element.getName().equals("text")
136+
|| element.getName().equals("fill_in_the_blank")
137+
|| element.getName().equals("question")) {
139138
return true;
140139
}
141140

@@ -169,8 +168,8 @@ private static String getQuestionLabel(Element question) {
169168

170169
switch (question.getName()) {
171170
case "multiple_choice":
172-
if (question.getAttribute("select") != null
173-
&& question.getAttribute("select").getValue().equals("single")) {
171+
if (question.getAttribute("select") != null
172+
&& question.getAttribute("select").getValue().equals("single")) {
174173
return "Multiple Choice";
175174
}
176175
return "Check All That Apply";
@@ -219,16 +218,16 @@ public static int countPoolQuestions(Element element) {
219218
if (element.getName().equals("pool")) {
220219
for (Element c : element.getChildren()) {
221220
switch (c.getName()) {
222-
case "multiple_choice":
223-
case "ordering":
224-
case "short_answer":
225-
case "essay":
226-
case "numeric":
227-
case "text":
228-
case "fill_in_the_blank":
229-
case "question":
230-
count = count + 1;
231-
default:
221+
case "multiple_choice":
222+
case "ordering":
223+
case "short_answer":
224+
case "essay":
225+
case "numeric":
226+
case "text":
227+
case "fill_in_the_blank":
228+
case "question":
229+
count = count + 1;
230+
default:
232231
}
233232
}
234233
}
@@ -368,29 +367,26 @@ public enum EmbedActivityType {
368367
UNKNOWN("UNKNOWN");
369368

370369
private String type;
371-
370+
372371
EmbedActivityType(String type) {
373372
this.type = type;
374373
}
375-
374+
376375
public String getAsString() {
377376
return type;
378377
}
379-
378+
380379
private static final Map<String, EmbedActivityType> reverseLookup = new HashMap<>();
381-
380+
382381
// Populate the reverse lookup table on loading time
383-
static
384-
{
385-
for(EmbedActivityType activityType : EmbedActivityType.values())
386-
{
382+
static {
383+
for (EmbedActivityType activityType : EmbedActivityType.values()) {
387384
reverseLookup.put(activityType.getAsString(), activityType);
388385
}
389386
}
390-
387+
391388
//This method can be used for reverse lookup purpose
392-
public static EmbedActivityType fromString(String type)
393-
{
389+
public static EmbedActivityType fromString(String type) {
394390
return reverseLookup.get(type);
395391
}
396392
}
@@ -399,7 +395,7 @@ public static EmbedActivityType inferEmbedActivityType(JsonObject embedActivity)
399395
// use activity_type property or infer type based on content
400396
if (embedActivity.has("@activity_type")) {
401397
// use activity_type attribute to determine type
402-
switch(embedActivity.get("@activity_type").getAsString().toLowerCase()) {
398+
switch (embedActivity.get("@activity_type").getAsString().toLowerCase()) {
403399
case "repl":
404400
return EmbedActivityType.REPL;
405401
default:
@@ -424,7 +420,7 @@ public static EmbedActivityType inferEmbedActivityType(JsonObject embedActivity)
424420
String source = itemObj.get("source").getAsJsonObject().get("#text").getAsString();
425421
if (source.endsWith("activity.js") || source.endsWith("repl.js")) {
426422
flags = flags | 0b1;
427-
423+
428424
if (flags == REPL_FLAGS) {
429425
return EmbedActivityType.REPL;
430426
}
@@ -467,7 +463,52 @@ public static EmbedActivityType inferEmbedActivityType(JsonObject embedActivity)
467463
}
468464

469465
}
470-
466+
471467
return EmbedActivityType.UNKNOWN;
472468
}
469+
470+
private static Map<String, Long> delayedSlackMessages;
471+
static {
472+
delayedSlackMessages = new ConcurrentHashMap<>();
473+
long sleepTimeInMilli = 1000L * 60 * 1; // 1 minutes
474+
Timer timer = new Timer(true);
475+
TimerTask timerTask = new TimerTask() {
476+
@Override
477+
public void run() {
478+
delayedSlackMessagesClear();
479+
}
480+
};
481+
timer.scheduleAtFixedRate(timerTask, sleepTimeInMilli, sleepTimeInMilli);
482+
}
483+
484+
private static void delayedSlackMessagesClear(){
485+
Iterator<Map.Entry<String, Long>> it = delayedSlackMessages.entrySet().iterator();
486+
while (it.hasNext()){
487+
Map.Entry<String, Long> next = it.next();
488+
// Remove entry after 15 minutes
489+
if((System.currentTimeMillis() - next.getValue()) > (1000L * 60 * 15)){
490+
delayedSlackMessages.remove(next.getKey());
491+
}
492+
}
493+
}
494+
495+
public static Response.Status sendSlackAlert(JsonObject message) {
496+
String slackHook = System.getenv().get("slack_alert_hook");
497+
if (slackHook == null || slackHook.isEmpty() || slackHook.equalsIgnoreCase("none")) {
498+
return Response.Status.FORBIDDEN;
499+
}
500+
String messageString = AppUtils.gsonBuilder().create().toJson(message);
501+
if(delayedSlackMessages.containsKey(messageString)){
502+
// Same message already dispatched less than 15 minutes ago; ignore this one
503+
return Response.Status.OK;
504+
}
505+
506+
delayedSlackMessages.put(messageString, System.currentTimeMillis());
507+
WebTarget target = ClientBuilder.newClient().target(slackHook);
508+
Response response = target.request(MediaType.APPLICATION_JSON)
509+
.post(Entity.json(messageString));
510+
log.info("response code " + response.getStatusInfo() + " code " + response.getStatus());
511+
512+
return Response.Status.fromStatusCode(response.getStatus());
513+
}
473514
}

src/main/java/edu/cmu/oli/content/boundary/endpoints/AnalyticsResource.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package edu.cmu.oli.content.boundary.endpoints;
22

3-
import com.airhacks.porcupine.execution.boundary.Dedicated;
3+
import edu.cmu.oli.content.configuration.DedicatedExecutor;
44
import com.google.gson.Gson;
55
import com.google.gson.JsonElement;
66
import edu.cmu.oli.content.AppUtils;
@@ -45,7 +45,7 @@ public class AnalyticsResource {
4545
AppSecurityContextFactory appSecurityContextFactory;
4646

4747
@Inject
48-
@Dedicated("AnalyticsResourceApiExecutor")
48+
@DedicatedExecutor("AnalyticsResourceApiExecutor")
4949
ExecutorService executor;
5050

5151
@Context

src/main/java/edu/cmu/oli/content/boundary/endpoints/ContentPackageResource.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package edu.cmu.oli.content.boundary.endpoints;
22

3-
import com.airhacks.porcupine.execution.boundary.Dedicated;
3+
import edu.cmu.oli.content.configuration.DedicatedExecutor;
44
import com.google.gson.Gson;
55
import com.google.gson.JsonElement;
66
import com.google.gson.JsonParser;
@@ -61,7 +61,7 @@ public class ContentPackageResource {
6161
Instance<Configurations> configuration;
6262

6363
@Inject
64-
@Dedicated("packagesResourceApiExecutor")
64+
@DedicatedExecutor("packagesResourceApiExecutor")
6565
ExecutorService executor;
6666

6767
@Context

src/main/java/edu/cmu/oli/content/boundary/endpoints/ContentResource.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package edu.cmu.oli.content.boundary.endpoints;
22

3-
import com.airhacks.porcupine.execution.boundary.Dedicated;
3+
import edu.cmu.oli.content.configuration.DedicatedExecutor;
44
import com.google.gson.Gson;
55
import com.google.gson.JsonElement;
66
import com.google.gson.JsonParser;
@@ -47,7 +47,7 @@ public class ContentResource {
4747
private ContentResourceManager pm;
4848

4949
@Inject
50-
@Dedicated("resourcesApiExecutor")
50+
@DedicatedExecutor("resourcesApiExecutor")
5151
ExecutorService mes;
5252

5353
@Inject

0 commit comments

Comments
 (0)