diff --git a/commit/commit/code_analysis/apis.py b/commit/commit/code_analysis/apis.py index 483995b..c26ee1a 100644 --- a/commit/commit/code_analysis/apis.py +++ b/commit/commit/code_analysis/apis.py @@ -31,7 +31,8 @@ def find_all_occurrences_of_whitelist(path: str, app_name: str): ## Comment out later # if file.endswith('party.py'): - indexes,line_nos = find_indexes_of_whitelist(file_content, no_of_occurrences) + indexes,line_nos,no_of_occurrences = find_indexes_of_whitelist(file_content, no_of_occurrences) + api_count += no_of_occurrences apis = get_api_details(file, file_content, indexes,line_nos, path) api_details.extend(apis) @@ -43,17 +44,70 @@ def find_all_occurrences_of_whitelist(path: str, app_name: str): def find_indexes_of_whitelist(file_content: str, count: int): ''' - Find indexes of @frappe.whitelist in the file content + Find indexes of @frappe.whitelist in the file content, + ensuring it's not commented out or inside a string. ''' + def is_in_string_or_comment(file_content, index): + # State variables + in_single_quote = False + in_double_quote = False + in_comment = False + in_triple_single_quote = False + in_triple_double_quote = False + + i = 0 + while i < index: + char = file_content[i] + + # Handle triple single-quoted strings + if file_content[i:i+3] == "'''" and not in_double_quote: + if in_triple_single_quote: + in_triple_single_quote = False + i += 2 + else: + in_triple_single_quote = True + i += 2 + # Handle triple double-quoted strings + elif file_content[i:i+3] == '"""' and not in_single_quote: + if in_triple_double_quote: + in_triple_double_quote = False + i += 2 + else: + in_triple_double_quote = True + i += 2 + # Handle single-quoted strings + elif char == "'" and not in_double_quote and not in_triple_single_quote and not in_triple_double_quote: + in_single_quote = not in_single_quote + # Handle double-quoted strings + elif char == '"' and not in_single_quote and not in_triple_single_quote and not in_triple_double_quote: + in_double_quote = not in_double_quote + # Handle single-line comments + elif char == '#' and not in_single_quote and not in_double_quote and not in_triple_single_quote and not in_triple_double_quote: + in_comment = True + # Handle end of line for single-line comments + elif char == '\n': + in_comment = False + + i += 1 + + return in_single_quote or in_double_quote or in_comment or in_triple_single_quote or in_triple_double_quote + indexes = [] line_nos = [] - for i in range(count): - index = file_content.find('@frappe.whitelist', indexes[i - 1] + len('@frappe.whitelist') if i > 0 else 0) - indexes.append(index) - line_nos.append((file_content.count('\n', 0, index)+1)) - + actual_count = count - return indexes, line_nos + start = 0 + while actual_count > 0: + index = file_content.find('@frappe.whitelist', start) + if index == -1: + break + if not is_in_string_or_comment(file_content, index): + indexes.append(index) + line_nos.append(file_content.count('\n', 0, index) + 1) + actual_count -= 1 + start = index + len('@frappe.whitelist') + + return indexes, line_nos, actual_count def get_api_details(file, file_content: str, indexes: list,line_nos:list, path: str): ''' @@ -98,7 +152,10 @@ def get_whitelist_details(file_content: str, index: int): ''' whitelist_end_index = file_content.find(')', index) whitelisted_content = file_content[index:whitelist_end_index + 1] - args = whitelisted_content.split("(")[1].split(")")[0].split(",") + if "(" in whitelisted_content and ")" in whitelisted_content: + args = whitelisted_content.split("(")[1].split(")")[0].split(",") + else: + args = [] request_types = [] xss_safe = False allow_guest = False @@ -159,7 +216,10 @@ def extract_arguments_from_def(api_def: str): ''' Extract arguments from def ''' - arguments_with_types_defaults = api_def.split("(")[1].split(")")[0].split(",") + if "(" not in api_def or ")" not in api_def: + arguments_with_types_defaults = [] + else: + arguments_with_types_defaults = api_def.split("(")[1].split(")")[0].split(",") arguments = [] for arg in arguments_with_types_defaults: @@ -202,7 +262,28 @@ def find_function_end_lines(source_code: str,function_name:str): for node in ast.walk(tree): if isinstance(node, ast.FunctionDef): - end_line = node.end_lineno - function_end_lines[node.name] = end_line + decorators = get_decorators(node) + if 'whitelist' in decorators: + end_line = node.end_lineno + function_end_lines[node.name] = end_line + + return function_end_lines.get(function_name,0) - return function_end_lines.get(function_name,0) \ No newline at end of file +def get_decorator_name(node): + if isinstance(node, ast.Call): + if isinstance(node.func, ast.Name): + return node.func.id + elif isinstance(node.func, ast.Attribute): + return node.func.attr + elif isinstance(node, ast.Attribute): + return node.attr + else: + return None + +def get_decorators(node): + decorators = [] + for decorator in node.decorator_list: + decorator_name = get_decorator_name(decorator) + if decorator_name is not None: + decorators.append(decorator_name) + return decorators \ No newline at end of file diff --git a/commit/commit/doctype/commit_project_branch/commit_project_branch.py b/commit/commit/doctype/commit_project_branch/commit_project_branch.py index c35af44..d78db59 100644 --- a/commit/commit/doctype/commit_project_branch/commit_project_branch.py +++ b/commit/commit/doctype/commit_project_branch/commit_project_branch.py @@ -17,9 +17,16 @@ class CommitProjectBranch(Document): def before_insert(self): self.path_to_folder = self.get_path_to_folder() self.create_branch_folder() - self.clone_repo() - self.get_modules() - self.find_all_apis() + + def after_insert(self): + frappe.enqueue( + method = background_fetch_process, + is_async = True, + job_name="Fetch Project Branch", + enqueue_after_commit = True, + at_front = True, + project_branch = self.name + ) def create_branch_folder(self): if not os.path.exists(self.path_to_folder): @@ -44,7 +51,6 @@ def clone_repo(self): # print("Cloned repo") self.last_fetched = frappe.utils.now_datetime() self.commit_hash = repo.head.object.hexsha - pass def fetch_repo(self): repo = git.Repo(self.path_to_folder) @@ -107,10 +113,58 @@ def get_doctypes_in_module(self, module): def on_trash(self): # Delete the folder - if os.path.exists(self.path_to_folder): + if self.path_to_folder and os.path.exists(self.path_to_folder): shutil.rmtree(self.path_to_folder) - pass +def background_fetch_process(project_branch): + try: + doc = frappe.get_doc("Commit Project Branch", project_branch) + frappe.publish_realtime('commit_branch_clone_repo', + { + 'branch_name': doc.branch_name, + 'project': doc.project, + 'text': "Cloning repository...", + 'is_completed': False + }, user=frappe.session.user) + + + doc.clone_repo() + frappe.publish_realtime('commit_branch_get_modules', + { + 'branch_name': doc.branch_name, + 'project': doc.project, + 'text': "Getting all modules for your app...", + 'is_completed': False + }, user=frappe.session.user) + + doc.get_modules() + + frappe.publish_realtime('commit_branch_find_apis', + { + 'branch_name': doc.branch_name, + 'project': doc.project, + 'text': "Finding all APIs...", + 'is_completed': False + }, user=frappe.session.user) + + doc.find_all_apis() + doc.save() + + frappe.publish_realtime("commit_project_branch_created", { + 'name': doc.name, + 'branch_name': doc.branch_name, + 'project': doc.project, + 'text': "Branch created successfully.", + 'is_completed': True + }, user=frappe.session.user) + + + + except frappe.DoesNotExistError: + # throw the error and delete the document + frappe.throw("Project Branch not found") + frappe.delete_doc("Commit Project Branch", project_branch) + @frappe.whitelist(allow_guest=True) def fetch_repo(doc): diff --git a/commit/commit/github_connect/connected_app.py b/commit/commit/github_connect/connected_app.py deleted file mode 100644 index 78bfa88..0000000 --- a/commit/commit/github_connect/connected_app.py +++ /dev/null @@ -1,7 +0,0 @@ -import frappe - -@frappe.whitelist(allow_guest=True) -def trial(): - connected_app = frappe.get_doc("Connected App", "fa306fb623") - return connected_app.initiate_web_application_flow() - \ No newline at end of file diff --git a/dashboard/index.html b/dashboard/index.html index 250ced6..2eaa979 100644 --- a/dashboard/index.html +++ b/dashboard/index.html @@ -17,6 +17,10 @@
+ diff --git a/dashboard/package.json b/dashboard/package.json index 84eaafd..d713012 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -40,7 +40,7 @@ "class-variance-authority": "^0.6.1", "clsx": "^1.2.1", "cmdk": "^0.2.0", - "frappe-react-sdk": "^1.2.8", + "frappe-react-sdk": "^1.7.0", "install": "^0.13.0", "lodash": "^4.17.21", "lodash.isplainobject": "^4.0.6", diff --git a/dashboard/src/App.tsx b/dashboard/src/App.tsx index 5ce8661..c9d2db2 100644 --- a/dashboard/src/App.tsx +++ b/dashboard/src/App.tsx @@ -8,8 +8,18 @@ import { CreateERD } from './pages/features/erd/meta/CreateERDForMeta' function App() { + const getSiteName = () => { + // @ts-ignore + if (window.frappe?.boot?.versions?.frappe && (window.frappe.boot.versions.frappe.startsWith('15') || window.frappe.boot.versions.frappe.startsWith('16'))) { + // @ts-ignore + return window.frappe?.boot?.sitename ?? import.meta.env.VITE_SITE_NAME + } + return import.meta.env.VITE_SITE_NAME + + } + return ( - + {/* */} diff --git a/dashboard/src/components/features/api_viewer/APIList.tsx b/dashboard/src/components/features/api_viewer/APIList.tsx index 95cce1f..964788d 100644 --- a/dashboard/src/components/features/api_viewer/APIList.tsx +++ b/dashboard/src/components/features/api_viewer/APIList.tsx @@ -65,7 +65,7 @@ export const APIList = ({ apiList, app_name, branch_name, setSelectedEndpoint }: {/* fixed height container */} -
+
diff --git a/dashboard/src/components/features/projects/Branch/CreateBranchModal.tsx b/dashboard/src/components/features/projects/Branch/CreateBranchModal.tsx index f054676..2b212c8 100644 --- a/dashboard/src/components/features/projects/Branch/CreateBranchModal.tsx +++ b/dashboard/src/components/features/projects/Branch/CreateBranchModal.tsx @@ -5,8 +5,10 @@ import { Label } from "@/components/ui/label" import { Button } from '@/components/ui/button' import { ProjectData, ProjectWithBranch } from "../Projects" import { useToast } from "@/components/ui/use-toast" -import { useFrappeCreateDoc } from "frappe-react-sdk" +import { useFrappeCreateDoc, useFrappeEventListener } from "frappe-react-sdk" import { KeyedMutator } from "swr" +import { useState } from "react" +import { ErrorBanner } from "@/components/common/ErrorBanner/ErrorBanner" type FormFields = { project: string, @@ -19,9 +21,13 @@ export interface BranchProps { message: ProjectData[]; }> setBranch: React.Dispatch> + setOpen: React.Dispatch> } -const CreateBranchModal = ({ project, mutate, setBranch }: BranchProps) => { +const CreateBranchModal = ({ project, mutate, setBranch, setOpen }: BranchProps) => { + + const [desc, setDesc] = useState("Please enter the branch name.") + const [eventLoading, setEventLoading] = useState(false) const { toast } = useToast() const methods = useForm({ @@ -30,20 +36,56 @@ const CreateBranchModal = ({ project, mutate, setBranch }: BranchProps) => { } }) + const { createDoc, reset, loading, error } = useFrappeCreateDoc() + + const branchName = methods.watch('branch_name') + + + useFrappeEventListener('commit_branch_clone_repo', (data) => { + if (data.branch_name === branchName && data.project === project.name) { + setDesc("Cloning repository...") + } + }) + + useFrappeEventListener('commit_branch_get_modules', (data) => { + if (data.branch_name === branchName && data.project === project.name) { + setDesc("Getting modules for app...") + } + }) + + useFrappeEventListener('commit_branch_find_apis', (data) => { + if (data.branch_name === branchName && data.project === project.name) { + setDesc("Finding all APIs...") + } + }) + + useFrappeEventListener('commit_project_branch_created', (data) => { + if (data.branch_name === branchName && data.project === project.name) { + setDesc("Branch created successfully.") + handleClose(data.name, data.branch_name) + } + }) + + const handleClose = (name: string, branch_name: string) => { + setEventLoading(false) + setBranch(name) + methods.reset() + toast({ + description: `Branch ${branch_name} added for ${project.app_name}` + }) + reset() + setDesc("") + setOpen(false) + } const { handleSubmit, register } = methods; - const { createDoc, reset } = useFrappeCreateDoc() const onSubmit: SubmitHandler = (data) => { createDoc('Commit Project Branch', data) .then((doc) => { - setBranch(doc.name) - methods.reset() mutate() - toast({ - description: `Branch ${doc.branch_name} added for ${project.app_name}`, - }) - reset() + setBranch(doc.name) + setEventLoading(true) }) } @@ -53,9 +95,10 @@ const CreateBranchModal = ({ project, mutate, setBranch }: BranchProps) => { Adding Branch of{' '}{project.display_name} - Please enter the branch name. + {desc} + {error && }
@@ -67,9 +110,12 @@ const CreateBranchModal = ({ project, mutate, setBranch }: BranchProps) => { className="mb-3 p-3 w-full" /> - +
diff --git a/dashboard/src/components/features/projects/Projects.tsx b/dashboard/src/components/features/projects/Projects.tsx index d082e42..c8309d7 100644 --- a/dashboard/src/components/features/projects/Projects.tsx +++ b/dashboard/src/components/features/projects/Projects.tsx @@ -7,8 +7,8 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { CommitProject } from "@/types/commit/CommitProject" import { CommitProjectBranch } from "@/types/commit/CommitProjectBranch" import { AvatarImage } from "@radix-ui/react-avatar" -import { useFrappeEventListener, useFrappeGetCall } from "frappe-react-sdk" -import { useEffect, useMemo, useState } from "react" +import { useFrappeGetCall } from "frappe-react-sdk" +import { useMemo, useState } from "react" import { AiOutlineApi } from "react-icons/ai" import { BsDatabase } from "react-icons/bs" import { MdAdd, MdAddBox } from "react-icons/md" @@ -36,19 +36,6 @@ export interface ProjectData extends CommitProjectBranch { } export const Projects = () => { - const [branchCreation, setBranchCreation] = useState<{ branch_name: string, project: string }[] | null>(null) - - useFrappeEventListener('creating_branch', (data) => { - console.log("fired") - setBranchCreation([branchCreation, ...data]) - }) - - useFrappeEventListener('branch_created', (data) => { - console.log("fired2") - const filtered = branchCreation?.filter((doc) => doc.branch_name !== data.branch_name) - setBranchCreation(filtered ?? null) - }) - const isCreateAccess = isSystemManager() const { data, error, isLoading, mutate } = useFrappeGetCall<{ message: ProjectData[] }>('commit.api.commit_project.commit_project.get_project_list_with_branches') @@ -71,29 +58,28 @@ export const Projects = () => { - - {isCreateAccess && + {isCreateAccess && + - - } - - + + } -
    +
      {data.message.map((org: ProjectData) => { - return + return })}
    @@ -103,17 +89,16 @@ export const Projects = () => { } -export const OrgComponent = ({ org, mutate, branchCreation }: { +export const OrgComponent = ({ org, mutate }: { org: ProjectData, mutate: KeyedMutator<{ message: ProjectData[]; - }>, branchCreation: { - branch_name: string; - project: string; - }[] | null + }> }) => { const isCreateAccess = isSystemManager() + const [createProject, setCreateProject] = useState(false) + return (
    @@ -123,7 +108,7 @@ export const OrgComponent = ({ org, mutate, branchCreation }: { {isCreateAccess &&
    - +
    }
    + + {org?.projects?.length === 0 &&
    No Projects Found, Click to add a new project. +
    }
    {org.projects.map((project => { return ( - + ) } ))}
    - ) } @@ -158,15 +146,11 @@ export interface ProjectCardProps { org: ProjectData mutate: KeyedMutator<{ message: ProjectData[]; - }>, - branchCreation: { - branch_name: string; - project: string; - }[] | null + }> } -export const ProjectCard = ({ project, org, mutate, branchCreation }: ProjectCardProps) => { +export const ProjectCard = ({ project, org, mutate }: ProjectCardProps) => { const navigate = useNavigate() @@ -181,14 +165,12 @@ export const ProjectCard = ({ project, org, mutate, branchCreation }: ProjectCar return project.display_name.split('_').map((word) => word[0]).join('').toUpperCase() }, [project]) - const creationBranchName = useMemo(() => { - const find = branchCreation?.find((doc) => doc.project === project.name) - if (find) return find.branch_name - return undefined - }, [branchCreation, project]) - const isCreateAccess = isSystemManager() + const [open, setOpen] = useState(false) + + const [selectOpen, setSelectOpen] = useState(false) + return (
  • @@ -209,24 +191,21 @@ export const ProjectCard = ({ project, org, mutate, branchCreation }: ProjectCar

    {org.organization_name}

    {project.description} - {creationBranchName && - - } - {/* */} -
    - + + +
  • ) } - - -const BranchCreationLoadingState = ({ branch_name }: { branch_name: string }) => { - - const [currentLoadingText, setCurrentLoadingText] = useState("") - const loadingTexts = [ - "May the forks be with you...", - "A commit a day keeps the mobs away...", - "Tickling the servers...", - "Why so serious?...", - "It's not you. It's me...", - "Counting backwards from Infinity...", - "Don't panic...", - "Don't break your screen yet!...", - "I swear it's almost done...", - "Let's take a mindfulness minute...", - ] - - useEffect(() => { - const changeInterval = setInterval(() => setCurrentLoadingText(loadingTexts[Math.floor(Math.random() * 10)]), 3000) - - return () => { - clearInterval(changeInterval); - } - }, []) - - return ( -
    -
    -
    -
    - {currentLoadingText} - {'Loading branch '} - {branch_name} -
    -
    - ) -} - -export default BranchCreationLoadingState - diff --git a/dashboard/yarn.lock b/dashboard/yarn.lock index f9fb09c..1aa5e8f 100644 --- a/dashboard/yarn.lock +++ b/dashboard/yarn.lock @@ -2646,7 +2646,7 @@ frappe-js-sdk@^1.5.0: dependencies: axios "^1.6.7" -frappe-react-sdk@^1.2.8: +frappe-react-sdk@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/frappe-react-sdk/-/frappe-react-sdk-1.7.0.tgz#6d7430f2e606dbe733ffbe1fda7566fc815b6a45" integrity sha512-UBch8j7nWCkYWQjGG7tOds5tFif/hFdAbg8T+FlOiJAyFmtpbVdBOsWx2ime9aBkNa2U8zqjOkQLLIvL2/qOrg== @@ -4415,6 +4415,7 @@ stdin-discarder@^0.1.0: bl "^5.0.0" "string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: + name string-width-cjs version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==