From fe1a5b18b05e706c5367520ba110e924022c7f6b Mon Sep 17 00:00:00 2001 From: Ankit Tiwari Date: Wed, 20 Mar 2024 12:59:36 +0530 Subject: [PATCH 01/14] feat: Add BulkImport APIs and cron --- .../supertokens/pluginInterface/Storage.java | 2 + .../pluginInterface/StorageUtils.java | 9 ++ .../bulkimport/BulkImportStorage.java | 58 ++++++++ .../bulkimport/BulkImportUser.java | 132 ++++++++++++++++++ .../exceptions/DuplicateUserIdException.java | 21 +++ .../sqlStorage/BulkImportSQLStorage.java | 40 ++++++ 6 files changed, 262 insertions(+) create mode 100644 src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java create mode 100644 src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java create mode 100644 src/main/java/io/supertokens/pluginInterface/bulkimport/exceptions/DuplicateUserIdException.java create mode 100644 src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index 51e28f57..c17cb931 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -32,6 +32,8 @@ public interface Storage { // if silent is true, do not log anything out on the console void constructor(String processId, boolean silent, boolean isTesting); + Storage createBulkImportProxyStorageInstance(); + void loadConfig(JsonObject jsonConfig, Set logLevels, TenantIdentifier tenantIdentifier) throws InvalidConfigException; // this returns a unique ID based on the db's connection URI and table prefix such that diff --git a/src/main/java/io/supertokens/pluginInterface/StorageUtils.java b/src/main/java/io/supertokens/pluginInterface/StorageUtils.java index 19bfb89a..9e6b4c99 100644 --- a/src/main/java/io/supertokens/pluginInterface/StorageUtils.java +++ b/src/main/java/io/supertokens/pluginInterface/StorageUtils.java @@ -17,6 +17,7 @@ package io.supertokens.pluginInterface; import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage; +import io.supertokens.pluginInterface.bulkimport.sqlStorage.BulkImportSQLStorage; import io.supertokens.pluginInterface.dashboard.sqlStorage.DashboardSQLStorage; import io.supertokens.pluginInterface.emailpassword.sqlStorage.EmailPasswordSQLStorage; import io.supertokens.pluginInterface.emailverification.sqlStorage.EmailVerificationSQLStorage; @@ -132,4 +133,12 @@ public static MultitenancyStorage getMultitenancyStorage(Storage storage) { } return (MultitenancyStorage) storage; } + + public static BulkImportSQLStorage getBulkImportStorage(Storage storage) { + if (storage.getType() != STORAGE_TYPE.SQL) { + // we only support SQL for now + throw new UnsupportedOperationException(""); + } + return (BulkImportSQLStorage) storage; + } } diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java new file mode 100644 index 00000000..246fc5ea --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.bulkimport; + +import java.util.List; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + + +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; +import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; + +public interface BulkImportStorage extends NonAuthRecipeStorage { + /** + * Add users to the bulk_import_users table + */ + void addBulkImportUsers(AppIdentifier appIdentifier, List users) + throws StorageQueryException, + TenantOrAppNotFoundException, + io.supertokens.pluginInterface.bulkimport.exceptions.DuplicateUserIdException; + + /** + * Get users from the bulk_import_users table + */ + List getBulkImportUsers(AppIdentifier appIdentifier, @Nonnull Integer limit, @Nullable BULK_IMPORT_USER_STATUS status, + @Nullable String bulkImportUserId, @Nullable Long createdAt) throws StorageQueryException; + + /** + * Delete users by id from the bulk_import_users table + */ + List deleteBulkImportUsers(AppIdentifier appIdentifier, @Nonnull String[] bulkImportUserIds) throws StorageQueryException; + + /** + * Returns the users from the bulk_import_users table for processing + */ + List getBulkImportUsersForProcessing(AppIdentifier appIdentifier, @Nonnull Integer limit) throws StorageQueryException; + + public enum BULK_IMPORT_USER_STATUS { + NEW, PROCESSING, FAILED + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java new file mode 100644 index 00000000..d3f192e0 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.bulkimport; + +import java.util.List; + +import com.google.gson.Gson; +import com.google.gson.JsonObject; + +import io.supertokens.pluginInterface.bulkimport.BulkImportStorage.BULK_IMPORT_USER_STATUS; + +public class BulkImportUser { + public String id; + public String externalUserId; + public JsonObject userMetadata; + public List userRoles; + public List totpDevices; + public List loginMethods; + + // Following fields come from the DB Record. + public BULK_IMPORT_USER_STATUS status; + public String errorMessage; + public Long createdAt; + public Long updatedAt; + + public BulkImportUser(String id, String externalUserId, JsonObject userMetadata, List userRoles, + List totpDevices, List loginMethods) { + this.id = id; + this.externalUserId = externalUserId; + this.userMetadata = userMetadata; + this.userRoles = userRoles; + this.totpDevices = totpDevices; + this.loginMethods = loginMethods; + } + + public static BulkImportUser forTesting_fromJson(JsonObject jsonObject) { + return new Gson().fromJson(jsonObject, BulkImportUser.class); + } + + // This method returns a JSON object string representation, excluding 'status', 'createdAt', and 'updatedAt'. + // It is used for inserting the user into the database or during testing. + public String toRawDataForDbStorage() { + JsonObject jsonObject = new Gson().fromJson(new Gson().toJson(this), JsonObject.class); + jsonObject.remove("status"); + jsonObject.remove("createdAt"); + jsonObject.remove("updatedAt"); + return jsonObject.toString(); + } + + public static BulkImportUser fromRawDataFromDbStorage(String id, String rawData, BULK_IMPORT_USER_STATUS status, String errorMessage, long createdAt, long updatedAt) { + BulkImportUser user = new Gson().fromJson(rawData, BulkImportUser.class); + user.id = id; + user.status = status; + user.errorMessage = errorMessage; + user.createdAt = createdAt; + user.updatedAt = updatedAt; + return user; + } + + public JsonObject toJsonObject() { + return new Gson().fromJson(new Gson().toJson(this), JsonObject.class); + } + + public static class UserRole { + public String role; + public List tenantIds; + + public UserRole(String role, List tenantIds) { + this.role = role; + this.tenantIds = tenantIds; + } + } + + public static class TotpDevice { + public String secretKey; + public int period; + public int skew; + public String deviceName; + + public TotpDevice(String secretKey, int period, int skew, String deviceName) { + this.secretKey = secretKey; + this.period = period; + this.skew = skew; + this.deviceName = deviceName; + } + } + + public static class LoginMethod { + public List tenantIds; + public boolean isVerified; + public boolean isPrimary; + public long timeJoinedInMSSinceEpoch; + public String recipeId; + public String email; + public String passwordHash; + public String hashingAlgorithm; + public String thirdPartyId; + public String thirdPartyUserId; + public String phoneNumber; + public String superTokensOrExternalUserId; + + public LoginMethod(List tenantIds, String recipeId, boolean isVerified, boolean isPrimary, + long timeJoinedInMSSinceEpoch, String email, String passwordHash, String hashingAlgorithm, + String thirdPartyId, String thirdPartyUserId, String phoneNumber) { + this.tenantIds = tenantIds; + this.recipeId = recipeId; + this.isVerified = isVerified; + this.isPrimary = isPrimary; + this.timeJoinedInMSSinceEpoch = timeJoinedInMSSinceEpoch; + this.email = email; + this.passwordHash = passwordHash; + this.hashingAlgorithm = hashingAlgorithm; + this.thirdPartyId = thirdPartyId; + this.thirdPartyUserId = thirdPartyUserId; + this.phoneNumber = phoneNumber; + } + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/exceptions/DuplicateUserIdException.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/exceptions/DuplicateUserIdException.java new file mode 100644 index 00000000..07070c15 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/exceptions/DuplicateUserIdException.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.bulkimport.exceptions; + +public class DuplicateUserIdException extends Exception { + private static final long serialVersionUID = 6848053563771647272L; +} diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java new file mode 100644 index 00000000..4196c90e --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.bulkimport.sqlStorage; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import io.supertokens.pluginInterface.bulkimport.BulkImportStorage; +import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.sqlStorage.SQLStorage; +import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; + +public interface BulkImportSQLStorage extends BulkImportStorage, SQLStorage { + + /** + * Update the status of the users in the bulk_import_users table + */ + void updateBulkImportUserStatus_Transaction(AppIdentifier appIdentifier, + TransactionConnection con, @Nonnull String[] bulkImportUserIds, @Nonnull BULK_IMPORT_USER_STATUS status, @Nullable String errorMessage) throws StorageQueryException; + + /** + * Delete users by id from the bulk_import_users table + */ + void deleteBulkImportUser_Transaction(AppIdentifier appIdentifier, TransactionConnection con, @Nonnull String bulkImportUserId) throws StorageQueryException; +} From 5bbb2bfefb4260683623b5594dc2364b55e755c8 Mon Sep 17 00:00:00 2001 From: Ankit Tiwari Date: Thu, 21 Mar 2024 16:14:53 +0530 Subject: [PATCH 02/14] fix: PR changes --- .../pluginInterface/bulkimport/BulkImportStorage.java | 2 +- .../bulkimport/sqlStorage/BulkImportSQLStorage.java | 2 +- .../pluginInterface/sqlStorage/SQLStorage.java | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java index 246fc5ea..15fa89ee 100644 --- a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java @@ -50,7 +50,7 @@ List getBulkImportUsers(AppIdentifier appIdentifier, @Nonnull In /** * Returns the users from the bulk_import_users table for processing */ - List getBulkImportUsersForProcessing(AppIdentifier appIdentifier, @Nonnull Integer limit) throws StorageQueryException; + List getBulkImportUsersAndChangeStatusToProcessing(AppIdentifier appIdentifier, @Nonnull Integer limit) throws StorageQueryException; public enum BULK_IMPORT_USER_STATUS { NEW, PROCESSING, FAILED diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java index 4196c90e..59212455 100644 --- a/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java @@ -31,7 +31,7 @@ public interface BulkImportSQLStorage extends BulkImportStorage, SQLStorage { * Update the status of the users in the bulk_import_users table */ void updateBulkImportUserStatus_Transaction(AppIdentifier appIdentifier, - TransactionConnection con, @Nonnull String[] bulkImportUserIds, @Nonnull BULK_IMPORT_USER_STATUS status, @Nullable String errorMessage) throws StorageQueryException; + TransactionConnection con, @Nonnull String bulkImportUserId, @Nonnull BULK_IMPORT_USER_STATUS status, @Nullable String errorMessage) throws StorageQueryException; /** * Delete users by id from the bulk_import_users table diff --git a/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java b/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java index d1351b30..740aa4b2 100644 --- a/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java @@ -45,4 +45,12 @@ interface TransactionLogic { public enum TransactionIsolationLevel { SERIALIZABLE, REPEATABLE_READ, READ_COMMITTED, READ_UNCOMMITTED, NONE } + + /* BulkImportProxyStorage methods */ + + void closeConnectionForBulkImportProxyStorage() throws StorageQueryException; + + void commitTransactionForBulkImportProxyStorage() throws StorageQueryException; + + void rollbackTransactionForBulkImportProxyStorage() throws StorageQueryException; } From 551df7eaebb9fa0c456feb88e84e49425a2d585f Mon Sep 17 00:00:00 2001 From: Ankit Tiwari Date: Fri, 29 Mar 2024 11:49:19 +0530 Subject: [PATCH 03/14] fix: PR changes --- .../pluginInterface/bulkimport/BulkImportStorage.java | 6 ++++++ .../pluginInterface/bulkimport/BulkImportUser.java | 11 +++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java index 15fa89ee..628b8c7a 100644 --- a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java @@ -52,6 +52,12 @@ List getBulkImportUsers(AppIdentifier appIdentifier, @Nonnull In */ List getBulkImportUsersAndChangeStatusToProcessing(AppIdentifier appIdentifier, @Nonnull Integer limit) throws StorageQueryException; + + /** + * Update the bulk_import_user's primary_user_id by bulk_import_user_id + */ + void updateBulkImportUserPrimaryUserId(AppIdentifier appIdentifier, @Nonnull String bulkImportUserId, @Nonnull String primaryUserId) throws StorageQueryException; + public enum BULK_IMPORT_USER_STATUS { NEW, PROCESSING, FAILED } diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java index d3f192e0..5d929784 100644 --- a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java @@ -33,6 +33,7 @@ public class BulkImportUser { // Following fields come from the DB Record. public BULK_IMPORT_USER_STATUS status; + public String primaryUserId; public String errorMessage; public Long createdAt; public Long updatedAt; @@ -61,10 +62,11 @@ public String toRawDataForDbStorage() { return jsonObject.toString(); } - public static BulkImportUser fromRawDataFromDbStorage(String id, String rawData, BULK_IMPORT_USER_STATUS status, String errorMessage, long createdAt, long updatedAt) { + public static BulkImportUser fromRawDataFromDbStorage(String id, String rawData, BULK_IMPORT_USER_STATUS status, String primaryUserId, String errorMessage, long createdAt, long updatedAt) { BulkImportUser user = new Gson().fromJson(rawData, BulkImportUser.class); user.id = id; user.status = status; + user.primaryUserId = primaryUserId; user.errorMessage = errorMessage; user.createdAt = createdAt; user.updatedAt = updatedAt; @@ -111,7 +113,12 @@ public static class LoginMethod { public String thirdPartyId; public String thirdPartyUserId; public String phoneNumber; - public String superTokensOrExternalUserId; + public String superTokensUserId; + public String externalUserId; + + public String getSuperTokenOrExternalUserId() { + return this.externalUserId != null ? this.externalUserId : this.superTokensUserId; + } public LoginMethod(List tenantIds, String recipeId, boolean isVerified, boolean isPrimary, long timeJoinedInMSSinceEpoch, String email, String passwordHash, String hashingAlgorithm, From 288ea58f715d58f22a083429355f32641de594c7 Mon Sep 17 00:00:00 2001 From: Ankit Tiwari Date: Tue, 9 Apr 2024 17:59:24 +0530 Subject: [PATCH 04/14] fix: Update version and changelog --- CHANGELOG.md | 12 ++++++++++++ build.gradle | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73efa2e9..9aee0909 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [6.1.0] - 2024-04-10 + +### Added + +- Adds support for Bulk Import +- Adds `BulkImportUser` class to represent a bulk import user +- Adds `BulkImportStorage` interface +- Adds `DuplicateUserIdException` class +- Adds `createBulkImportProxyStorageInstance` method in `Storage` class +- Adds `closeConnectionForBulkImportProxyStorage`, `commitTransactionForBulkImportProxyStorage`, and `rollbackTransactionForBulkImportProxyStorage` method in `SQLStorage` class + + ## [6.0.0] - 2024-03-13 - Replace `TotpNotEnabledException` with `UnknownUserTotpIdException` diff --git a/build.gradle b/build.gradle index 72803d4c..239db1a8 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "6.0.0" +version = "6.0.1" repositories { mavenCentral() From fbce954e30852a5e31d40c42f6a9cc28134e8227 Mon Sep 17 00:00:00 2001 From: Ankit Tiwari Date: Thu, 18 Apr 2024 15:56:26 +0530 Subject: [PATCH 05/14] fix: PR changes --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 239db1a8..2158e809 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "6.0.1" +version = "6.1.0" repositories { mavenCentral() From 02f37da2898dcd954a0ba9c1be19c89295b9accc Mon Sep 17 00:00:00 2001 From: Ankit Tiwari Date: Wed, 29 May 2024 12:46:06 +0530 Subject: [PATCH 06/14] fix: PR changes --- .../bulkimport/BulkImportStorage.java | 6 +++++- .../bulkimport/BulkImportUser.java | 16 +++++++++++++--- .../sqlStorage/BulkImportSQLStorage.java | 5 ----- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java index 628b8c7a..492d199f 100644 --- a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportStorage.java @@ -21,7 +21,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; - import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; @@ -58,6 +57,11 @@ List getBulkImportUsers(AppIdentifier appIdentifier, @Nonnull In */ void updateBulkImportUserPrimaryUserId(AppIdentifier appIdentifier, @Nonnull String bulkImportUserId, @Nonnull String primaryUserId) throws StorageQueryException; + /** + * Returns the count of users from the bulk_import_users table + */ + long getBulkImportUsersCount(AppIdentifier appIdentifier, @Nullable BULK_IMPORT_USER_STATUS status) throws StorageQueryException; + public enum BULK_IMPORT_USER_STATUS { NEW, PROCESSING, FAILED } diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java index 5d929784..dff8ac57 100644 --- a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java @@ -52,8 +52,12 @@ public static BulkImportUser forTesting_fromJson(JsonObject jsonObject) { return new Gson().fromJson(jsonObject, BulkImportUser.class); } - // This method returns a JSON object string representation, excluding 'status', 'createdAt', and 'updatedAt'. - // It is used for inserting the user into the database or during testing. + // The bulk_import_users table stores users to be imported via a Cron Job. + // It has a `raw_data` column containing user data in JSON format. + + // The BulkImportUser class represents this `raw_data`, including additional fields like `status`, `createdAt`, and `updatedAt`. + // First, we validate all fields of `raw_data` using the BulkImportUser class, then store this data in the bulk_import_users table. + // This function retrieves the `raw_data` after removing the additional fields. public String toRawDataForDbStorage() { JsonObject jsonObject = new Gson().fromJson(new Gson().toJson(this), JsonObject.class); jsonObject.remove("status"); @@ -62,6 +66,10 @@ public String toRawDataForDbStorage() { return jsonObject.toString(); } + // The bulk_import_users table contains a `raw_data` column with user data in JSON format, along with other columns such as `id`, `status`, `primary_user_id`, and `error_msg` etc. + + // When creating an instance of the BulkImportUser class, the extra fields must be passed separately as they are not part of the `raw_data`. + // This function creates a BulkImportUser instance from a stored bulk_import_user entry. public static BulkImportUser fromRawDataFromDbStorage(String id, String rawData, BULK_IMPORT_USER_STATUS status, String primaryUserId, String errorMessage, long createdAt, long updatedAt) { BulkImportUser user = new Gson().fromJson(rawData, BulkImportUser.class); user.id = id; @@ -110,6 +118,7 @@ public static class LoginMethod { public String email; public String passwordHash; public String hashingAlgorithm; + public String plainTextPassword; public String thirdPartyId; public String thirdPartyUserId; public String phoneNumber; @@ -121,7 +130,7 @@ public String getSuperTokenOrExternalUserId() { } public LoginMethod(List tenantIds, String recipeId, boolean isVerified, boolean isPrimary, - long timeJoinedInMSSinceEpoch, String email, String passwordHash, String hashingAlgorithm, + long timeJoinedInMSSinceEpoch, String email, String passwordHash, String hashingAlgorithm, String plainTextPassword, String thirdPartyId, String thirdPartyUserId, String phoneNumber) { this.tenantIds = tenantIds; this.recipeId = recipeId; @@ -131,6 +140,7 @@ public LoginMethod(List tenantIds, String recipeId, boolean isVerified, this.email = email; this.passwordHash = passwordHash; this.hashingAlgorithm = hashingAlgorithm; + this.plainTextPassword = plainTextPassword; this.thirdPartyId = thirdPartyId; this.thirdPartyUserId = thirdPartyUserId; this.phoneNumber = phoneNumber; diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java index 59212455..a7d1fff3 100644 --- a/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java @@ -32,9 +32,4 @@ public interface BulkImportSQLStorage extends BulkImportStorage, SQLStorage { */ void updateBulkImportUserStatus_Transaction(AppIdentifier appIdentifier, TransactionConnection con, @Nonnull String bulkImportUserId, @Nonnull BULK_IMPORT_USER_STATUS status, @Nullable String errorMessage) throws StorageQueryException; - - /** - * Delete users by id from the bulk_import_users table - */ - void deleteBulkImportUser_Transaction(AppIdentifier appIdentifier, TransactionConnection con, @Nonnull String bulkImportUserId) throws StorageQueryException; } From be5c9497bd327d11da41ce7285983445986db609 Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Fri, 27 Sep 2024 12:16:23 +0200 Subject: [PATCH 07/14] fix: fixing transaction rolled back issues with multithreaded bulk import --- ...kImportTransactionRolledBackException.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 src/main/java/io/supertokens/pluginInterface/bulkimport/exceptions/BulkImportTransactionRolledBackException.java diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/exceptions/BulkImportTransactionRolledBackException.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/exceptions/BulkImportTransactionRolledBackException.java new file mode 100644 index 00000000..b78afbca --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/exceptions/BulkImportTransactionRolledBackException.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.bulkimport.exceptions; + +import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; + +import java.io.Serial; +/* + The purpose of this exception is to signal for the BulkImport cronjob that the mysql transaction was rolled back. + PostgreSQL supports nested transactions while MySQL doesn't. When encountering a deadlock in the multithreaded + bulkimport process, both DBMS issue a rollback to resolve the deadlock. + In PostgreSQL: the innermost transaction gets reverted and later retried by the java logic. + In MySQL: the rollback causes the whole "big" transaction to revert, but the java logic only retries the innermost + automatically. + This exception is intended for the ProcessBulkImportUsers cronjob, to signal to restart the whole transaction. + */ +public class BulkImportTransactionRolledBackException extends StorageTransactionLogicException { + @Serial + private static final long serialVersionUID = 5196064868023712426L; + + public BulkImportTransactionRolledBackException(Exception e) { + super(e); + } +} From a7a489e88deec801bd4099c0af305c89f745933d Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Fri, 27 Sep 2024 12:19:31 +0200 Subject: [PATCH 08/14] fix: changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c243891e..33fedac8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +- Adds `BulkImportTransactionRolledBackException` for signaling if the transaction was rolled back by the DBMS + ## [6.2.0] - 2024-04-25 ### Added From 7562d9fd4b254f9f6442450e3cec872d46b26d9e Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Thu, 17 Oct 2024 10:31:27 +0200 Subject: [PATCH 09/14] fix: reusing gson object --- .../pluginInterface/bulkimport/BulkImportUser.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java index dff8ac57..dc18dc83 100644 --- a/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/BulkImportUser.java @@ -38,6 +38,8 @@ public class BulkImportUser { public Long createdAt; public Long updatedAt; + private static final Gson gson = new Gson(); + public BulkImportUser(String id, String externalUserId, JsonObject userMetadata, List userRoles, List totpDevices, List loginMethods) { this.id = id; @@ -49,7 +51,7 @@ public BulkImportUser(String id, String externalUserId, JsonObject userMetadata, } public static BulkImportUser forTesting_fromJson(JsonObject jsonObject) { - return new Gson().fromJson(jsonObject, BulkImportUser.class); + return gson.fromJson(jsonObject, BulkImportUser.class); } // The bulk_import_users table stores users to be imported via a Cron Job. @@ -59,7 +61,7 @@ public static BulkImportUser forTesting_fromJson(JsonObject jsonObject) { // First, we validate all fields of `raw_data` using the BulkImportUser class, then store this data in the bulk_import_users table. // This function retrieves the `raw_data` after removing the additional fields. public String toRawDataForDbStorage() { - JsonObject jsonObject = new Gson().fromJson(new Gson().toJson(this), JsonObject.class); + JsonObject jsonObject = gson.fromJson(new Gson().toJson(this), JsonObject.class); jsonObject.remove("status"); jsonObject.remove("createdAt"); jsonObject.remove("updatedAt"); @@ -71,7 +73,7 @@ public String toRawDataForDbStorage() { // When creating an instance of the BulkImportUser class, the extra fields must be passed separately as they are not part of the `raw_data`. // This function creates a BulkImportUser instance from a stored bulk_import_user entry. public static BulkImportUser fromRawDataFromDbStorage(String id, String rawData, BULK_IMPORT_USER_STATUS status, String primaryUserId, String errorMessage, long createdAt, long updatedAt) { - BulkImportUser user = new Gson().fromJson(rawData, BulkImportUser.class); + BulkImportUser user = gson.fromJson(rawData, BulkImportUser.class); user.id = id; user.status = status; user.primaryUserId = primaryUserId; @@ -82,7 +84,7 @@ public static BulkImportUser fromRawDataFromDbStorage(String id, String rawData, } public JsonObject toJsonObject() { - return new Gson().fromJson(new Gson().toJson(this), JsonObject.class); + return gson.fromJson(gson.toJson(this), JsonObject.class); } public static class UserRole { From 714e4c76f0dc92971aeceba99912f3609792f3af Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Fri, 15 Nov 2024 16:58:01 +0100 Subject: [PATCH 10/14] feat: bulk inserting the bulk migration data --- .../sqlStorage/AuthRecipeSQLStorage.java | 13 +++++++ .../bulkimport/ImportUserBase.java | 34 +++++++++++++++++++ .../EmailPasswordImportUser.java | 30 ++++++++++++++++ .../emailpassword/EmailPasswordStorage.java | 7 ++++ .../EmailVerificationSQLStorage.java | 6 ++++ .../passwordless/PasswordlessImportUser.java | 30 ++++++++++++++++ .../sqlStorage/PasswordlessSQLStorage.java | 5 +++ .../sqlStorage/SQLStorage.java | 3 +- .../thirdparty/ThirdPartyImportUser.java | 33 ++++++++++++++++++ .../sqlStorage/ThirdPartySQLStorage.java | 6 ++++ .../totp/sqlStorage/TOTPSQLStorage.java | 5 +++ .../sqlStorage/UserMetadataSQLStorage.java | 10 ++++++ .../sqlStorage/UserRolesSQLStorage.java | 5 +++ 13 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/supertokens/pluginInterface/bulkimport/ImportUserBase.java create mode 100644 src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordImportUser.java create mode 100644 src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessImportUser.java create mode 100644 src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyImportUser.java diff --git a/src/main/java/io/supertokens/pluginInterface/authRecipe/sqlStorage/AuthRecipeSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/authRecipe/sqlStorage/AuthRecipeSQLStorage.java index 1bcb8efb..f8d30058 100644 --- a/src/main/java/io/supertokens/pluginInterface/authRecipe/sqlStorage/AuthRecipeSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/authRecipe/sqlStorage/AuthRecipeSQLStorage.java @@ -23,12 +23,19 @@ import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; +import java.util.List; +import java.util.Map; + public interface AuthRecipeSQLStorage extends AuthRecipeStorage, SQLStorage { AuthRecipeUserInfo getPrimaryUserById_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId) throws StorageQueryException; + List getPrimaryUsersByIds_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + List userIds) + throws StorageQueryException; + // lock order: // - emailpassword table // - thirdparty table @@ -52,9 +59,15 @@ AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo_Transaction(AppIdentifier void makePrimaryUser_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId) throws StorageQueryException; + void makePrimaryUsers_Transaction(AppIdentifier appIdentifier, TransactionConnection con, List userIds) + throws StorageQueryException; + void linkAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String recipeUserId, String primaryUserId) throws StorageQueryException; + void linkMultipleAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + Map recipeUserIdByPrimaryUserId) throws StorageQueryException; + void unlinkAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String primaryUserId, String recipeUserId) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/ImportUserBase.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/ImportUserBase.java new file mode 100644 index 00000000..fd97cff2 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/ImportUserBase.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.bulkimport; + +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; + +public class ImportUserBase { + + public String userId; + public String email; + public TenantIdentifier tenantIdentifier; + public long timeJoinedMSSinceEpoch; + + public ImportUserBase(String userId, String email, TenantIdentifier tenantIdentifier, long timeJoinedMSSinceEpoch) { + this.userId = userId; //this will be the supertokens userId. + this.email = email; + this.tenantIdentifier = tenantIdentifier; + this.timeJoinedMSSinceEpoch = timeJoinedMSSinceEpoch; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordImportUser.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordImportUser.java new file mode 100644 index 00000000..00f254df --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordImportUser.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.emailpassword; + +import io.supertokens.pluginInterface.bulkimport.ImportUserBase; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; + +public class EmailPasswordImportUser extends ImportUserBase { + + public String passwordHash; + + public EmailPasswordImportUser(String userId, String email, String passwordHash, TenantIdentifier tenantId, long timeJoinedInMSSinceEpoch) { + super(userId, email, tenantId, timeJoinedInMSSinceEpoch); + this.passwordHash = passwordHash; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java index 1d4aee84..2f9a2c0b 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java @@ -23,10 +23,13 @@ import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateUserIdException; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; +import java.util.List; + public interface EmailPasswordStorage extends AuthRecipeStorage { // we pass tenantIdentifier here cause this also adds to the userId <-> tenantId mapping @@ -35,6 +38,10 @@ AuthRecipeUserInfo signUp(TenantIdentifier tenantIdentifier, String id, String e throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException, TenantOrAppNotFoundException; + void signUpMultiple(List users) + throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException, + TenantOrAppNotFoundException, StorageTransactionLogicException; + // password reset stuff is app wide cause changing the password for a user affects all the tenants // across which it's shared. void addPasswordResetToken(AppIdentifier appIdentifier, PasswordResetTokenInfo passwordResetTokenInfo) diff --git a/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java index 877fb902..bde59d29 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailverification/sqlStorage/EmailVerificationSQLStorage.java @@ -25,6 +25,8 @@ import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; +import java.util.Map; + public interface EmailVerificationSQLStorage extends EmailVerificationStorage, SQLStorage { EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUser_Transaction(TenantIdentifier tenantIdentifier, @@ -42,6 +44,10 @@ void updateIsEmailVerified_Transaction(AppIdentifier appIdentifier, TransactionC boolean isEmailVerified) throws StorageQueryException, TenantOrAppNotFoundException; + void updateMultipleIsEmailVerified_Transaction(AppIdentifier appIdentifier, TransactionConnection con, + Map emailToUserId, boolean isEmailVerified) + throws StorageQueryException, TenantOrAppNotFoundException; + void deleteEmailVerificationUserInfo_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessImportUser.java b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessImportUser.java new file mode 100644 index 00000000..4afb2cf9 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/PasswordlessImportUser.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.passwordless; + +import io.supertokens.pluginInterface.bulkimport.ImportUserBase; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; + +public class PasswordlessImportUser extends ImportUserBase { + + public String phoneNumber; + + public PasswordlessImportUser(String userId, String phoneNumber, String email, TenantIdentifier tenantIdentifier, long timeJoinedInMSSinceEpoch) { + super(userId, email, tenantIdentifier, timeJoinedInMSSinceEpoch); + this.phoneNumber = phoneNumber; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java index cfd6dbf7..1235366e 100644 --- a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java @@ -23,6 +23,7 @@ import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.passwordless.PasswordlessCode; import io.supertokens.pluginInterface.passwordless.PasswordlessDevice; +import io.supertokens.pluginInterface.passwordless.PasswordlessImportUser; import io.supertokens.pluginInterface.passwordless.PasswordlessStorage; import io.supertokens.pluginInterface.passwordless.exception.DuplicatePhoneNumberException; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; @@ -30,6 +31,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Collection; public interface PasswordlessSQLStorage extends PasswordlessStorage, SQLStorage { PasswordlessDevice getDevice_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, @@ -85,4 +87,7 @@ void updateUserPhoneNumber_Transaction(AppIdentifier appIdentifier, TransactionC void deletePasswordlessUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, boolean deleteUserIdMappingToo) throws StorageQueryException; + + void importPasswordlessUsers_Transaction(TransactionConnection con, Collection users) + throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java b/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java index 740aa4b2..a5fa1259 100644 --- a/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java @@ -39,7 +39,8 @@ KeyValueInfo getKeyValue_Transaction(TenantIdentifier tenantIdentifier, Transact throws StorageQueryException; interface TransactionLogic { - T mainLogicAndCommit(TransactionConnection con) throws StorageQueryException, StorageTransactionLogicException; + T mainLogicAndCommit(TransactionConnection con) + throws StorageQueryException, StorageTransactionLogicException, TenantOrAppNotFoundException; } public enum TransactionIsolationLevel { diff --git a/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyImportUser.java b/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyImportUser.java new file mode 100644 index 00000000..e66784d7 --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/thirdparty/ThirdPartyImportUser.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.thirdparty; + +import io.supertokens.pluginInterface.bulkimport.ImportUserBase; +import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; + +public class ThirdPartyImportUser extends ImportUserBase { + + public String thirdpartyId; + public String thirdpartyUserId; + + public ThirdPartyImportUser(String email, String userId, String thirdpartyId, String thirdpartyUserId, + TenantIdentifier tenantIdentifier, long timeJoinedInMSSinceEpoch) { + super(userId, email, tenantIdentifier, timeJoinedInMSSinceEpoch); + this.thirdpartyId = thirdpartyId; + this.thirdpartyUserId = thirdpartyUserId; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java b/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java index ea55a02f..3e022a16 100644 --- a/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java @@ -20,8 +20,11 @@ import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; +import io.supertokens.pluginInterface.thirdparty.ThirdPartyImportUser; import io.supertokens.pluginInterface.thirdparty.ThirdPartyStorage; +import java.util.Collection; + public interface ThirdPartySQLStorage extends ThirdPartyStorage, SQLStorage { void updateUserEmail_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String thirdPartyId, @@ -31,4 +34,7 @@ void updateUserEmail_Transaction(AppIdentifier appIdentifier, TransactionConnect void deleteThirdPartyUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, boolean deleteUserIdMappingToo) throws StorageQueryException; + + void importThirdPartyUsers_Transaction(TransactionConnection con, Collection usersToImport) + throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java index ab8cb181..cf9750ca 100644 --- a/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/totp/sqlStorage/TOTPSQLStorage.java @@ -13,6 +13,8 @@ import io.supertokens.pluginInterface.totp.exception.UnknownTotpUserIdException; import io.supertokens.pluginInterface.totp.exception.UsedCodeAlreadyExistsException; +import java.util.List; + public interface TOTPSQLStorage extends TOTPStorage, SQLStorage { public int deleteDevice_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, String deviceName) @@ -47,4 +49,7 @@ TOTPDevice getDeviceByName_Transaction(TransactionConnection con, AppIdentifier TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentifier appIdentifier, TOTPDevice device) throws StorageQueryException, DeviceAlreadyExistsException, TenantOrAppNotFoundException; + + void createDevices_Transaction(TransactionConnection con, AppIdentifier appIdentifier, List devices) + throws StorageQueryException, TenantOrAppNotFoundException; } diff --git a/src/main/java/io/supertokens/pluginInterface/usermetadata/sqlStorage/UserMetadataSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/usermetadata/sqlStorage/UserMetadataSQLStorage.java index dbe5cc74..d391cf3e 100644 --- a/src/main/java/io/supertokens/pluginInterface/usermetadata/sqlStorage/UserMetadataSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/usermetadata/sqlStorage/UserMetadataSQLStorage.java @@ -24,14 +24,24 @@ import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; import io.supertokens.pluginInterface.usermetadata.UserMetadataStorage; +import java.util.List; +import java.util.Map; + public interface UserMetadataSQLStorage extends UserMetadataStorage, SQLStorage { JsonObject getUserMetadata_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId) throws StorageQueryException; + Map getMultipleUsersMetadatas_Transaction(AppIdentifier appIdentifier, TransactionConnection + con, List userIds) + throws StorageQueryException; + int setUserMetadata_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String userId, JsonObject metadata) throws StorageQueryException, TenantOrAppNotFoundException; + void setMultipleUsersMetadatas_Transaction(AppIdentifier appIdentifier, TransactionConnection con, Map metadataByUserId) + throws StorageQueryException, TenantOrAppNotFoundException; + int deleteUserMetadata_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java index c395ff31..b3e7931e 100644 --- a/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java @@ -25,6 +25,8 @@ import io.supertokens.pluginInterface.userroles.UserRolesStorage; import io.supertokens.pluginInterface.userroles.exception.UnknownRoleException; +import java.util.Map; + public interface UserRolesSQLStorage extends UserRolesStorage, SQLStorage { // delete role associated with the input userId from the input roles @@ -56,4 +58,7 @@ boolean doesRoleExist_Transaction(AppIdentifier appIdentifier, TransactionConnec void deleteAllRolesForUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) throws StorageQueryException; + + void addRolesToUsers_Transaction(TransactionConnection connection, Map> rolesToUserByTenants) + throws StorageQueryException; } From 8f6ba1416a74cee34c551dde1ec13ce5960e5e8e Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Fri, 22 Nov 2024 17:09:10 +0100 Subject: [PATCH 11/14] fix: fixes and error handling changes --- .../supertokens/pluginInterface/Storage.java | 4 ++ .../authRecipe/AuthRecipeStorage.java | 3 ++ .../sqlStorage/AuthRecipeSQLStorage.java | 7 ++++ .../BulkImportBatchInsertException.java | 40 +++++++++++++++++++ .../sqlStorage/BulkImportSQLStorage.java | 10 +++-- .../emailpassword/EmailPasswordStorage.java | 7 ---- .../sqlStorage/EmailPasswordSQLStorage.java | 7 ++++ .../EmailVerificationStorage.java | 5 +++ .../sqlStorage/PasswordlessSQLStorage.java | 7 ++-- .../sqlStorage/SQLStorage.java | 5 ++- .../sqlStorage/ThirdPartySQLStorage.java | 8 ++-- .../useridmapping/UserIdMappingStorage.java | 5 +++ .../sqlStorage/UserIdMappingSQLStorage.java | 6 +++ .../sqlStorage/UserRolesSQLStorage.java | 4 ++ 14 files changed, 101 insertions(+), 17 deletions(-) create mode 100644 src/main/java/io/supertokens/pluginInterface/bulkimport/exceptions/BulkImportBatchInsertException.java diff --git a/src/main/java/io/supertokens/pluginInterface/Storage.java b/src/main/java/io/supertokens/pluginInterface/Storage.java index 56f253f9..a5adca1d 100644 --- a/src/main/java/io/supertokens/pluginInterface/Storage.java +++ b/src/main/java/io/supertokens/pluginInterface/Storage.java @@ -26,6 +26,7 @@ import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import java.util.List; +import java.util.Map; import java.util.Set; public interface Storage { @@ -80,6 +81,9 @@ void setKeyValue(TenantIdentifier tenantIdentifier, String key, KeyValueInfo inf boolean isUserIdBeingUsedInNonAuthRecipe(AppIdentifier appIdentifier, String className, String userId) throws StorageQueryException; + Map> findNonAuthRecipesWhereForUserIdsUsed(AppIdentifier appIdentifier, List userIds) + throws StorageQueryException; + // to be used for testing purposes only. This function will add dummy data to non-auth tables. void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifier, String className, String userId) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java index 701b8130..9eeef263 100644 --- a/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/authRecipe/AuthRecipeStorage.java @@ -26,6 +26,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.List; public interface AuthRecipeStorage extends Storage { @@ -45,6 +46,8 @@ AuthRecipeUserInfo[] getUsers(TenantIdentifier tenantIdentifier, @Nonnull Intege boolean doesUserIdExist(TenantIdentifier tenantIdentifierIdentifier, String userId) throws StorageQueryException; + List findExistingUserIds(AppIdentifier appIdentifier, List userIds) throws StorageQueryException; + AuthRecipeUserInfo getPrimaryUserById(AppIdentifier appIdentifier, String userId) throws StorageQueryException; String getPrimaryUserIdStrForUserId(AppIdentifier appIdentifier, String userId) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/authRecipe/sqlStorage/AuthRecipeSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/authRecipe/sqlStorage/AuthRecipeSQLStorage.java index f8d30058..6e5352f7 100644 --- a/src/main/java/io/supertokens/pluginInterface/authRecipe/sqlStorage/AuthRecipeSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/authRecipe/sqlStorage/AuthRecipeSQLStorage.java @@ -45,6 +45,13 @@ AuthRecipeUserInfo[] listPrimaryUsersByEmail_Transaction(AppIdentifier appIdenti String email) throws StorageQueryException; + //helper method for bulk import + AuthRecipeUserInfo[] listPrimaryUsersByMultipleEmailsOrPhoneNumbersOrThirdparty_Transaction(AppIdentifier appIdentifier, + TransactionConnection con, + List emails, List phones, + Map thirdpartyIdToThirdpartyUserId) + throws StorageQueryException; + // locks only passwordless table AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String phoneNumber) diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/exceptions/BulkImportBatchInsertException.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/exceptions/BulkImportBatchInsertException.java new file mode 100644 index 00000000..fb55e18a --- /dev/null +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/exceptions/BulkImportBatchInsertException.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved. + * + * This software is licensed under the Apache License, Version 2.0 (the + * "License") as published by the Apache Software Foundation. + * + * You may not use this file except in compliance with the License. You may + * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.supertokens.pluginInterface.bulkimport.exceptions; + +import java.util.Map; + +//In case of batch inserting when one +public class BulkImportBatchInsertException extends Exception { + public Map exceptionByUserId; + + public BulkImportBatchInsertException(String message, Map exceptionByPosition) { + super(message); + this.exceptionByUserId = exceptionByPosition; + } + + public BulkImportBatchInsertException(String message, Throwable cause, + Map exceptionByPosition) { + super(message, cause); + this.exceptionByUserId = exceptionByPosition; + } + + public BulkImportBatchInsertException(Throwable cause, Map exceptionByPosition) { + super(cause); + this.exceptionByUserId = exceptionByPosition; + } +} diff --git a/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java index a7d1fff3..d8cb6f3f 100644 --- a/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/bulkimport/sqlStorage/BulkImportSQLStorage.java @@ -16,15 +16,16 @@ package io.supertokens.pluginInterface.bulkimport.sqlStorage; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - import io.supertokens.pluginInterface.bulkimport.BulkImportStorage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.util.Map; + public interface BulkImportSQLStorage extends BulkImportStorage, SQLStorage { /** @@ -32,4 +33,7 @@ public interface BulkImportSQLStorage extends BulkImportStorage, SQLStorage { */ void updateBulkImportUserStatus_Transaction(AppIdentifier appIdentifier, TransactionConnection con, @Nonnull String bulkImportUserId, @Nonnull BULK_IMPORT_USER_STATUS status, @Nullable String errorMessage) throws StorageQueryException; + + void updateMultipleBulkImportUsersStatusToError_Transaction(AppIdentifier appIdentifier, + TransactionConnection con, @Nonnull Map bulkImportUserIdToErrorMessage) throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java index 2f9a2c0b..1d4aee84 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/EmailPasswordStorage.java @@ -23,13 +23,10 @@ import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateUserIdException; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; -import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; -import java.util.List; - public interface EmailPasswordStorage extends AuthRecipeStorage { // we pass tenantIdentifier here cause this also adds to the userId <-> tenantId mapping @@ -38,10 +35,6 @@ AuthRecipeUserInfo signUp(TenantIdentifier tenantIdentifier, String id, String e throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException, TenantOrAppNotFoundException; - void signUpMultiple(List users) - throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException, - TenantOrAppNotFoundException, StorageTransactionLogicException; - // password reset stuff is app wide cause changing the password for a user affects all the tenants // across which it's shared. void addPasswordResetToken(AppIdentifier appIdentifier, PasswordResetTokenInfo passwordResetTokenInfo) diff --git a/src/main/java/io/supertokens/pluginInterface/emailpassword/sqlStorage/EmailPasswordSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/emailpassword/sqlStorage/EmailPasswordSQLStorage.java index af47334f..d212d8e1 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailpassword/sqlStorage/EmailPasswordSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailpassword/sqlStorage/EmailPasswordSQLStorage.java @@ -16,14 +16,18 @@ package io.supertokens.pluginInterface.emailpassword.sqlStorage; +import io.supertokens.pluginInterface.emailpassword.EmailPasswordImportUser; import io.supertokens.pluginInterface.emailpassword.EmailPasswordStorage; import io.supertokens.pluginInterface.emailpassword.PasswordResetTokenInfo; import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; +import java.util.List; + public interface EmailPasswordSQLStorage extends EmailPasswordStorage, SQLStorage { // all password reset related stuff is app wide cause the same user ID can be shared across tenants, @@ -49,4 +53,7 @@ void updateUsersEmail_Transaction(AppIdentifier appIdentifier, TransactionConnec void deleteEmailPasswordUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, boolean deleteUserIdMappingToo) throws StorageQueryException; + + void signUpMultipleViaBulkImport_Transaction(TransactionConnection connection, List users) + throws StorageQueryException, StorageTransactionLogicException; } diff --git a/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java b/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java index 55d8b298..ade6b091 100644 --- a/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/emailverification/EmailVerificationStorage.java @@ -23,6 +23,8 @@ import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.nonAuthRecipe.NonAuthRecipeStorage; +import java.util.Map; + public interface EmailVerificationStorage extends NonAuthRecipeStorage { void addEmailVerificationToken(TenantIdentifier tenantIdentifier, EmailVerificationTokenInfo emailVerificationInfo) @@ -49,4 +51,7 @@ EmailVerificationTokenInfo[] getAllEmailVerificationTokenInfoForUser(TenantIdent void updateIsEmailVerifiedToExternalUserId(AppIdentifier appIdentifier, String supertokensUserId, String externalUserId) throws StorageQueryException; + + void updateMultipleIsEmailVerifiedToExternalUserIds(AppIdentifier appIdentifier, + Map supertokensUserIdToExternalUserId) throws StorageQueryException; } \ No newline at end of file diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java index 1235366e..d6687a71 100644 --- a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java @@ -21,6 +21,7 @@ import io.supertokens.pluginInterface.exceptions.StorageQueryException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.passwordless.PasswordlessCode; import io.supertokens.pluginInterface.passwordless.PasswordlessDevice; import io.supertokens.pluginInterface.passwordless.PasswordlessImportUser; @@ -31,7 +32,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.Collection; +import java.util.List; public interface PasswordlessSQLStorage extends PasswordlessStorage, SQLStorage { PasswordlessDevice getDevice_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, @@ -88,6 +89,6 @@ void deletePasswordlessUser_Transaction(TransactionConnection con, AppIdentifier boolean deleteUserIdMappingToo) throws StorageQueryException; - void importPasswordlessUsers_Transaction(TransactionConnection con, Collection users) - throws StorageQueryException; + void importPasswordlessUsers_Transaction(TransactionConnection con, List users) + throws StorageQueryException, TenantOrAppNotFoundException; } diff --git a/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java b/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java index a5fa1259..a66d7801 100644 --- a/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/sqlStorage/SQLStorage.java @@ -24,6 +24,8 @@ import io.supertokens.pluginInterface.multitenancy.TenantIdentifier; import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; +import java.sql.SQLException; + public interface SQLStorage extends Storage { T startTransaction(TransactionLogic logic, TransactionIsolationLevel isolationLevel) throws StorageQueryException, StorageTransactionLogicException; @@ -40,7 +42,8 @@ KeyValueInfo getKeyValue_Transaction(TenantIdentifier tenantIdentifier, Transact interface TransactionLogic { T mainLogicAndCommit(TransactionConnection con) - throws StorageQueryException, StorageTransactionLogicException, TenantOrAppNotFoundException; + throws StorageQueryException, StorageTransactionLogicException, TenantOrAppNotFoundException, + SQLException; } public enum TransactionIsolationLevel { diff --git a/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java b/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java index 3e022a16..c0f17215 100644 --- a/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/thirdparty/sqlStorage/ThirdPartySQLStorage.java @@ -17,13 +17,15 @@ package io.supertokens.pluginInterface.thirdparty.sqlStorage; import io.supertokens.pluginInterface.exceptions.StorageQueryException; +import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException; import io.supertokens.pluginInterface.multitenancy.AppIdentifier; +import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException; import io.supertokens.pluginInterface.sqlStorage.SQLStorage; import io.supertokens.pluginInterface.sqlStorage.TransactionConnection; import io.supertokens.pluginInterface.thirdparty.ThirdPartyImportUser; import io.supertokens.pluginInterface.thirdparty.ThirdPartyStorage; -import java.util.Collection; +import java.util.List; public interface ThirdPartySQLStorage extends ThirdPartyStorage, SQLStorage { @@ -35,6 +37,6 @@ void deleteThirdPartyUser_Transaction(TransactionConnection con, AppIdentifier a boolean deleteUserIdMappingToo) throws StorageQueryException; - void importThirdPartyUsers_Transaction(TransactionConnection con, Collection usersToImport) - throws StorageQueryException; + void importThirdPartyUsers_Transaction(TransactionConnection con, List usersToImport) + throws StorageQueryException, StorageTransactionLogicException, TenantOrAppNotFoundException; } diff --git a/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java b/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java index eab19ae9..1b7b8c7a 100644 --- a/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/useridmapping/UserIdMappingStorage.java @@ -25,6 +25,7 @@ import javax.annotation.Nullable; import java.util.ArrayList; import java.util.HashMap; +import java.util.Map; public interface UserIdMappingStorage extends Storage { @@ -35,6 +36,10 @@ void createUserIdMapping(AppIdentifier appIdentifier, String superTokensUserId, @Nullable String externalUserIdInfo) throws StorageQueryException, UnknownSuperTokensUserIdException, UserIdMappingAlreadyExistsException; + // support method for bulk migration + void createBulkUserIdMapping(AppIdentifier appIdentifier, Map superTokensUserIdToExternalUserId) + throws StorageQueryException; + boolean deleteUserIdMapping(AppIdentifier appIdentifier, String userId, boolean isSuperTokensUserId) throws StorageQueryException; diff --git a/src/main/java/io/supertokens/pluginInterface/useridmapping/sqlStorage/UserIdMappingSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/useridmapping/sqlStorage/UserIdMappingSQLStorage.java index 44bb8573..28e283b4 100644 --- a/src/main/java/io/supertokens/pluginInterface/useridmapping/sqlStorage/UserIdMappingSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/useridmapping/sqlStorage/UserIdMappingSQLStorage.java @@ -23,6 +23,8 @@ import io.supertokens.pluginInterface.useridmapping.UserIdMapping; import io.supertokens.pluginInterface.useridmapping.UserIdMappingStorage; +import java.util.List; + public interface UserIdMappingSQLStorage extends UserIdMappingStorage, SQLStorage { UserIdMapping getUserIdMapping_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, boolean isSuperTokensUserId) @@ -30,4 +32,8 @@ UserIdMapping getUserIdMapping_Transaction(TransactionConnection con, AppIdentif UserIdMapping[] getUserIdMapping_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) throws StorageQueryException; + + List getMultipleUserIdMapping_Transaction(TransactionConnection connection, AppIdentifier appIdentifier, + List userIds, boolean isSupertokensIds) + throws StorageQueryException; } diff --git a/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java index b3e7931e..79641984 100644 --- a/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java @@ -25,6 +25,7 @@ import io.supertokens.pluginInterface.userroles.UserRolesStorage; import io.supertokens.pluginInterface.userroles.exception.UnknownRoleException; +import java.util.List; import java.util.Map; public interface UserRolesSQLStorage extends UserRolesStorage, SQLStorage { @@ -56,6 +57,9 @@ int deleteAllPermissionsForRole_Transaction(AppIdentifier appIdentifier, Transac boolean doesRoleExist_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String role) throws StorageQueryException; + List doesMultipleRoleExist_Transaction(AppIdentifier appIdentifier, TransactionConnection con, List roles) + throws StorageQueryException; + void deleteAllRolesForUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) throws StorageQueryException; From d4a125cdd5247191924b14f8e2127778db542bb9 Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Tue, 26 Nov 2024 21:48:06 +0100 Subject: [PATCH 12/14] fix: fixing tests --- .../userroles/sqlStorage/UserRolesSQLStorage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java index 79641984..2d5ab21e 100644 --- a/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/userroles/sqlStorage/UserRolesSQLStorage.java @@ -57,12 +57,12 @@ int deleteAllPermissionsForRole_Transaction(AppIdentifier appIdentifier, Transac boolean doesRoleExist_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String role) throws StorageQueryException; - List doesMultipleRoleExist_Transaction(AppIdentifier appIdentifier, TransactionConnection con, List roles) + List doesMultipleRoleExist_Transaction(AppIdentifier appIdentifier, TransactionConnection con, List roles) throws StorageQueryException; void deleteAllRolesForUser_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId) throws StorageQueryException; - void addRolesToUsers_Transaction(TransactionConnection connection, Map> rolesToUserByTenants) + void addRolesToUsers_Transaction(TransactionConnection connection, Map>> rolesToUserByTenants) throws StorageQueryException; } From 8965953c76bcf062b0314774ce7e0e995aa85ea7 Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Wed, 27 Nov 2024 12:13:28 +0100 Subject: [PATCH 13/14] chore: update build version and changelog --- CHANGELOG.md | 2 ++ build.gradle | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c10ea31f..22b76669 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [6.4.0] + - Adds support for Bulk Import - Adds `BulkImportUser` class to represent a bulk import user - Adds `BulkImportStorage` interface diff --git a/build.gradle b/build.gradle index b945eddd..a13bdd2f 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java-library' } -version = "6.3.0" +version = "6.4.0" repositories { mavenCentral() From 8d2798932c32261bbf962a0325467fe7ae0260be Mon Sep 17 00:00:00 2001 From: tamassoltesz Date: Wed, 4 Dec 2024 21:24:31 +0100 Subject: [PATCH 14/14] fix: adding exeption thrown declaration for bulk import and mysql --- .../passwordless/sqlStorage/PasswordlessSQLStorage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java index d6687a71..4722546c 100644 --- a/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java +++ b/src/main/java/io/supertokens/pluginInterface/passwordless/sqlStorage/PasswordlessSQLStorage.java @@ -16,6 +16,7 @@ package io.supertokens.pluginInterface.passwordless.sqlStorage; +import io.supertokens.pluginInterface.bulkimport.exceptions.BulkImportTransactionRolledBackException; import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException; import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException; import io.supertokens.pluginInterface.exceptions.StorageQueryException; @@ -90,5 +91,5 @@ void deletePasswordlessUser_Transaction(TransactionConnection con, AppIdentifier throws StorageQueryException; void importPasswordlessUsers_Transaction(TransactionConnection con, List users) - throws StorageQueryException, TenantOrAppNotFoundException; + throws StorageQueryException, TenantOrAppNotFoundException, BulkImportTransactionRolledBackException; }