Merge pull request #1377 from makeplane/mobile-uat

release: v24.10.07-1
This commit is contained in:
sriram veeraghanta
2024-10-07 19:59:51 +05:30
committed by GitHub
11 changed files with 118 additions and 18 deletions

View File

@@ -15,6 +15,11 @@ on:
description: "Release Version"
type: string
default: v0.0.0-cloud
useVaultSecrets:
description: "Use Vault Secrets"
type: boolean
default: false
required: true
isPrerelease:
description: "Is Pre-release"
type: boolean
@@ -113,6 +118,7 @@ jobs:
- name: Tailscale
uses: tailscale/github-action@v2
if: ${{github.event.inputs.useVaultSecrets == 'true'}}
with:
oauth-client-id: ${{ secrets.TAILSCALE_OAUTH_CLIENT_ID }}
oauth-secret: ${{ secrets.TAILSCALE_OAUTH_SECRET }}
@@ -120,6 +126,7 @@ jobs:
- name: Get the ENV values from Vault
id: get_vault_secrets
if: ${{github.event.inputs.useVaultSecrets == 'true'}}
run: |
if [ "${{ env.TARGET_BRANCH }}" == "master" ]; then
ENV_NAME="prod"

View File

@@ -10,9 +10,14 @@ from rest_framework.views import APIView
# Module imports
from plane.authentication.utils.host import user_ip
from plane.db.models import User
from plane.app.authentication.session import BaseSessionAuthentication
class MobileSignOutAuthEndpoint(APIView):
authentication_classes = [
BaseSessionAuthentication,
]
def post(self, request):
# Get user
try:

View File

@@ -9,7 +9,7 @@ from asgiref.sync import sync_to_async
# Django Imports
from django.utils import timezone
from django.db.models import Q
from django.db.models import Q, Exists, OuterRef
# Strawberry Imports
from strawberry.types import Info
@@ -17,7 +17,7 @@ from strawberry.scalars import JSON
from strawberry.permission import PermissionExtension
# Module Imports
from plane.db.models import Cycle, Issue, CycleUserProperties
from plane.db.models import Cycle, Issue, CycleUserProperties, UserFavorite
from plane.graphql.types.cycle import CycleType, CycleUserPropertyType
from plane.graphql.types.issue import (
IssuesInformationType,
@@ -43,6 +43,13 @@ class CycleQuery:
slug: str,
project: strawberry.ID,
) -> list[CycleType]:
subquery = UserFavorite.objects.filter(
user=info.context.user,
entity_type="cycle",
entity_identifier=OuterRef("pk"),
project_id=project,
)
# get cycles those are current and upcoming cycles based on the start_date and end_date
cycles = await sync_to_async(list)(
Cycle.objects.filter(workspace__slug=slug, project_id=project)
@@ -62,6 +69,7 @@ class CycleQuery:
)
)
.order_by("start_date")
.annotate(is_favorite=Exists(subquery))
)
return cycles

View File

@@ -20,6 +20,14 @@ from strawberry.permission import PermissionExtension
from plane.graphql.permissions.workspace import WorkspaceBasePermission
from plane.db.models import Issue
from plane.graphql.types.issue import IssueLiteType
from plane.graphql.types.paginator import PaginatorResponse
from plane.graphql.utils.paginator import paginate
@sync_to_async
def get_issue_details(issue_id):
issue = Issue.objects.get(id=issue_id)
return issue
@strawberry.type
@@ -43,7 +51,8 @@ class IssuesSearchQuery:
relationType: Optional[bool] = False,
subIssues: Optional[bool] = False,
search: Optional[str] = None,
) -> list[IssueLiteType]:
cursor: Optional[str] = None,
) -> PaginatorResponse[IssueLiteType]:
issue_queryset = Issue.issue_objects.filter(
workspace__slug=slug,
project__project_projectmember__member=info.context.user,
@@ -81,15 +90,13 @@ class IssuesSearchQuery:
# sub issues
if subIssues and issue:
current_issue = await sync_to_async(Issue.issue_objects.get)(
pk=issue
)
current_issue = await get_issue_details(issue)
issue_queryset = issue_queryset.filter(
Q(parent__isNull=True), ~Q(parent=issue)
Q(parent__isnull=True), ~Q(pk=issue)
)
if current_issue.parent:
issue_queryset = issue_queryset.filter(
~Q(parent=current_issue.parent)
~Q(pk=current_issue.parent_id)
)
# apply search filter
@@ -123,4 +130,8 @@ class IssuesSearchQuery:
issue["project_identifier"] = issue["project__identifier"]
del issue["project__identifier"]
return [IssueLiteType(**issue) for issue in issues]
listed_issues: list[IssueLiteType] = [
IssueLiteType(**issue) for issue in issues
]
return paginate(results_object=listed_issues, cursor=cursor)

View File

@@ -7,13 +7,16 @@ import strawberry
# Python Standard Library Imports
from asgiref.sync import sync_to_async
# Django Imports
from django.db.models import Exists, OuterRef
# Strawberry Imports
from strawberry.types import Info
from strawberry.scalars import JSON
from strawberry.permission import PermissionExtension
# Module Imports
from plane.db.models import Module, Issue, ModuleUserProperties
from plane.db.models import Module, Issue, ModuleUserProperties, UserFavorite
from plane.graphql.types.module import ModuleType, ModuleUserPropertyType
from plane.graphql.types.issue import (
IssuesInformationType,
@@ -40,6 +43,13 @@ class ModuleQuery:
project: strawberry.ID,
cursor: Optional[str] = None,
) -> PaginatorResponse[ModuleType]:
subquery = UserFavorite.objects.filter(
user=info.context.user,
entity_type="module",
entity_identifier=OuterRef("pk"),
project_id=project,
)
modules = await sync_to_async(list)(
Module.objects.filter(workspace__slug=slug)
.filter(project_id=project)
@@ -47,6 +57,7 @@ class ModuleQuery:
project__project_projectmember__member=info.context.user,
project__project_projectmember__is_active=True,
)
.annotate(is_favorite=Exists(subquery))
)
return paginate(results_object=modules, cursor=cursor)

View File

@@ -169,12 +169,11 @@ class YourWorkQuery:
# issues
issues = await sync_to_async(list)(
Issue.objects.filter(workspace__slug=slug)
Issue.issue_objects.filter(workspace__slug=slug)
.filter(
Q(
project__project_projectmember__member=info.context.user,
project__project_projectmember__is_active=True,
# state__group__in=["unstarted", "started"],
assignees__in=[info.context.user],
),
)

View File

@@ -39,6 +39,7 @@ class CycleType:
updated_at: datetime
total_issues: int
completed_issues: int
is_favorite: bool
owned_by: Optional[UserType]
@strawberry.field

View File

@@ -41,6 +41,7 @@ class ModuleType:
logo_props: Optional[JSON]
total_issues: int
completed_issues: int
is_favorite: bool
lead: Optional[UserType]
@strawberry.field

View File

@@ -14,6 +14,8 @@ import {
} from "@/helpers/authentication.helper";
import { cn } from "@/helpers/common.helper";
import { checkEmailValidity } from "@/helpers/string.helper";
// plane web services
import mobileAuthService from "@/plane-web/services/mobile.service";
// services
import { AuthService } from "@/services/auth.service";
@@ -47,9 +49,28 @@ export const MobileAuthEmailValidationForm: FC<TMobileAuthEmailValidationForm> =
const handleFormSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
let isUserSignOut = false;
try {
await mobileAuthService.currentUser();
isUserSignOut = true;
} catch (error) {
console.error(error);
}
let isEmailShouldBeVerified = true;
if (isUserSignOut) {
try {
await mobileAuthService.signOut();
} catch (error) {
console.error(error);
isEmailShouldBeVerified = false;
}
}
if (!isEmailShouldBeVerified) return;
handleEmail(email);
setIsSubmitting(true);
const payload: IEmailCheckData = {
email: email,
};

View File

@@ -3,6 +3,8 @@
import { FC, useEffect, useState } from "react";
import { observer } from "mobx-react";
import { useSearchParams } from "next/navigation";
// components
import { AuthBanner } from "@/components/account";
// helpers
import {
authErrorHandler,
@@ -11,12 +13,8 @@ import {
EErrorAlertType,
TAuthErrorInfo,
} from "@/helpers/authentication.helper";
// components
import { AuthBanner } from "@/components/account";
// hooks
import { useInstance } from "@/hooks/store";
// services
import { AuthService } from "@/services/auth.service";
// plane web components
import {
MobileTermsAndConditions,
@@ -24,6 +22,8 @@ import {
MobileAuthUniqueCodeForm,
MobileAuthPasswordForm,
} from "@/plane-web/components/mobile";
// services
import { AuthService } from "@/services/auth.service";
// service initialization
const authService = new AuthService();
@@ -53,7 +53,7 @@ const UNIQUE_CODE_ERROR_CODES = [
const PASSWORD_ERROR_CODES = [EAuthenticationErrorCodes.AUTHENTICATION_FAILED_SIGN_IN];
export const AuthRoot: FC = observer((props) => {
export const AuthRoot: FC = observer(() => {
// router
const searchParams = useSearchParams();
// query params

View File

@@ -0,0 +1,36 @@
import axios, { AxiosInstance } from "axios";
import { IUser } from "@plane/types";
// helpers
import { API_BASE_URL } from "@/helpers/common.helper";
export class MobileAuthService {
axiosInstance: AxiosInstance;
constructor() {
this.axiosInstance = axios.create({
baseURL: API_BASE_URL,
withCredentials: true,
});
}
async currentUser(): Promise<IUser> {
return this.axiosInstance
.get("/api/users/me/")
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async signOut(): Promise<void> {
return this.axiosInstance
.post("/auth/mobile/sign-out/", {})
.then((response) => response.data)
.catch((error) => {
throw error;
});
}
}
const mobileAuthService = new MobileAuthService();
export default mobileAuthService;