existingMutexes = this.getCollection().find(eq("mutexId", mutexId)).into(new ArrayList<>());
+
+ log.info("Existent mutexes: {}", existingMutexes);
+
+ if (existingMutexes.isEmpty()) {
+ // create it assuming no collision at this point
+ InstanceMutex newMutex = new InstanceMutex(mutexId);
+ log.info("Creating new mutex {}", newMutex);
+
+ UpdateResult result = this.getCollection().updateOne(
+ eq("mutexId", mutexId),
+ Updates.set("mutexId", mutexId),
+ new UpdateOptions().upsert(true)
+ );
+
+ if (result.getUpsertedId() != null) {
+ log.info("Mutex {} created.", mutexId);
+ } else {
+ log.info("Mutex {} was added in another thread before we could get to it.", mutexId);
+ }
+ } else if (existingMutexes.size() > 1) {
+ // problem
+ log.error("multiple mutex for id {}", mutexId);
+ this.clearDuplicateMutexes(mutexId);
+ }
+ }
+
+ /**
+ * Locks the mutex with the given id.
+ *
+ * Non blocking.
+ *
+ * Call {@link #register(String)} before using this method.
+ *
+ * @param mutexId The is of the mutex to get the lock for
+ * @param additionalIdentity If an additional identity is required. Only use if need the mutex to offer concurrency within the same instance of the app.
+ * @return true when reserved, false when not.
+ */
+ public boolean lock(@NonNull String mutexId, Optional additionalIdentity) {
+ String identity = this.getIdentity(additionalIdentity);
+ Bson mutexIdEquals = eq("mutexId", mutexId);
+
+ //ensure only one mutex object
+ this.clearDuplicateMutexes(mutexId);
+
+ // try an update
+ InstanceMutex old = this.getCollection().findOneAndUpdate(
+ and(
+ mutexIdEquals,
+ or(
+ not(exists("taken")),
+ eq("taken", false)
+ )
+
+ ),
+ Updates.combine(
+ Updates.set("taken", true),
+ Updates.set("takenAt", ZonedDateTime.now()),
+ Updates.set("takenBy", identity)
+ )
+ );
+
+ if (old != null) {
+ // success ...
+ // Update the information
+ log.debug("Got lock for {} on mutex (showing old data) {}", identity, old);
+ log.info("Acquired lock for {} on mutex {}", identity, mutexId);
+ return true;
+ } else {
+ log.info("Failed to reserve Mutex {} for {}", mutexId, identity);
+
+ InstanceMutex lockedMutex = this.getCollection().find(mutexIdEquals).first();
+ log.debug("Locked mutex: {}", lockedMutex);
+
+ if (lockedMutex == null) {
+ log.warn("No mutex found. It needs to be registered first.");
+ } else if (lockedMutex.getTakenAt() != null && ZonedDateTime.now().isAfter(lockedMutex.getTakenAt().plus(this.lockExpire))) {
+ this.getCollection().findOneAndUpdate(mutexIdEquals, Updates.set("taken", false));
+ log.warn("Unlocked mutex that appeared deadlocked: {}", mutexId);
+ } else {
+ log.debug("Was locked. returning.");
+ }
+
+ return false;
+ }
+ }
+
+ public boolean lock(@NonNull String mutexId) {
+ return this.lock(mutexId, Optional.empty());
+ }
+
+ /**
+ * Free a mutex previously reserved.
+ *
+ * @param mutexId The id of the mutex to free
+ */
+ public void free(@NonNull String mutexId, Optional additionalIdentity) {
+ String identity = this.getIdentity(additionalIdentity);
+
+ InstanceMutex mutex = this.getCollection().findOneAndUpdate(
+ and(
+ eq("mutexId", mutexId),
+ eq("taken", true),
+ eq("takenBy", identity)
+ ),
+ Updates.combine(
+ Updates.set("taken", false),
+ Updates.set("takenAt", null),
+ Updates.set("takenBy", null)
+ )
+ );
+
+ if (mutex == null) {
+ log.info("Mutex NOT freed. Either not taken or not taken by this identity. Mutex: {}", mutex);
+ } else {
+ log.info("Mutex FREED: {}", mutexId);
+ }
+ }
+
+ public void free(@NonNull String mutexId) {
+ this.free(mutexId, Optional.empty());
+ }
+}
diff --git a/software/oqm-core-api/src/main/java/tech/ebp/oqm/core/api/service/notification/EventNotificationWrapper.java b/software/oqm-core-api/src/main/java/tech/ebp/oqm/core/api/service/notification/EventNotificationWrapper.java
index 55b22231e..4f3249942 100644
--- a/software/oqm-core-api/src/main/java/tech/ebp/oqm/core/api/service/notification/EventNotificationWrapper.java
+++ b/software/oqm-core-api/src/main/java/tech/ebp/oqm/core/api/service/notification/EventNotificationWrapper.java
@@ -1,14 +1,12 @@
package tech.ebp.oqm.core.api.service.notification;
-import lombok.AccessLevel;
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.Setter;
+import lombok.*;
import org.bson.types.ObjectId;
import tech.ebp.oqm.core.api.model.object.history.ObjectHistoryEvent;
@Data
@AllArgsConstructor
+@NoArgsConstructor
@Setter(AccessLevel.PRIVATE)
public class EventNotificationWrapper {
private ObjectId database;
diff --git a/software/oqm-core-api/src/main/java/tech/ebp/oqm/core/api/service/notification/HistoryEventNotificationService.java b/software/oqm-core-api/src/main/java/tech/ebp/oqm/core/api/service/notification/HistoryEventNotificationService.java
index 7f392e74f..cf40dbdaf 100644
--- a/software/oqm-core-api/src/main/java/tech/ebp/oqm/core/api/service/notification/HistoryEventNotificationService.java
+++ b/software/oqm-core-api/src/main/java/tech/ebp/oqm/core/api/service/notification/HistoryEventNotificationService.java
@@ -6,6 +6,8 @@
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import lombok.extern.slf4j.Slf4j;
+import org.apache.kafka.common.header.Headers;
+import org.apache.kafka.common.header.internals.RecordHeaders;
import org.bson.types.ObjectId;
import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.reactive.messaging.Channel;
@@ -22,78 +24,98 @@
@Slf4j
@ApplicationScoped
public class HistoryEventNotificationService {
-
+
public static final String INTERNAL_EVENT_CHANNEL = "events-internal";
public static final String OUTGOING_EVENT_CHANNEL = "events-outgoing";
- public static final String ALL_EVENT_TOPIC = "all-events";
-
+ public static final String TOPIC_PREPEND = "oqm-core-";
+ public static final String ALL_EVENT_TOPIC_LABEL = "all-events";
+ public static final String ALL_EVENT_TOPIC = TOPIC_PREPEND + ALL_EVENT_TOPIC_LABEL;
+
@ConfigProperty(name = "mp.messaging.outgoing.events-outgoing.bootstrap.servers")
Optional outgoingServers;
@ConfigProperty(name = "kafka.bootstrap.servers")
Optional kafkaServers;
-
+
@Inject
@Broadcast
@Channel(INTERNAL_EVENT_CHANNEL)
- @OnOverflow(value = OnOverflow.Strategy.DROP)//TODO:: this better https://quarkus.io/version/3.2/guides/kafka#sending-messages-with-emitter
+ @OnOverflow(value = OnOverflow.Strategy.DROP)
Emitter internalEventEmitter;
-
+
+
@Inject
@Broadcast
@Channel(OUTGOING_EVENT_CHANNEL)
@OnOverflow(value = OnOverflow.Strategy.DROP)
- Emitter outgoingEventEmitter;
-
- private boolean haveOutgoingServers(){
+ Emitter outgoingEventEmitter;
+
+ private boolean haveOutgoingServers() {
return outgoingServers.isPresent() || kafkaServers.isPresent();
}
-
+
/**
* Don't call this directly, use the other one
*/
@WithSpan
@Incoming(INTERNAL_EVENT_CHANNEL)
void sendEventOutgoing(EventNotificationWrapper notificationWrapper) {
- if(!this.haveOutgoingServers()){
+ if (!this.haveOutgoingServers()) {
log.info("NOT Sending event to external channels (no outgoing servers configured): {}/{}", notificationWrapper.getClass().getSimpleName(),
notificationWrapper.getEvent().getId());
return;
}
log.info("Sending event to external channels: {}/{}", notificationWrapper.getClass().getSimpleName(), notificationWrapper.getEvent().getId());
try {
+ Headers headers = new RecordHeaders()
+ .add("database", notificationWrapper.getDatabase().toHexString().getBytes())
+ .add("object", notificationWrapper.getObjectName().getBytes());
this.outgoingEventEmitter.send(
- Message.of(
- notificationWrapper.getEvent()
- ).addMetadata(
- OutgoingKafkaRecordMetadata.builder()
- .withTopic(
- (notificationWrapper.getDatabase() == null? "" : notificationWrapper.getDatabase().toHexString() + "-") + notificationWrapper.getObjectName() + "-" + notificationWrapper.getEvent().getType()
- )
- .build()
- ));
- this.outgoingEventEmitter.send(
- Message.of(
- notificationWrapper.getEvent()
- ).addMetadata(
- OutgoingKafkaRecordMetadata.builder()
- .withTopic((notificationWrapper.getDatabase() == null? "" : notificationWrapper.getDatabase().toHexString() + "-") + ALL_EVENT_TOPIC)
- .build()
- ));
+ Message.of(notificationWrapper)
+ .addMetadata(
+ OutgoingKafkaRecordMetadata.builder()
+ .withTopic(ALL_EVENT_TOPIC)
+ .withHeaders(headers)
+ .build()
+ ));
+ //TODO:: maybe support in future
+// this.outgoingEventEmitter.send(
+// Message.of(
+// notificationWrapper
+// ).addMetadata(
+// OutgoingKafkaRecordMetadata.builder()
+// .withTopic(
+// TOPIC_PREPEND + (notificationWrapper.getDatabase() == null ? "" : notificationWrapper.getDatabase().toHexString() + "-") + ALL_EVENT_TOPIC_LABEL
+// )
+// .withHeaders(headers)
+// .build()
+// ));
+
+
+ //TODO:: maybe support this in future
+// this.outgoingEventEmitter.send(
+// Message.of(notificationWrapper.getEvent()).addMetadata(
+// OutgoingKafkaRecordMetadata.builder()
+// .withTopic(
+// TOPIC_PREPEND + (notificationWrapper.getDatabase() == null ? "" : notificationWrapper.getDatabase().toHexString() + "-") + notificationWrapper.getObjectName() + "-" + notificationWrapper.getEvent().getType()
+// )
+// .withHeaders(headers)
+// .build()
+// ));
log.debug("Sent event to external channels: {}/{}", notificationWrapper.getClass().getSimpleName(), notificationWrapper.getEvent().getId());
- } catch(Throwable e) {
+ } catch (Throwable e) {
log.error("FAILED to send event to external channels: {}/{}:", notificationWrapper.getClass().getSimpleName(), notificationWrapper.getEvent().getId(), e);
throw e;
}
}
-
+
public void sendEvent(ObjectId oqmDatabase, Class> objectClass, ObjectHistoryEvent event) {
this.sendEvents(oqmDatabase, objectClass, event);
}
-
+
public void sendEvents(ObjectId oqmDatabase, Class> objectClass, ObjectHistoryEvent... events) {
this.sendEvents(oqmDatabase, objectClass, Arrays.asList(events));
}
-
+
public void sendEvents(ObjectId oqmDatabase, Class> objectClass, Collection events) {
for (ObjectHistoryEvent event : events) {
log.info("Sending event to internal channel: {}/{}", objectClass.getSimpleName(), event.getId());
@@ -103,5 +125,5 @@ public void sendEvents(ObjectId oqmDatabase, Class> objectClass, Collection getParams() {
+ return Stream.of(
+ Arguments.of(2, 10, Duration.of(250, ChronoUnit.MILLIS)),
+ Arguments.of(3, 10, Duration.of(250, ChronoUnit.MILLIS)),
+ Arguments.of(5, 10, Duration.of(250, ChronoUnit.MILLIS)),
+ Arguments.of(10, 10, Duration.of(250, ChronoUnit.MILLIS)),
+ Arguments.of(20, 20, Duration.of(150, ChronoUnit.MILLIS))
+ );
+ }
+
+ @Inject
+ InstanceMutexService instanceMutexService;
+
+ @Test
+ public void basicTest() {
+ String mutexId = "testMutex";
+ this.instanceMutexService.register(mutexId);
+
+ assertTrue(this.instanceMutexService.lock(mutexId));
+
+ assertFalse(this.instanceMutexService.lock(mutexId));
+
+ this.instanceMutexService.free(mutexId);
+
+ assertTrue(this.instanceMutexService.lock(mutexId));
+ this.instanceMutexService.free(mutexId);
+ }
+
+
+ @ParameterizedTest
+ @MethodSource("getParams")
+ public void threadTest(int numThreads, int numIterations, Duration workDuration) throws InterruptedException, ExecutionException {
+ String mutexId = "testMutex2";
+ List>> futures = new ArrayList<>(numThreads);
+ SortedSet results = new TreeSet<>();
+ ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
+
+ TestThread.Builder threadBuilder = TestThread.builder()
+ .mutexId(mutexId)
+ .numIterations(numIterations)
+ .durationOfWork(workDuration)
+ .instanceMutexService(instanceMutexService);
+
+ for (int i = 1; i <= numThreads; i++) {
+ threadBuilder.threadId("testThread-" + i);
+
+ futures.add(executor.submit(threadBuilder.build()));
+ }
+ executor.shutdown();
+ while (!executor.awaitTermination(1, TimeUnit.MINUTES)) {
+ log.info("Still waiting on threads...");
+ }
+
+ for (Future> future : futures) {
+ results.addAll(future.get());
+ }
+
+ assertEquals(numIterations * numThreads, results.size());
+
+ //TODO:: check results
+ log.info("Results: {}", results);
+
+ Iterator iterator = results.iterator();
+ ThreadResult cur = iterator.next();
+ while (iterator.hasNext()) {
+ ThreadResult next = iterator.next();
+
+ assertTrue(
+ next.getStart().isAfter(cur.getStart()),
+ "result " + cur + " start overlaps with the next result " + next + " (next start is before cur start)"
+ );
+ assertTrue(
+ (next.getStart().isAfter(cur.getEnd()) || next.getStart().equals(cur.getEnd())),
+ "result " + cur + " overlaps with the next result " + next + " (next start is before cur end)"
+ );
+
+ cur = next;
+ }
+
+ }
+
+ @Builder
+ @Data
+ @AllArgsConstructor
+ static
+ class ThreadResult implements Comparable {
+ private String threadId;
+ private LocalDateTime start;
+ private LocalDateTime end;
+
+ @Override
+ public int compareTo(@NonNull InstanceMutexServiceTest.ThreadResult threadResult) {
+ return this.getStart().compareTo(threadResult.getStart());
+ }
+ }
+
+ @Builder
+ @Slf4j
+ @AllArgsConstructor
+ static class TestThread implements Callable> {
+
+ private String mutexId;
+ private String threadId;
+ private InstanceMutexService instanceMutexService;
+ private int numIterations;
+ private Duration durationOfWork;
+
+ @SneakyThrows
+ @Override
+ public List call() {
+ log.info("Running test thread {}", this.threadId);
+
+ this.instanceMutexService.register(this.mutexId);
+
+// Thread.sleep(500);
+
+ List results = new ArrayList<>(this.numIterations);
+ for (int i = 1; i <= this.numIterations; i++) {
+ log.info("Thread {} waiting for lock on iteration {}", this.threadId, i);
+ while (!instanceMutexService.lock(this.mutexId, Optional.of(this.threadId))) {
+ Thread.sleep(50);
+ }
+ log.info("Thread {} got lock on iteration {}/{}", this.threadId, i, this.numIterations);
+ ThreadResult.Builder resultBuilder = ThreadResult.builder()
+ .threadId(this.threadId)
+ .start(LocalDateTime.now());
+
+ Thread.sleep(this.durationOfWork);
+
+ resultBuilder.end(LocalDateTime.now());
+
+ this.instanceMutexService.free(this.mutexId, Optional.of(this.threadId));
+ log.info("Thread {} done doing work & released lock on iteration {}", this.threadId, i);
+ results.add(resultBuilder.build());
+ }
+ log.info("DONE running test thread {}", this.threadId);
+ return results;
+ }
+ }
+
+}
diff --git a/software/oqm-core-api/src/test/java/tech/ebp/oqm/core/api/service/mongo/MongoHistoriedObjectServiceTest.java b/software/oqm-core-api/src/test/java/tech/ebp/oqm/core/api/service/mongo/MongoHistoriedObjectServiceTest.java
index 3d6c83d69..60f4ce8d3 100644
--- a/software/oqm-core-api/src/test/java/tech/ebp/oqm/core/api/service/mongo/MongoHistoriedObjectServiceTest.java
+++ b/software/oqm-core-api/src/test/java/tech/ebp/oqm/core/api/service/mongo/MongoHistoriedObjectServiceTest.java
@@ -11,12 +11,11 @@
import org.bson.types.ObjectId;
import org.junit.jupiter.api.Test;
import tech.ebp.oqm.core.api.model.object.ObjectUtils;
-import tech.ebp.oqm.core.api.service.mongo.InteractingEntityService;
+import tech.ebp.oqm.core.api.service.notification.EventNotificationWrapper;
import tech.ebp.oqm.core.api.service.notification.HistoryEventNotificationService;
import tech.ebp.oqm.core.api.service.serviceState.db.OqmDatabaseService;
import tech.ebp.oqm.core.api.testResources.data.TestMainObject;
import tech.ebp.oqm.core.api.testResources.data.TestMongoHistoriedService;
-import tech.ebp.oqm.core.api.testResources.data.TestUserService;
import tech.ebp.oqm.core.api.testResources.lifecycleManagers.TestResourceLifecycleManager;
import tech.ebp.oqm.core.api.testResources.testClasses.RunningServerTest;
import tech.ebp.oqm.core.api.model.object.history.ObjectHistoryEvent;
@@ -73,24 +72,28 @@ public void testAdd() throws JsonProcessingException {
assertEquals(objectId, createEvent.getObjectId());
assertNotNull(createEvent.getEntity());
assertEquals(testUser.getId(), createEvent.getEntity());
-
+
+
ConsumerTask createFromAll = this.kafkaCompanion.consumeStrings().fromTopics(
- this.oqmDatabaseService.getDatabaseCache().getFromName(DEFAULT_TEST_DB_NAME).get().getDbId().toHexString() + "-" + HistoryEventNotificationService.ALL_EVENT_TOPIC,
+ HistoryEventNotificationService.ALL_EVENT_TOPIC,
1
);
createFromAll.awaitCompletion();
assertEquals(1, createFromAll.count());
- CreateEvent createEventFromMessage = ObjectUtils.OBJECT_MAPPER.readValue(createFromAll.getFirstRecord().value(), CreateEvent.class);
- assertEquals(createEvent, createEventFromMessage);
-
- ConsumerTask createFromCreate = this.kafkaCompanion.consumeStrings().fromTopics(
- this.oqmDatabaseService.getDatabaseCache().getFromName(DEFAULT_TEST_DB_NAME).get().getDbId().toHexString() + "-" + HistoryEventNotificationService.ALL_EVENT_TOPIC,
- 1
- );
- createFromCreate.awaitCompletion();
- assertEquals(1, createFromCreate.count());
- createEventFromMessage = ObjectUtils.OBJECT_MAPPER.readValue(createFromCreate.getFirstRecord().value(), CreateEvent.class);
- assertEquals(createEvent, createEventFromMessage);
+ EventNotificationWrapper createEventFromMessage = ObjectUtils.OBJECT_MAPPER.readValue(createFromAll.getFirstRecord().value(), EventNotificationWrapper.class);
+ assertEquals(createEvent, createEventFromMessage.getEvent());
+
+ // TODO: more when we want to
+// ConsumerTask createFromAllInDb = this.kafkaCompanion.consumeStrings().fromTopics(
+// HistoryEventNotificationService.TOPIC_PREPEND + this.oqmDatabaseService.getDatabaseCache().getFromName(DEFAULT_TEST_DB_NAME).get().getDbId().toHexString() + "-" + HistoryEventNotificationService.ALL_EVENT_TOPIC_LABEL,
+// 1
+// );
+// createFromAllInDb.awaitCompletion();
+// assertEquals(1, createFromAllInDb.count());
+// createEventFromMessage = ObjectUtils.OBJECT_MAPPER.readValue(createFromAllInDb.getFirstRecord().value(), EventNotificationWrapper.class);
+// assertEquals(createEvent, createEventFromMessage.getEvent());
+
+ //TODO:: cover last type?
}
//TODO:: test rest
}
\ No newline at end of file
diff --git a/software/oqm-core-base-station/build.gradle b/software/oqm-core-base-station/build.gradle
index fe7b7505e..57c139439 100644
--- a/software/oqm-core-base-station/build.gradle
+++ b/software/oqm-core-base-station/build.gradle
@@ -1,11 +1,11 @@
plugins {
id 'java'
id 'io.quarkus'
- id "io.freefair.lombok" version "8.6"
+ id "io.freefair.lombok" version "8.10.2"
}
group 'com.ebp.openQuarterMaster'
-version '1.4.2'
+version '1.4.5'
repositories {
mavenCentral()
@@ -28,13 +28,13 @@ dependencies {
implementation 'io.quarkus:quarkus-opentelemetry'
implementation 'io.quarkus:quarkus-scheduler'
- implementation 'tech.ebp.oqm.lib:core-api-lib-quarkus:2.1.0-SNAPSHOT'
+ implementation 'tech.ebp.oqm.lib:core-api-lib-quarkus:2.2.0-SNAPSHOT'
implementation 'org.apache.commons:commons-io:1.3.2'
implementation 'org.apache.commons:commons-text:1.12.0'
implementation group: 'org.jsoup', name: 'jsoup', version: '1.18.1'
- implementation 'uk.org.okapibarcode:okapibarcode:0.4.6'
+ implementation 'uk.org.okapibarcode:okapibarcode:0.4.8'
implementation 'com.itextpdf:html2pdf:5.0.5'
//webjars
@@ -42,12 +42,12 @@ dependencies {
implementation 'org.webjars:bootstrap:5.3.3'
testImplementation 'io.quarkus:quarkus-junit5'
- testImplementation "org.junit.jupiter:junit-jupiter-params:5.10.3"
+ testImplementation "org.junit.jupiter:junit-jupiter-params:5.11.3"
testImplementation 'io.rest-assured:rest-assured'
- testImplementation 'net.datafaker:datafaker:2.3.1'
+ testImplementation 'net.datafaker:datafaker:2.4.1'
- testImplementation 'com.microsoft.playwright:playwright:1.45.1'
- testImplementation 'com.deque.html.axe-core:playwright:4.9.1'
+ testImplementation 'com.microsoft.playwright:playwright:1.48.0'
+ testImplementation 'com.deque.html.axe-core:playwright:4.10.0'
}
java {
diff --git a/software/oqm-core-base-station/docs/Releasing.md b/software/oqm-core-base-station/docs/Releasing.md
index 4596702c3..1a881fe3f 100644
--- a/software/oqm-core-base-station/docs/Releasing.md
+++ b/software/oqm-core-base-station/docs/Releasing.md
@@ -31,7 +31,7 @@ These are the steps to take to perform a release of the software:
4. Ensure everything committed and pushed to github. Check workflows.
5. Be logged into docker hub with ebprod user `docker login`
6. Deploy jvm version
- 1. Clean/build/push project `./gradlew clean build -Pquarkus.container-image.build=true -Pquarkus.jib.platforms=linux/arm64,linux/amd64 -Pquarkus.container-image.group=ebprod -Pquarkus.container-image.name=oqm-core-base_station -Pquarkus.container-image.push=true`
+ 1. Clean/build/push project `./gradlew clean build -Pquarkus.container-image.build=true -Pquarkus.jib.platforms=linux/arm64,linux/amd64 -Pquarkus.container-image.push=true`
2. Ensure was deployed successfully: https://hub.docker.com/repository/registry-1.docker.io/ebprod/open-qm-base-station/tags?page=1&ordering=last_updated
7. Make installers: `./makeInstallers.sh`
8. Make release for version on Github, attach all installers to it (`build/installers`)
\ No newline at end of file
diff --git a/software/oqm-core-base-station/installerSrc/installerProperties.json b/software/oqm-core-base-station/installerSrc/installerProperties.json
index e72df4206..14e88f75a 100644
--- a/software/oqm-core-base-station/installerSrc/installerProperties.json
+++ b/software/oqm-core-base-station/installerSrc/installerProperties.json
@@ -6,7 +6,7 @@
"description": "Base Station instance for Open QuarterMaster.",
"homepage": "https://github.com/Epic-Breakfast-Productions/OpenQuarterMaster/tree/main/software/open-qm-base-station",
"dependencies": {
- "deb": "curl, jq, docker.io, docker, oqm-manager-station+captain (>= 2.0.0), oqm-infra-jaeger (>= 1.1.0), oqm-infra-keycloak (>= 1.1.0), oqm-core-depot, oqm-core-api (>= 2.1.0)",
+ "deb": "curl, jq, docker.io, oqm-manager-station+captain (>= 2.0.0), oqm-infra-jaeger (>= 1.1.0), oqm-infra-keycloak (>= 1.1.0), oqm-core-depot, oqm-core-api (>= 2.1.0)",
"debRec": "oqm-plugin-ext_item_search"
},
"copyright": {
diff --git a/software/oqm-core-base-station/installerSrc/oqm-core-base_station.service b/software/oqm-core-base-station/installerSrc/oqm-core-base_station.service
index dd6f8953e..b72e51fec 100644
--- a/software/oqm-core-base-station/installerSrc/oqm-core-base_station.service
+++ b/software/oqm-core-base-station/installerSrc/oqm-core-base_station.service
@@ -17,7 +17,7 @@ Requires=oqm-infra-keycloak.service
[Service]
Type=simple
Restart=always
-TimeoutSec=5m
+TimeoutSec=10m
# Setup operating variables
Environment="CONTAINER_NAME=oqm-core-base_station"
Environment="IMAGE_NAME=ebprod/oqm-core-base_station"
diff --git a/software/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/main.js b/software/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/main.js
index 7eff615ef..fd2f731dd 100644
--- a/software/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/main.js
+++ b/software/oqm-core-base-station/src/main/resources/META-INF/resources/res/js/main.js
@@ -2,14 +2,10 @@ const navSearchInput = $('#navSearchInput');
const navSearchForm = $('#navSearchForm');
const navSearchTypeSelect = $('#navSearchTypeSelect');
-navSearchTypeSelect.on("change", function(event){
- console.log(
- "Changing nav form to " +
- "action: " + navSearchTypeSelect[0].options[event.target.selectedIndex].dataset.action +
- " with fieldName: " + navSearchTypeSelect[0].options[event.target.selectedIndex].dataset.field
- );
- navSearchForm.attr("action", navSearchTypeSelect[0].options[event.target.selectedIndex].dataset.action);
- navSearchInput.attr("name", navSearchTypeSelect[0].options[event.target.selectedIndex].dataset.field);
-});
+function updateNavSearchDestination(action, icon, fieldName){
+ navSearchForm.attr("action", action);
+ navSearchTypeSelect.html(icon);
+ navSearchInput.attr("name", fieldName);
+}
TimeHelpers.setupDateTimeInputs();
\ No newline at end of file
diff --git a/software/oqm-core-base-station/src/main/resources/application.yml b/software/oqm-core-base-station/src/main/resources/application.yml
index 25f426803..c4dd9f43f 100644
--- a/software/oqm-core-base-station/src/main/resources/application.yml
+++ b/software/oqm-core-base-station/src/main/resources/application.yml
@@ -68,6 +68,9 @@ ui:
quarkus:
application:
name: ${service.nameShort} - V${service.apiVersion}
+ container-image:
+ name: oqm-core-base_station
+ group: ebprod
rest-client:
logging:
scope: all
@@ -123,7 +126,6 @@ quarkus:
post-logout-path: /?message=You%20have%20successfully%20logged%20out&messageType=success&messageHeading=Logged%20Out
oqmCoreApi:
devservice:
- coreApiVersion: 2.1.0-DEV
certPath: ../../../../dev/devTest-cert-cert.pem
certKeyPath: ../../../../dev/devTest-cert-key.pem
diff --git a/software/oqm-core-base-station/src/main/resources/templates/tags/links/itemCategory.html b/software/oqm-core-base-station/src/main/resources/templates/tags/links/itemCategory.html
index 6a77ada5c..9769923c9 100644
--- a/software/oqm-core-base-station/src/main/resources/templates/tags/links/itemCategory.html
+++ b/software/oqm-core-base-station/src/main/resources/templates/tags/links/itemCategory.html
@@ -1 +1 @@
-{#icons/categories}{/icons/categories} Categories
\ No newline at end of file
+{#icons/categories}{/icons/categories} Categories
\ No newline at end of file
diff --git a/software/oqm-core-base-station/src/main/resources/templates/webui/mainWebPageTemplate.html b/software/oqm-core-base-station/src/main/resources/templates/webui/mainWebPageTemplate.html
index 56c0500b9..b80b30657 100644
--- a/software/oqm-core-base-station/src/main/resources/templates/webui/mainWebPageTemplate.html
+++ b/software/oqm-core-base-station/src/main/resources/templates/webui/mainWebPageTemplate.html
@@ -58,7 +58,7 @@