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

feat: Add BulkImport APIs and cron #148

Open
wants to merge 7 commits into
base: feat/bulk-import-base
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
2 changes: 2 additions & 0 deletions src/main/java/io/supertokens/pluginInterface/Storage.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<LOG_LEVEL> logLevels, TenantIdentifier tenantIdentifier) throws InvalidConfigException;

// this returns a unique ID based on the db's connection URI and table prefix such that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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<BulkImportUser> users)
throws StorageQueryException,
TenantOrAppNotFoundException,
io.supertokens.pluginInterface.bulkimport.exceptions.DuplicateUserIdException;

/**
* Get users from the bulk_import_users table
*/
List<BulkImportUser> 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<String> deleteBulkImportUsers(AppIdentifier appIdentifier, @Nonnull String[] bulkImportUserIds) throws StorageQueryException;

/**
* Returns the users from the bulk_import_users table for processing
*/
List<BulkImportUser> 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;
sattvikc marked this conversation as resolved.
Show resolved Hide resolved

public enum BULK_IMPORT_USER_STATUS {
NEW, PROCESSING, FAILED
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* 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<UserRole> userRoles;
public List<TotpDevice> totpDevices;
public List<LoginMethod> loginMethods;

// 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;

public BulkImportUser(String id, String externalUserId, JsonObject userMetadata, List<UserRole> userRoles,
List<TotpDevice> totpDevices, List<LoginMethod> 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 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;
return user;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these two fucntions are very unclear. As a reader of this code, it makes no sense why they are there. Please add more detailed comment with examples.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added comment to explain the purpose of these functions. That should help explain it.


public JsonObject toJsonObject() {
return new Gson().fromJson(new Gson().toJson(this), JsonObject.class);
}

public static class UserRole {
public String role;
public List<String> tenantIds;

public UserRole(String role, List<String> 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<String> 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 superTokensUserId;
public String externalUserId;

public String getSuperTokenOrExternalUserId() {
return this.externalUserId != null ? this.externalUserId : this.superTokensUserId;
}

public LoginMethod(List<String> 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;
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
@@ -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 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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,12 @@ interface TransactionLogic<T> {
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;
}
Loading