Skip to content

Commit 3ffdb7f

Browse files
porcellusMihaly Lengyelrishabhpoddar
authored
feat(user_metadata): adds support for usermetadata (#33)
* feat(user_metadata): adds support for usermetadata * fix: removed old plugin interface version from the supported list * test(usermetadata): test if concurrently creating a new metadata object deadlocks * Revert "test(usermetadata): test if concurrently creating a new metadata object deadlocks" This reverts commit e04fac5. * fix(user_metadata): add NOT NULL to user_metadata column Co-authored-by: Mihaly Lengyel <mihaly.lengyel@tresorit.com> Co-authored-by: Rishabh Poddar <rishabh.poddar@gmail.com>
1 parent 43edc44 commit 3ffdb7f

File tree

7 files changed

+152
-4
lines changed

7 files changed

+152
-4
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
88
## [Unreleased]
99
- Fixes https://github.com/supertokens/supertokens-postgresql-plugin/issues/34
1010

11+
## [1.15.0] - 2022-03-04
12+
13+
- Adds support for the new usermetadata recipe
14+
1115
## [1.14.0] - 2022-02-23
1216

1317
- Adds an index on device_id_hash to the codes table.

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ plugins {
22
id 'java-library'
33
}
44

5-
version = "1.14.0"
5+
version = "1.15.0"
66

77
repositories {
88
mavenCentral()

pluginInterfaceSupported.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"_comment": "contains a list of plugin interfaces branch names that this core supports",
33
"versions": [
4-
"2.12"
4+
"2.13"
55
]
66
}

src/main/java/io/supertokens/storage/postgresql/Start.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,14 @@
4848
import io.supertokens.pluginInterface.sqlStorage.TransactionConnection;
4949
import io.supertokens.pluginInterface.thirdparty.exception.DuplicateThirdPartyUserException;
5050
import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage;
51+
import io.supertokens.pluginInterface.usermetadata.UserMetadataStorage;
52+
import io.supertokens.pluginInterface.usermetadata.sqlStorage.UserMetadataSQLStorage;
5153
import io.supertokens.storage.postgresql.config.Config;
5254
import io.supertokens.storage.postgresql.config.PostgreSQLConfig;
5355
import io.supertokens.storage.postgresql.output.Logging;
5456
import io.supertokens.storage.postgresql.queries.*;
57+
import netscape.javascript.JSObject;
58+
5559
import org.jetbrains.annotations.NotNull;
5660
import org.jetbrains.annotations.Nullable;
5761
import org.jetbrains.annotations.TestOnly;
@@ -66,7 +70,7 @@
6670
import java.util.List;
6771

6872
public class Start implements SessionSQLStorage, EmailPasswordSQLStorage, EmailVerificationSQLStorage,
69-
ThirdPartySQLStorage, JWTRecipeSQLStorage, PasswordlessSQLStorage {
73+
ThirdPartySQLStorage, JWTRecipeSQLStorage, PasswordlessSQLStorage, UserMetadataSQLStorage {
7074

7175
private static final Object appenderLock = new Object();
7276
public static boolean silent = false;
@@ -1407,4 +1411,44 @@ public io.supertokens.pluginInterface.passwordless.UserInfo getUserByPhoneNumber
14071411
throw new StorageQueryException(e);
14081412
}
14091413
}
1414+
1415+
@Override
1416+
public JsonObject getUserMetadata(String userId) throws StorageQueryException {
1417+
try {
1418+
return UserMetadataQueries.getUserMetadata(this, userId);
1419+
} catch (SQLException e) {
1420+
throw new StorageQueryException(e);
1421+
}
1422+
}
1423+
1424+
@Override
1425+
public JsonObject getUserMetadata_Transaction(TransactionConnection con, String userId)
1426+
throws StorageQueryException {
1427+
Connection sqlCon = (Connection) con.getConnection();
1428+
try {
1429+
return UserMetadataQueries.getUserMetadata_Transaction(this, sqlCon, userId);
1430+
} catch (SQLException e) {
1431+
throw new StorageQueryException(e);
1432+
}
1433+
}
1434+
1435+
@Override
1436+
public int setUserMetadata_Transaction(TransactionConnection con, String userId, JsonObject metadata)
1437+
throws StorageQueryException {
1438+
Connection sqlCon = (Connection) con.getConnection();
1439+
try {
1440+
return UserMetadataQueries.setUserMetadata_Transaction(this, sqlCon, userId, metadata);
1441+
} catch (SQLException e) {
1442+
throw new StorageQueryException(e);
1443+
}
1444+
}
1445+
1446+
@Override
1447+
public int deleteUserMetadata(String userId) throws StorageQueryException {
1448+
try {
1449+
return UserMetadataQueries.deleteUserMetadata(this, userId);
1450+
} catch (SQLException e) {
1451+
throw new StorageQueryException(e);
1452+
}
1453+
}
14101454
}

src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,10 @@ public String getJWTSigningKeysTable() {
277277
return addSchemaAndPrefixToTableName("jwt_signing_keys");
278278
}
279279

280+
public String getUserMetadataTable() {
281+
return addSchemaAndPrefixToTableName("user_metadata");
282+
}
283+
280284
private String addSchemaAndPrefixToTableName(String tableName) {
281285
String name = tableName;
282286
if (!postgresql_table_names_prefix.trim().equals("")) {

src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import static io.supertokens.storage.postgresql.queries.PasswordlessQueries.getQueryToCreateDevicesTable;
5858
import static io.supertokens.storage.postgresql.queries.SessionQueries.getQueryToCreateAccessTokenSigningKeysTable;
5959
import static io.supertokens.storage.postgresql.queries.SessionQueries.getQueryToCreateSessionInfoTable;
60+
import static io.supertokens.storage.postgresql.queries.UserMetadataQueries.getQueryToCreateUserMetadataTable;
6061

6162
public class GeneralQueries {
6263

@@ -188,6 +189,11 @@ public static void createTablesIfNotExists(Start start) throws SQLException, Sto
188189
// index if not exists"
189190
// We missed creating this earlier for the codes table, so it may be missing even if the table exists
190191
update(start, getQueryToCreateCodeDeviceIdHashIndex(start), NO_OP_SETTER);
192+
193+
if (!doesTableExists(start, Config.getConfig(start).getUserMetadataTable())) {
194+
getInstance(start).addState(CREATING_NEW_TABLE, null);
195+
update(start, getQueryToCreateUserMetadataTable(start), NO_OP_SETTER);
196+
}
191197
} catch (Exception e) {
192198
if (e.getMessage().contains("schema") && e.getMessage().contains("does not exist")
193199
&& numberOfRetries < 1) {
@@ -231,7 +237,7 @@ public static void deleteAllTables(Start start) throws SQLException, StorageQuer
231237
+ "," + getConfig(start).getJWTSigningKeysTable() + ","
232238
+ getConfig(start).getPasswordlessCodesTable() + ","
233239
+ getConfig(start).getPasswordlessDevicesTable() + ","
234-
+ getConfig(start).getPasswordlessUsersTable();
240+
+ getConfig(start).getPasswordlessUsersTable() + "," + getConfig(start).getUserMetadataTable();
235241
update(start, DROP_QUERY, NO_OP_SETTER);
236242
}
237243
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved.
3+
*
4+
* This software is licensed under the Apache License, Version 2.0 (the
5+
* "License") as published by the Apache Software Foundation.
6+
*
7+
* You may not use this file except in compliance with the License. You may
8+
* obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
17+
package io.supertokens.storage.postgresql.queries;
18+
19+
import com.google.gson.JsonObject;
20+
import com.google.gson.JsonParser;
21+
22+
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
23+
import io.supertokens.storage.postgresql.Start;
24+
import io.supertokens.storage.postgresql.config.Config;
25+
import io.supertokens.storage.postgresql.utils.Utils;
26+
27+
import java.sql.Connection;
28+
import java.sql.SQLException;
29+
30+
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute;
31+
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update;
32+
import static io.supertokens.storage.postgresql.config.Config.getConfig;
33+
34+
public class UserMetadataQueries {
35+
36+
public static String getQueryToCreateUserMetadataTable(Start start) {
37+
String schema = Config.getConfig(start).getTableSchema();
38+
String tableName = Config.getConfig(start).getUserMetadataTable();
39+
// @formatter:off
40+
return "CREATE TABLE IF NOT EXISTS " + tableName + " ("
41+
+ "user_id VARCHAR(128) NOT NULL,"
42+
+ "user_metadata TEXT NOT NULL,"
43+
+ "CONSTRAINT " + Utils.getConstraintName(schema, tableName, null, "pkey") + " PRIMARY KEY(user_id)" + " );";
44+
// @formatter:on
45+
46+
}
47+
48+
public static int deleteUserMetadata(Start start, String userId) throws SQLException, StorageQueryException {
49+
String QUERY = "DELETE FROM " + getConfig(start).getUserMetadataTable() + " WHERE user_id = ?";
50+
51+
return update(start, QUERY.toString(), pst -> pst.setString(1, userId));
52+
}
53+
54+
public static int setUserMetadata_Transaction(Start start, Connection con, String userId, JsonObject metadata)
55+
throws SQLException, StorageQueryException {
56+
57+
String QUERY = "INSERT INTO " + getConfig(start).getUserMetadataTable()
58+
+ "(user_id, user_metadata) VALUES(?, ?) "
59+
+ "ON CONFLICT(user_id) DO UPDATE SET user_metadata=excluded.user_metadata;";
60+
61+
return update(con, QUERY, pst -> {
62+
pst.setString(1, userId);
63+
pst.setString(2, metadata.toString());
64+
});
65+
}
66+
67+
public static JsonObject getUserMetadata_Transaction(Start start, Connection con, String userId)
68+
throws SQLException, StorageQueryException {
69+
String QUERY = "SELECT user_metadata FROM " + getConfig(start).getUserMetadataTable()
70+
+ " WHERE user_id = ? FOR UPDATE";
71+
return execute(con, QUERY, pst -> pst.setString(1, userId), result -> {
72+
if (result.next()) {
73+
JsonParser jp = new JsonParser();
74+
return jp.parse(result.getString("user_metadata")).getAsJsonObject();
75+
}
76+
return null;
77+
});
78+
}
79+
80+
public static JsonObject getUserMetadata(Start start, String userId) throws SQLException, StorageQueryException {
81+
String QUERY = "SELECT user_metadata FROM " + getConfig(start).getUserMetadataTable() + " WHERE user_id = ?";
82+
return execute(start, QUERY, pst -> pst.setString(1, userId), result -> {
83+
if (result.next()) {
84+
JsonParser jp = new JsonParser();
85+
return jp.parse(result.getString("user_metadata")).getAsJsonObject();
86+
}
87+
return null;
88+
});
89+
}
90+
}

0 commit comments

Comments
 (0)