[WEB-3918] fix: api tokens is_active (#6941)
* fix: is_active always returning true chore: formate expired_at to iso date * Display exact expiration timestamp for API tokens * chore: remove conversion to iso * chore: remove unwanted imports * fix: added timestamp for api token expiry * fix: handle none value in expired_at * fix: fix: handle none value in expired_at * chore: add type hints * fix: refactor --------- Co-authored-by: Alaaeddine bousselmi <alaaeddine.bousselmi@medtech.tn> Co-authored-by: gakshita <akshitagoyal1516@gmail.com> Co-authored-by: Akshita Goyal <36129505+gakshita@users.noreply.github.com>
This commit is contained in:
parent
b5ceb94fb2
commit
2bbaaed3ea
5 changed files with 37 additions and 22 deletions
|
|
@ -1,5 +1,7 @@
|
|||
from .base import BaseSerializer
|
||||
from plane.db.models import APIToken, APIActivityLog
|
||||
from rest_framework import serializers
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
class APITokenSerializer(BaseSerializer):
|
||||
|
|
@ -17,10 +19,17 @@ class APITokenSerializer(BaseSerializer):
|
|||
|
||||
|
||||
class APITokenReadSerializer(BaseSerializer):
|
||||
is_active = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = APIToken
|
||||
exclude = ("token",)
|
||||
|
||||
def get_is_active(self, obj: APIToken) -> bool:
|
||||
if obj.expired_at is None:
|
||||
return True
|
||||
return timezone.now() < obj.expired_at
|
||||
|
||||
|
||||
class APIActivityLogSerializer(BaseSerializer):
|
||||
class Meta:
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ export const CreateApiTokenModal: React.FC<Props> = (props) => {
|
|||
const csvData = {
|
||||
Title: data.label,
|
||||
Description: data.description,
|
||||
Expiry: data.expired_at ? renderFormattedDate(data.expired_at)?.replace(",", " ") ?? "" : "Never expires",
|
||||
Expiry: data.expired_at ? (renderFormattedDate(data.expired_at)?.replace(",", " ") ?? "") : "Never expires",
|
||||
"Secret key": data.token ?? "",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import { Button, CustomSelect, Input, TextArea, ToggleSwitch, TOAST_TYPE, setToa
|
|||
import { DateDropdown } from "@/components/dropdowns";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
import { renderFormattedDate, renderFormattedPayloadDate } from "@/helpers/date-time.helper";
|
||||
import { renderFormattedDate, renderFormattedTime } from "@/helpers/date-time.helper";
|
||||
|
||||
type Props = {
|
||||
handleClose: () => void;
|
||||
|
|
@ -51,20 +51,21 @@ const defaultValues: Partial<IApiToken> = {
|
|||
expired_at: null,
|
||||
};
|
||||
|
||||
const getExpiryDate = (val: string): string | null | undefined => {
|
||||
const getExpiryDate = (val: string): Date | null | undefined => {
|
||||
const today = new Date();
|
||||
|
||||
const dateToAdd = EXPIRY_DATE_OPTIONS.find((option) => option.key === val)?.value;
|
||||
|
||||
if (dateToAdd) {
|
||||
const expiryDate = add(today, dateToAdd);
|
||||
|
||||
return renderFormattedDate(expiryDate);
|
||||
}
|
||||
|
||||
if (dateToAdd) return add(today, dateToAdd);
|
||||
return null;
|
||||
};
|
||||
|
||||
const getFormattedDate = (date: Date): Date => {
|
||||
const now = new Date();
|
||||
const hours = now.getHours();
|
||||
const minutes = now.getMinutes();
|
||||
const seconds = now.getSeconds();
|
||||
return add(date, { hours, minutes, seconds });
|
||||
};
|
||||
|
||||
export const CreateApiTokenForm: React.FC<Props> = (props) => {
|
||||
const { handleClose, neverExpires, toggleNeverExpires, onSubmit } = props;
|
||||
// states
|
||||
|
|
@ -97,12 +98,13 @@ export const CreateApiTokenForm: React.FC<Props> = (props) => {
|
|||
// if never expires is toggled on, set expired_at to null
|
||||
if (neverExpires) payload.expired_at = null;
|
||||
// if never expires is toggled off, and the user has selected a custom date, set expired_at to the custom date
|
||||
else if (data.expired_at === "custom") payload.expired_at = renderFormattedPayloadDate(customDate);
|
||||
else if (data.expired_at === "custom") {
|
||||
payload.expired_at = customDate && getFormattedDate(customDate).toISOString();
|
||||
}
|
||||
// if never expires is toggled off, and the user has selected a predefined date, set expired_at to the predefined date
|
||||
else {
|
||||
const expiryDate = getExpiryDate(data.expired_at ?? "");
|
||||
|
||||
if (expiryDate) payload.expired_at = renderFormattedPayloadDate(new Date(expiryDate));
|
||||
if (expiryDate) payload.expired_at = expiryDate.toISOString();
|
||||
}
|
||||
|
||||
await onSubmit(payload).then(() => {
|
||||
|
|
@ -114,6 +116,8 @@ export const CreateApiTokenForm: React.FC<Props> = (props) => {
|
|||
const today = new Date();
|
||||
const tomorrow = add(today, { days: 1 });
|
||||
const expiredAt = watch("expired_at");
|
||||
const expiryDate = getExpiryDate(expiredAt ?? "");
|
||||
const customDateFormatted = customDate && getFormattedDate(customDate);
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(handleFormSubmit)}>
|
||||
|
|
@ -219,10 +223,10 @@ export const CreateApiTokenForm: React.FC<Props> = (props) => {
|
|||
<span className="text-xs text-custom-text-400">
|
||||
{expiredAt === "custom"
|
||||
? customDate
|
||||
? `Expires ${renderFormattedDate(customDate)}`
|
||||
? `Expires ${renderFormattedDate(customDateFormatted ?? "")} at ${renderFormattedTime(customDateFormatted ?? "")}`
|
||||
: null
|
||||
: expiredAt
|
||||
? `Expires ${getExpiryDate(expiredAt ?? "")}`
|
||||
? `Expires ${renderFormattedDate(expiryDate ?? "")} at ${renderFormattedTime(expiryDate ?? "")}`
|
||||
: null}
|
||||
</span>
|
||||
)}
|
||||
|
|
@ -249,4 +253,4 @@ export const CreateApiTokenForm: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
};
|
||||
|
|
@ -6,7 +6,7 @@ import { IApiToken } from "@plane/types";
|
|||
// ui
|
||||
import { Button, Tooltip, TOAST_TYPE, setToast } from "@plane/ui";
|
||||
// helpers
|
||||
import { renderFormattedDate } from "@/helpers/date-time.helper";
|
||||
import { renderFormattedDate, renderFormattedTime } from "@/helpers/date-time.helper";
|
||||
import { copyTextToClipboard } from "@/helpers/string.helper";
|
||||
// types
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
|
|
@ -49,7 +49,9 @@ export const GeneratedTokenDetails: React.FC<Props> = (props) => {
|
|||
</button>
|
||||
<div className="mt-6 flex items-center justify-between">
|
||||
<p className="text-xs text-custom-text-400">
|
||||
{tokenDetails.expired_at ? `Expires ${renderFormattedDate(tokenDetails.expired_at)}` : "Never expires"}
|
||||
{tokenDetails.expired_at
|
||||
? `Expires ${renderFormattedDate(tokenDetails.expired_at!)} at ${renderFormattedTime(tokenDetails.expired_at!)}`
|
||||
: "Never expires"}
|
||||
</p>
|
||||
<Button variant="neutral-primary" size="sm" onClick={handleClose}>
|
||||
{t("close")}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { IApiToken } from "@plane/types";
|
|||
// components
|
||||
import { Tooltip } from "@plane/ui";
|
||||
import { DeleteApiTokenModal } from "@/components/api-token";
|
||||
import { renderFormattedDate, calculateTimeAgo } from "@/helpers/date-time.helper";
|
||||
import { renderFormattedDate, calculateTimeAgo, renderFormattedTime } from "@/helpers/date-time.helper";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
// ui
|
||||
// helpers
|
||||
|
|
@ -52,7 +52,7 @@ export const ApiTokenListItem: React.FC<Props> = (props) => {
|
|||
<p className="mb-1 text-xs leading-6 text-custom-text-400">
|
||||
{token.is_active
|
||||
? token.expired_at
|
||||
? `Expires ${renderFormattedDate(token.expired_at!)}`
|
||||
? `Expires ${renderFormattedDate(token.expired_at!)} at ${renderFormattedTime(token.expired_at!)}`
|
||||
: "Never expires"
|
||||
: `Expired ${calculateTimeAgo(token.expired_at)}`}
|
||||
</p>
|
||||
|
|
@ -60,4 +60,4 @@ export const ApiTokenListItem: React.FC<Props> = (props) => {
|
|||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue