Skip to content

Commit

Permalink
API updates
Browse files Browse the repository at this point in the history
  • Loading branch information
prateek3255 committed Apr 10, 2024
1 parent 16f2e3e commit d7b17fd
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 45 deletions.
21 changes: 10 additions & 11 deletions src/api/tenants/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ export const useTenantCreateService = () => {
createdNew: boolean;
}
| {
status: "MULTITENANCY_NOT_ENABLED_IN_CORE";
status: "MULTITENANCY_NOT_ENABLED_IN_CORE_ERROR";
}
| {
status: "INVALID_TENANT_ID";
status: "INVALID_TENANT_ID_ERROR";
message: string;
}
| undefined
Expand Down Expand Up @@ -195,15 +195,14 @@ export const useUpdateFirstFactorsService = () => {
enable: boolean
): Promise<
| { status: "OK" }
| { status: "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK"; message: string }
| { status: "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK_ERROR"; message: string }
| { status: "UNKNOWN_TENANT_ERROR" }
> => {
// TODO: Temporary mock data
await new Promise((resolve) => setTimeout(resolve, 1000));

return {
status: "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK",
message: "Recipe not initialized",
status: "OK",
};

const response = await fetchData({
Expand Down Expand Up @@ -235,17 +234,17 @@ export const useUpdateSecondaryFactorsService = () => {
factorId: string,
enable: boolean
): Promise<
| { status: "OK" }
| { status: "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK"; message: string }
| { status: "MFA_NOT_INITIALIZED" }
| { status: "MFA_REQUIREMENTS_FOR_AUTH_OVERRIDDEN" }
| { status: "OK"; isMFARequirementsForAuthOverridden: boolean }
| { status: "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK_ERROR"; message: string }
| { status: "MFA_NOT_INITIALIZED_ERROR" }
| { status: "UNKNOWN_TENANT_ERROR" }
> => {
// TODO: Temporary mock data
await new Promise((resolve) => setTimeout(resolve, 1000));

return {
status: "OK",
isMFARequirementsForAuthOverridden: true,
};

const response = await fetchData({
Expand Down Expand Up @@ -277,13 +276,13 @@ export const useUpdateCoreConfigService = () => {
name: string,
value: string | number | boolean | null
): Promise<
{ status: "OK" } | { status: "UNKNOWN_TENANT_ERROR" } | { status: "INVALID_CONFIG"; message: string }
{ status: "OK" } | { status: "UNKNOWN_TENANT_ERROR" } | { status: "INVALID_CONFIG_ERROR"; message: string }
> => {
// TODO: Temporary mock data
await new Promise((resolve) => setTimeout(resolve, 1000));

return {
status: "INVALID_CONFIG",
status: "INVALID_CONFIG_ERROR",
message: "Invalid config",
};

Expand Down
2 changes: 2 additions & 0 deletions src/ui/components/inputField/InputField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export type InputFieldPropTypes = {
forceShowError?: boolean;
disabled?: boolean;
prefix?: string;
autofocus?: boolean;
handleChange: React.ChangeEventHandler<HTMLInputElement>;
/** @default "bottom" */
errorPlacement?: "bottom" | "prefix-tooltip";
Expand Down Expand Up @@ -83,6 +84,7 @@ const InputField: React.FC<InputFieldPropTypes> = (props) => {
onChange={onChange}
onKeyUp={onChange}
value={props.value}
autoFocus={props.autofocus}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
disabled={props.disabled}
Expand Down
5 changes: 3 additions & 2 deletions src/ui/components/tenants/creatNewTenant/CreateNewTenant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ export const CreateNewTenantDialog = ({ onCloseDialog }: { onCloseDialog: () =>
} else {
setTenantCreationError("Tenant already exists");
}
} else if (resp?.status === "MULTITENANCY_NOT_ENABLED_IN_CORE") {
} else if (resp?.status === "MULTITENANCY_NOT_ENABLED_IN_CORE_ERROR") {
setTenantCreationError(
"Multitenancy is not enabled for your SuperTokens instance. Please add a license key to enable it."
);
} else if (resp?.status === "INVALID_TENANT_ID") {
} else if (resp?.status === "INVALID_TENANT_ID_ERROR") {
setTenantCreationError(resp.message);
} else {
throw new Error("Failed to create tenant");
Expand All @@ -73,6 +73,7 @@ export const CreateNewTenantDialog = ({ onCloseDialog }: { onCloseDialog: () =>
error={tenantCreationError}
forceShowError={true}
label="Tenant Id"
autofocus
name="tenantId"
type="text"
value={tenantId}
Expand Down
29 changes: 20 additions & 9 deletions src/ui/components/tenants/tenantDetail/CoreConfigSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
import { useEffect, useState } from "react";
import { useContext, useEffect, useState } from "react";
import { useUpdateCoreConfigService } from "../../../../api/tenants";
import { ReactComponent as PencilIcon } from "../../../../assets/edit.svg";
import { ReactComponent as InfoIcon } from "../../../../assets/info-icon.svg";
import { ReactComponent as QuestionMarkIcon } from "../../../../assets/question-mark.svg";
import { PUBLIC_TENANT_ID } from "../../../../constants";
import { getImageUrl } from "../../../../utils";
import { PopupContentContext } from "../../../contexts/PopupContentContext";
import Button from "../../button";
import { Checkbox } from "../../checkbox/Checkbox";
import InputField from "../../inputField/InputField";
Expand Down Expand Up @@ -133,13 +135,13 @@ const CoreConfigTableRow = ({
}: CoreConfigTableRowProps) => {
const [isEditing, setIsEditing] = useState(false);
const [currentValue, setCurrentValue] = useState(value);
const [error, setError] = useState<string | null>(null);
const { tenantInfo, refetchTenant } = useTenantDetailContext();
const updateCoreConfig = useUpdateCoreConfigService();
const [isLoading, setIsLoading] = useState(false);
const [isUneditablePropertyDialogVisible, setIsUneditablePropertyDialogVisible] = useState(false);
const isMultiValue = Array.isArray(possibleValues) && possibleValues.length > 0;
const isPublicTenant = tenantInfo.tenantId === PUBLIC_TENANT_ID;
const { showToast } = useContext(PopupContentContext);

const isUneditable =
isPublicTenant ||
Expand All @@ -164,26 +166,36 @@ const CoreConfigTableRow = ({
const handleCancelEdit = () => {
setIsEditing(false);
setCurrentValue(value);
setError(null);
};

const handleSaveProperty = async () => {
try {
setIsLoading(true);
setError(null);
const res = await updateCoreConfig(tenantInfo.tenantId, name, currentValue);
if (res.status !== "OK") {
if (res.status === "UNKNOWN_TENANT_ERROR") {
setError("Tenant not found.");
showToast({
iconImage: getImageUrl("form-field-error-icon.svg"),
toastType: "error",
children: <>Tenant not found.</>,
});
} else {
setError(res.message);
showToast({
iconImage: getImageUrl("form-field-error-icon.svg"),
toastType: "error",
children: <>{res.message}</>,
});
}
return;
}
await refetchTenant();
setIsEditing(false);
} catch (e) {
setError("Something went wrong. Please try again.");
showToast({
iconImage: getImageUrl("form-field-error-icon.svg"),
toastType: "error",
children: <>Something went wrong please try again.</>,
});
} finally {
setIsLoading(false);
}
Expand Down Expand Up @@ -305,12 +317,11 @@ const CoreConfigTableRow = ({
type="text"
size="small"
name={name}
autofocus
disabled={!isEditing || currentValue === null}
handleChange={(e) => {
setCurrentValue(e.target.value);
}}
error={error ?? undefined}
forceShowError
value={currentValue === null ? "[null]" : `${currentValue}`}
/>
)}
Expand Down
45 changes: 24 additions & 21 deletions src/ui/components/tenants/tenantDetail/LoginMethodsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
import { useContext, useState } from "react";
import { useContext, useEffect, useState } from "react";
import { useUpdateFirstFactorsService, useUpdateSecondaryFactorsService } from "../../../../api/tenants";
import { ReactComponent as ErrorIcon } from "../../../../assets/form-field-error-icon.svg";
import { ReactComponent as InfoIcon } from "../../../../assets/info-icon.svg";
Expand All @@ -26,7 +26,7 @@ import { useTenantDetailContext } from "./TenantDetailContext";
import { PanelHeader, PanelHeaderTitleWithTooltip, PanelRoot } from "./tenantDetailPanel/TenantDetailPanel";

export const LoginMethodsSection = () => {
const { tenantInfo, setTenantInfo } = useTenantDetailContext();
const { tenantInfo, setTenantInfo, refetchTenant } = useTenantDetailContext();
const updateFirstFactors = useUpdateFirstFactorsService();
const updateSecondaryFactors = useUpdateSecondaryFactorsService();
const [selectedFactors, setSelectedFactors] = useState<{
Expand All @@ -50,6 +50,13 @@ export const LoginMethodsSection = () => {
requiredSecondaryFactors: {},
});

useEffect(() => {
setSelectedFactors({
firstFactors: tenantInfo.firstFactors ?? [],
requiredSecondaryFactors: tenantInfo.requiredSecondaryFactors ?? [],
});
}, [tenantInfo]);

const { showToast } = useContext(PopupContentContext);

const doesTenantHasEmailPasswordAndPasswordlessEnabled =
Expand Down Expand Up @@ -77,7 +84,7 @@ export const LoginMethodsSection = () => {
// have also added an aritificial delay so that the toggle can finish its animation and
// has good UX in case API responds too quickly
setTimeout(() => setSelectedFactors(prevFactors), 200);
if (res.status === "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK") {
if (res.status === "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK_ERROR") {
setFactorErrors((prev) => ({
...prev,
firstFactors: { ...prev.firstFactors, [id]: res.message },
Expand All @@ -100,32 +107,22 @@ export const LoginMethodsSection = () => {
setIsSecondaryFactorsLoading(true);
const res = await updateSecondaryFactors(tenantInfo.tenantId, id, !doesFactorExist);
if (res.status !== "OK") {
if (res.status === "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK") {
if (res.status === "RECIPE_NOT_CONFIGURED_ON_BACKEND_SDK_ERROR") {
setFactorErrors((prev) => ({
...prev,
requiredSecondaryFactors: { ...prev.requiredSecondaryFactors, [id]: res.message },
}));
} else if (
res.status === "MFA_NOT_INITIALIZED" ||
res.status === "MFA_REQUIREMENTS_FOR_AUTH_OVERRIDDEN"
) {
setSecondaryFactorsError(res.status);
} else if (res.status === "MFA_NOT_INITIALIZED_ERROR") {
setSecondaryFactorsError("MFA_NOT_INITIALIZED");
} else {
throw new Error(res.status);
}

// We allow users to update secondary factors even if
// getMFARequirementsForAuth is overridden, for rest of
// cases we revert the state
if (res.status !== "MFA_REQUIREMENTS_FOR_AUTH_OVERRIDDEN") {
// If the API returns a non success status, revert the state
// have also added an aritificial delay so that the toggle can finish its animation and
// has good UX in case API responds too quickly
setTimeout(() => setSelectedFactors(prevFactors), 200);
}
}

if (res.status === "OK" || res.status === "MFA_REQUIREMENTS_FOR_AUTH_OVERRIDDEN") {
// If the API returns a non success status, revert the state
// have also added an aritificial delay so that the toggle can finish its animation and
// has good UX in case API responds too quickly
setTimeout(() => setSelectedFactors(prevFactors), 200);
} else if (res.status === "OK") {
setTenantInfo((prev) =>
prev
? {
Expand All @@ -135,6 +132,10 @@ export const LoginMethodsSection = () => {
: undefined
);
}

if (res.status === "OK" && res.isMFARequirementsForAuthOverridden) {
setSecondaryFactorsError("MFA_REQUIREMENTS_FOR_AUTH_OVERRIDDEN");
}
}
} catch (error) {
showToast({
Expand All @@ -150,6 +151,8 @@ export const LoginMethodsSection = () => {
} finally {
setIsFirstFactorsLoading(false);
setIsSecondaryFactorsLoading(false);
// TODO: Enable this when the API is ready
// void refetchTenant();
}
};

Expand Down
4 changes: 2 additions & 2 deletions src/ui/components/tenants/tenantDetail/TenantDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { ReactComponent as NoTenantFound } from "../../../../assets/no-tenants.s
import { FactorIds, PUBLIC_TENANT_ID } from "../../../../constants";
import { getImageUrl } from "../../../../utils";
import Button from "../../button";
import { Loader, LoaderOverlay } from "../../loader/Loader";
import { Loader } from "../../loader/Loader";
import { AddNewProviderDialog } from "./addNewProviderDialog/AddNewProviderDialog";
import { CoreConfigSection } from "./CoreConfigSection";
import { DeleteTenantDialog } from "./deleteTenant/DeleteTenant";
Expand Down Expand Up @@ -118,7 +118,7 @@ export const TenantDetail = ({

return (
<div className="tenant-detail">
{showLoadingOverlay && <LoaderOverlay />}
{/* {showLoadingOverlay && <LoaderOverlay />} */}
<button
className="button flat"
onClick={onBackButtonClicked}>
Expand Down

0 comments on commit d7b17fd

Please sign in to comment.