Skip to content

Commit

Permalink
fix: PR changes
Browse files Browse the repository at this point in the history
  • Loading branch information
anku255 committed Feb 15, 2024
1 parent 300592f commit bb7d84e
Show file tree
Hide file tree
Showing 8 changed files with 520 additions and 31 deletions.
79 changes: 79 additions & 0 deletions src/main/java/io/supertokens/bulkimport/BulkImport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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.bulkimport;

import io.supertokens.pluginInterface.bulkimport.BulkImportUser;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorage;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.utils.Utils;

import com.google.gson.JsonObject;

import java.util.ArrayList;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

public class BulkImport {

public static final int GET_USERS_PAGINATION_LIMIT = 500;
public static final int GET_USERS_DEFAULT_LIMIT = 100;

public static void addUsers(AppIdentifierWithStorage appIdentifierWithStorage, ArrayList<BulkImportUser> users)
throws StorageQueryException, TenantOrAppNotFoundException {
while (true) {
try {
appIdentifierWithStorage.getBulkImportStorage().addBulkImportUsers(appIdentifierWithStorage, users);
break;
} catch (io.supertokens.pluginInterface.bulkimport.exceptions.DuplicateUserIdException ignored) {
// We re-generate the user id for every user and retry
for (BulkImportUser user : users) {
user.id = Utils.getUUID();
}
}
}
}

public static BulkImportUserPaginationContainer getUsers(AppIdentifierWithStorage appIdentifierWithStorage,
@Nonnull Integer limit, @Nullable String status, @Nullable String paginationToken)
throws StorageQueryException, BulkImportUserPaginationToken.InvalidTokenException,
TenantOrAppNotFoundException {
JsonObject[] users;

if (paginationToken == null) {
users = appIdentifierWithStorage.getBulkImportStorage()
.getBulkImportUsers(appIdentifierWithStorage, limit + 1, status, null);
} else {
BulkImportUserPaginationToken tokenInfo = BulkImportUserPaginationToken.extractTokenInfo(paginationToken);
users = appIdentifierWithStorage.getBulkImportStorage()
.getBulkImportUsers(appIdentifierWithStorage, limit + 1, status, tokenInfo.bulkImportUserId);
}

String nextPaginationToken = null;
int maxLoop = users.length;
if (users.length == limit + 1) {
maxLoop = limit;
nextPaginationToken = new BulkImportUserPaginationToken(users[limit].get("id").getAsString())
.generateToken();
}

JsonObject[] resultUsers = new JsonObject[maxLoop];
System.arraycopy(users, 0, resultUsers, 0, maxLoop);
return new BulkImportUserPaginationContainer(resultUsers, nextPaginationToken);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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.bulkimport;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

import com.google.gson.JsonObject;

public class BulkImportUserPaginationContainer {
public final JsonObject[] users;
public final String nextPaginationToken;

public BulkImportUserPaginationContainer(@Nonnull JsonObject[] users, @Nullable String nextPaginationToken) {
this.users = users;
this.nextPaginationToken = nextPaginationToken;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* 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.bulkimport;

import java.util.Base64;

public class BulkImportUserPaginationToken {
public final String bulkImportUserId;

public BulkImportUserPaginationToken(String bulkImportUserId) {
this.bulkImportUserId = bulkImportUserId;
}

public static BulkImportUserPaginationToken extractTokenInfo(String token) throws InvalidTokenException {
try {
String bulkImportUserId = new String(Base64.getDecoder().decode(token));
return new BulkImportUserPaginationToken(bulkImportUserId);
} catch (Exception e) {
throw new InvalidTokenException();
}
}

public String generateToken() {
return new String(Base64.getEncoder().encode((this.bulkImportUserId).getBytes()));
}

public static class InvalidTokenException extends Exception {

private static final long serialVersionUID = 6289026174830695478L;
}
}
2 changes: 2 additions & 0 deletions src/main/java/io/supertokens/webserver/Webserver.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.webserver.api.accountlinking.*;
import io.supertokens.webserver.api.bulkimport.AddBulkImportUsers;
import io.supertokens.webserver.api.bulkimport.GetBulkImportUsers;
import io.supertokens.webserver.api.core.*;
import io.supertokens.webserver.api.dashboard.*;
import io.supertokens.webserver.api.emailpassword.UserAPI;
Expand Down Expand Up @@ -261,6 +262,7 @@ private void setupRoutes() {
addAPI(new RequestStatsAPI(main));

addAPI(new AddBulkImportUsers(main));
addAPI(new GetBulkImportUsers(main));

StandardContext context = tomcatReference.getContext();
Tomcat tomcat = tomcatReference.getTomcat();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,18 @@
package io.supertokens.webserver.api.bulkimport;

import io.supertokens.Main;
import io.supertokens.bulkimport.BulkImport;
import io.supertokens.featureflag.EE_FEATURES;
import io.supertokens.featureflag.FeatureFlag;
import io.supertokens.multitenancy.Multitenancy;
import io.supertokens.pluginInterface.bulkimport.BulkImportStorage;
import io.supertokens.pluginInterface.bulkimport.BulkImportUser;
import io.supertokens.pluginInterface.bulkimport.exceptions.InvalidBulkImportDataException;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
import io.supertokens.pluginInterface.multitenancy.AppIdentifierWithStorage;
import io.supertokens.pluginInterface.multitenancy.TenantConfig;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.webserver.InputParser;
import io.supertokens.webserver.WebserverAPI;
import jakarta.servlet.ServletException;
Expand Down Expand Up @@ -55,31 +60,27 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S
JsonObject input = InputParser.parseJsonObjectOrThrowError(req);
JsonArray users = InputParser.parseArrayOrThrowError(input, "users", false);

if (users.size() > MAX_USERS_TO_ADD) {
if (users.size() <= 0 || users.size() > MAX_USERS_TO_ADD) {
JsonObject errorResponseJson = new JsonObject();
errorResponseJson.addProperty("error", "You can only add 1000 users at a time.");
super.sendJsonResponse(400, errorResponseJson, resp);
return;
String errorMsg = users.size() <= 0 ? "You need to add at least one user."
: "You can only add 10000 users at a time.";
errorResponseJson.addProperty("error", errorMsg);
throw new ServletException(new WebserverAPI.BadRequestException(errorResponseJson.toString()));
}

AppIdentifier appIdentifier = null;
try {
appIdentifier = getTenantIdentifierFromRequest(req).toAppIdentifier();
} catch (ServletException e) {
throw new ServletException(e);
}

TenantConfig[] allTenantConfigs = Multitenancy.getAllTenantsForApp(appIdentifier, main);
ArrayList<String> validTenantIds = new ArrayList<>();
Arrays.stream(allTenantConfigs)
.forEach(tenantConfig -> validTenantIds.add(tenantConfig.tenantIdentifier.getTenantId()));
AppIdentifier appIdentifier = getTenantIdentifierFromRequest(req).toAppIdentifier();

JsonArray errorsJson = new JsonArray();
ArrayList<BulkImportUser> usersToAdd = new ArrayList<>();

for (int i = 0; i < users.size(); i++) {
try {
usersToAdd.add(new BulkImportUser(users.get(i).getAsJsonObject(), validTenantIds, null));
BulkImportUser user = new BulkImportUser(users.get(i).getAsJsonObject(), null);
usersToAdd.add(user);

for (BulkImportUser.LoginMethod loginMethod : user.loginMethods) {
validateTenantId(appIdentifier, loginMethod.tenantId, loginMethod.recipeId);
}
} catch (InvalidBulkImportDataException e) {
JsonObject errorObj = new JsonObject();

Expand All @@ -89,12 +90,6 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S

errorObj.addProperty("index", i);
errorObj.add("errors", errors);

errorsJson.add(errorObj);
} catch (Exception e) {
JsonObject errorObj = new JsonObject();
errorObj.addProperty("index", i);
errorObj.addProperty("errors", "An unknown error occurred");
errorsJson.add(errorObj);
}
}
Expand All @@ -104,21 +99,46 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S
errorResponseJson.addProperty("error",
"Data has missing or invalid fields. Please check the users field for more details.");
errorResponseJson.add("users", errorsJson);
super.sendJsonResponse(400, errorResponseJson, resp);
return;
throw new ServletException(new WebserverAPI.BadRequestException(errorResponseJson.toString()));
}

try {
AppIdentifierWithStorage appIdentifierWithStorage = getAppIdentifierWithStorage(req);
BulkImportStorage storage = appIdentifierWithStorage.getBulkImportStorage();
storage.addBulkImportUsers(appIdentifierWithStorage, usersToAdd);
} catch (Exception e) {
BulkImport.addUsers(appIdentifierWithStorage, usersToAdd);
} catch (TenantOrAppNotFoundException | StorageQueryException e) {
throw new ServletException(e);
}

JsonObject result = new JsonObject();
result.addProperty("status", "OK");
super.sendJsonResponse(200, result, resp);
}

private void validateTenantId(AppIdentifier appIdentifier, String tenantId, String recipeId)
throws InvalidBulkImportDataException, ServletException {
if (tenantId == null || tenantId.equals(TenantIdentifier.DEFAULT_TENANT_ID)) {
return;
}

try {
if (Arrays.stream(FeatureFlag.getInstance(main, appIdentifier).getEnabledFeatures())
.noneMatch(t -> t == EE_FEATURES.MULTI_TENANCY)) {
throw new InvalidBulkImportDataException(new ArrayList<>(
Arrays.asList("Multitenancy must be enabled before importing users to a different tenant.")));
}
} catch (TenantOrAppNotFoundException | StorageQueryException e) {
throw new ServletException(e);
}

TenantConfig[] allTenantConfigs = Multitenancy
.getAllTenantsForApp(appIdentifier, main);
ArrayList<String> validTenantIds = new ArrayList<>();
Arrays.stream(allTenantConfigs)
.forEach(tenantConfig -> validTenantIds.add(tenantConfig.tenantIdentifier.getTenantId()));

if (!validTenantIds.contains(tenantId)) {
throw new InvalidBulkImportDataException(
new ArrayList<>(Arrays.asList("Invalid tenantId: " + tenantId + " for " + recipeId + " recipe.")));
}
}
}
Loading

0 comments on commit bb7d84e

Please sign in to comment.