Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1851 add oura ring api key #1461

Merged
merged 16 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from 11 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
70 changes: 63 additions & 7 deletions Agents/UserAgent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,79 @@
UserAgent is an agent that manages TWA user with other services account or devices (e.g. smartphones). It currently supports the following functions through the endpoints:
- `/registerPhone`: register a new phone with the user
- `/getPhoneIds`: get the phone ids of the user
- `/registerOuraRing`: register an Oura ring device to a user
- `/status`: get the status of the agent. Used for testing.

## 2. Requirements
Launch stack with the default containers and the following additional containers:
- keycloak

## 3. Deploy
### 3.1 Retrieving UserAgent's image
Requests `/registerPhone`, `/getPhoneIds` and `/registerOuraRing` require Bearer user access token from Keycloak.

## 3. Usage
### `/registerPhone`

Input body:
```json
{"phoneId": "<phone-id>"}
```
Sample query:

Bearer access token should be added in header.
```
curl --location 'http://localhost:3838/user-agent/registerPhone' \
--header 'Content-Type: application/json' \
--header 'Authorization: ••••••' \
--data '{"userId": "<user-id>", "phoneId": "<phone-id>"}'
```

### `/registerOuraRing`

Input body:
```json
{"ouraRingApiKey": "<oura-ring-api-key>"}
```
Sample query:

Bearer access token should be added in header.
```
curl --location 'http://localhost:3838/user-agent/registerOuraRing' \
--header 'Content-Type: application/json' \
--header 'Authorization: ••••••' \
--data '{"ouraRingApiKey": "<oura-ring-api-key>"}'
```

### `/getPhoneIds`

No data needed. Sample query:

Bearer access token should be added in header.
```
curl --location --request GET 'http://localhost:3838/user-agent/getPhoneIds' \
--header 'Content-Type: application/json' \
--header 'Authorization: ••••••' \
```

## 4. Deploy
### 4.1 Retrieving UserAgent's image
The UserAgent should be pulled automatically with the stack-manager, if not you can pull the latest version from [cambridge_cares package](https://github.com/orgs/cambridge-cares/packages/container/package/user-agent) using `docker pull ghcr.io/cambridge-cares/user-agent:<LATEST-VERSION>`

### 3.2 Starting with the stack-manager
### 4.2 Import Keycloack configuration
1. Login to the Keycloak admin console
2. Create realm
3. Import [user-agent.json](stack-manager-config/inputs/data/user-agent.json) as a client in the realm
4. Update the root url in client setting
5. Download user-agent client adapter config from Keycloak admin console, and place the json adapter to [resources](UserAgent/src/main/resources)

Check this Keycloak [guide](https://www.keycloak.org/docs/latest/authorization_services/index.html#_resource_server_overview) for more information.

### 4.3 Starting with the stack-manager
The agent has been implemented to work in the stack, which requires the UserAgent Docker container to be deployed in the stack. To do so, place [user-agent.json](stack-manager-config/inputs/config/services/user-agent.json) in the [stack-manager config directory].

Then, run `./stack.sh start <STACK NAME>` in the [stack-manager](https://github.com/cambridge-cares/TheWorldAvatar/tree/main/Deploy/stacks/dynamic/stack-manager) main folder. This will spin up the agent in the stack.

## 4. Build and debug
## 4.1 Credentials
## 5. Build and debug
## 5.1 Credentials
The docker image uses TheWorldAvatar maven repository (`https://maven.pkg.github.com/cambridge-cares/TheWorldAvatar/`).
You will need to provide your credentials (GitHub username/personal access token) in single-word text files as follows:
```
Expand All @@ -28,10 +84,10 @@ You will need to provide your credentials (GitHub username/personal access token
repo_password.txt
```

### 4.2 Building Docker Image
### 5.2 Building Docker Image
In the same directory as this README, run `./stack.sh build`. This will build the TrajectoryQueryAgent local Docker Image.

### 4.2 Spinning up with stack-manager
### 5.2 Spinning up with stack-manager
To debug the agent, replace [`user-agent-debug.json`](stack-manager-config/inputs/config/services/user-agent-debug.json) instead of [`user-agent.json`](stack-manager-config/inputs/config/services/user-agent.json) in the [stack-manager config directory].

Spin up with `./stack.sh start <STACK NAME>` in the [stack-manager]'s main folder.
Expand Down
86 changes: 8 additions & 78 deletions Agents/UserAgent/UserAgent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>uk.ac.cam.cares.jps</groupId>
<artifactId>user-agent</artifactId>
<version>1.0.0</version>
<version>2.0.0</version>
<packaging>war</packaging>

<properties>
Expand All @@ -15,14 +15,15 @@

<!-- Version of the JPS Base Library to use -->
<jps.base.version>1.45.0</jps.base.version>
<jena.version>4.1.0</jena.version>
</properties>


<!-- Parent POM -->
<parent>
<groupId>uk.ac.cam.cares.jps</groupId>
<artifactId>jps-parent-pom</artifactId>
<version>2.2.0</version>
<version>2.3.1</version>
</parent>

<!-- Profiles are used to switch between building for development and production
Expand Down Expand Up @@ -116,49 +117,12 @@
<version>6.0.5</version>
</dependency>

<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-core</artifactId>

</dependency>

<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-jdbc-core</artifactId>

</dependency>

<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-jdbc-driver-remote</artifactId>

</dependency>



<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-jdbc-driver-bundle</artifactId>
<version>4.6.1</version>
</dependency>

<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena</artifactId>
<version>4.6.1</version>
<type>pom</type>
</dependency>

<dependency>
<groupId>uk.ac.cam.cares.downsampling</groupId>
<artifactId>downsampling</artifactId>
<version>1.0.0</version>
</dependency>





<!-- Java servlet API, version pulled from parent -->
<dependency>
<groupId>javax.servlet</groupId>
Expand Down Expand Up @@ -230,61 +194,27 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-authz-client</artifactId>
<version>26.0.3</version>
</dependency>

</dependencies>
<dependencyManagement>

<dependencies>

<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-arq</artifactId>
<version>4.6.1</version>
</dependency>

<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-core</artifactId>
<version>4.6.1</version> <!-- update to the desired version -->
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina</artifactId>
<version>9.0.56</version> <!-- update to the desired version -->
</dependency>

<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-jdbc-driver-remote</artifactId>
<version>4.5.0</version>
</dependency>


<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-jdbc-core</artifactId>
<version>4.5.0</version>
</dependency>

<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-jdbc-driver-mem</artifactId>
<version>4.6.1</version>
</dependency>


<dependency>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-jaxb-annotations</artifactId>
<version>2.10.3</version>
</dependency>

<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-shaded-guava</artifactId>
<version>3.1.1</version>
</dependency>

</dependencies>


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import org.apache.jena.arq.querybuilder.WhereBuilder;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.graph.Node_URI;
import org.apache.jena.sparql.core.Var;
import org.json.JSONArray;
import org.json.JSONObject;
Expand All @@ -16,13 +15,13 @@ public class KGQueryClient {
// Prefixes
static final String SLA = "https://www.theworldavatar.com/kg/sensorloggerapp/";
static final String MON = "https://w3id.org/MON/person.owl";
final static String str_s = "s";
final static Var VAR_S = Var.alloc(str_s);
final static String str_o = "o";
final static Var VAR_O = Var.alloc(str_o);
final Var VAR_S;
final Var VAR_O;

public KGQueryClient(RemoteStoreClient storeClient) {
this.storeClient = storeClient;
VAR_S = Var.alloc("s");
VAR_O = Var.alloc("o");
}

public JSONArray getPhoneIds(String userId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import org.apache.log4j.Logger;

import org.jooq.DSLContext;
import org.jooq.Name;
import org.jooq.Query;
import org.jooq.SQLDialect;
import org.jooq.conf.ParamType;
Expand All @@ -27,6 +26,7 @@ public TimelineRDBStoreHelper(RemoteRDBStoreClient postgresRdbClient) {
private void initRDBStore() {
initTimelineSchema();
initPhoneTable();
initOuraRingTable();
}

private void initTimelineSchema() {
Expand All @@ -52,17 +52,39 @@ private void initPhoneTable() {
postgresRdbClient.executeUpdate(queryCreateTable.getSQL());
}

private void initOuraRingTable() {
Query queryCreateTable = context.createTableIfNotExists(name("postgres", "timeline", "ouraRing"))
.column(name("oura_ring_api_key"), SQLDataType.CHAR.length(36))
.column(name("user_id"), SQLDataType.CHAR.length(36))
.constraints(constraint("pk_oura_ring_api_key").primaryKey(name("oura_ring_api_key")));
postgresRdbClient.executeUpdate(queryCreateTable.getSQL());
}

public void registerPhone(String phoneId, String userId) {
Query query = context.insertInto(table(name("timeline", "smartPhone")))
.columns(field(name("phone_id")), field(name("user_id")))
.values(val(phoneId), val(userId));
postgresRdbClient.executeUpdate(query.getSQL(ParamType.INLINED));
}

public void registerOuraRing(String ouraRingApi, String userId) {
Query query = context.insertInto(table(name("timeline", "ouraRing")))
.columns(field(name("oura_ring_api_key")), field(name("user_id")))
.values(val(ouraRingApi), val(userId));
postgresRdbClient.executeUpdate(query.getSQL(ParamType.INLINED));
}

public JSONArray getExistingPhoneIdRecord(String phoneId) {
Query query = context.select(field("user_id"))
.from(table(name("postgres", "timeline", "smartPhone")))
.where(field("phone_id").eq(val(phoneId)));
return postgresRdbClient.executeQuery(query.getSQL(ParamType.INLINED));
}

public JSONArray getExistingOuraRingRecord(String ouraRingApi) {
Query query = context.select(field("user_id"))
.from(table(name("postgres", "timeline", "ouraRing")))
.where(field("oura_ring_api_key").eq(val(ouraRingApi)));
return postgresRdbClient.executeQuery(query.getSQL(ParamType.INLINED));
}
}
Loading