This is an automated email from the ASF dual-hosted git repository.

ganning pushed a commit to branch public-private-resources
in repository https://gitbox.apache.org/repos/asf/airavata-portals.git

commit a38ed8309b027ee04bd39dcebe8e8ddbe41f6b43
Author: ganning127 <[email protected]>
AuthorDate: Mon Jun 30 23:57:42 2025 -0700

    support public/private resources
---
 .gitignore                                         |   0
 airavata-research-portal/package-lock.json         | 174 +++++++++------------
 .../src/components/add/AddGitUrl.tsx               |  13 ++
 .../src/components/add/DatasetSearch.tsx           |   2 +-
 .../src/components/add/RepoSearch.tsx              |   2 +-
 .../src/components/home/ResourceCard.tsx           |  12 +-
 .../resources/PrivateResourceTooltip.tsx           |  10 ++
 .../src/components/resources/ResourceDetails.tsx   |  21 ++-
 .../src/interfaces/PrivacyEnum.ts                  |   8 +
 9 files changed, 135 insertions(+), 107 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..e69de29bb
diff --git a/airavata-research-portal/package-lock.json 
b/airavata-research-portal/package-lock.json
index dc7728207..d5639e572 100644
--- a/airavata-research-portal/package-lock.json
+++ b/airavata-research-portal/package-lock.json
@@ -816,6 +816,84 @@
         "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
         "wrap-ansi": "^8.1.0",
         "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "5.0.1",
+          "resolved": 
"https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz";,
+          "integrity": 
"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+          "dev": true
+        },
+        "emoji-regex": {
+          "version": "8.0.0",
+          "resolved": 
"https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz";,
+          "integrity": 
"sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+          "dev": true
+        },
+        "string-width-cjs": {
+          "version": "npm:[email protected]",
+          "resolved": 
"https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz";,
+          "integrity": 
"sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+          "dev": true,
+          "requires": {
+            "emoji-regex": "^8.0.0",
+            "is-fullwidth-code-point": "^3.0.0",
+            "strip-ansi": "^6.0.1"
+          },
+          "dependencies": {
+            "strip-ansi": {
+              "version": "6.0.1",
+              "resolved": 
"https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz";,
+              "integrity": 
"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+              "dev": true,
+              "requires": {
+                "ansi-regex": "^5.0.1"
+              }
+            }
+          }
+        },
+        "strip-ansi-cjs": {
+          "version": "npm:[email protected]",
+          "resolved": 
"https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz";,
+          "integrity": 
"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^5.0.1"
+          }
+        },
+        "wrap-ansi-cjs": {
+          "version": "npm:[email protected]",
+          "resolved": 
"https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz";,
+          "integrity": 
"sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.0.0",
+            "string-width": "^4.1.0",
+            "strip-ansi": "^6.0.0"
+          },
+          "dependencies": {
+            "string-width": {
+              "version": "4.2.3",
+              "resolved": 
"https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz";,
+              "integrity": 
"sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+              "dev": true,
+              "requires": {
+                "emoji-regex": "^8.0.0",
+                "is-fullwidth-code-point": "^3.0.0",
+                "strip-ansi": "^6.0.1"
+              }
+            },
+            "strip-ansi": {
+              "version": "6.0.1",
+              "resolved": 
"https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz";,
+              "integrity": 
"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+              "dev": true,
+              "requires": {
+                "ansi-regex": "^5.0.1"
+              }
+            }
+          }
+        }
       }
     },
     "@jridgewell/gen-mapping": {
@@ -3699,40 +3777,6 @@
         "strip-ansi": "^7.0.1"
       }
     },
-    "string-width-cjs": {
-      "version": "npm:[email protected]",
-      "resolved": 
"https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz";,
-      "integrity": 
"sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-      "dev": true,
-      "requires": {
-        "emoji-regex": "^8.0.0",
-        "is-fullwidth-code-point": "^3.0.0",
-        "strip-ansi": "^6.0.1"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "5.0.1",
-          "resolved": 
"https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz";,
-          "integrity": 
"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-          "dev": true
-        },
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": 
"https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz";,
-          "integrity": 
"sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-          "dev": true
-        },
-        "strip-ansi": {
-          "version": "6.0.1",
-          "resolved": 
"https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz";,
-          "integrity": 
"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^5.0.1"
-          }
-        }
-      }
-    },
     "strip-ansi": {
       "version": "7.1.0",
       "resolved": 
"https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz";,
@@ -3742,23 +3786,6 @@
         "ansi-regex": "^6.0.1"
       }
     },
-    "strip-ansi-cjs": {
-      "version": "npm:[email protected]",
-      "resolved": 
"https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz";,
-      "integrity": 
"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-      "dev": true,
-      "requires": {
-        "ansi-regex": "^5.0.1"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "5.0.1",
-          "resolved": 
"https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz";,
-          "integrity": 
"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-          "dev": true
-        }
-      }
-    },
     "strip-json-comments": {
       "version": "3.1.1",
       "resolved": 
"https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz";,
@@ -4011,51 +4038,6 @@
         }
       }
     },
-    "wrap-ansi-cjs": {
-      "version": "npm:[email protected]",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz";,
-      "integrity": 
"sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^4.0.0",
-        "string-width": "^4.1.0",
-        "strip-ansi": "^6.0.0"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "5.0.1",
-          "resolved": 
"https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz";,
-          "integrity": 
"sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
-          "dev": true
-        },
-        "emoji-regex": {
-          "version": "8.0.0",
-          "resolved": 
"https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz";,
-          "integrity": 
"sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
-          "dev": true
-        },
-        "string-width": {
-          "version": "4.2.3",
-          "resolved": 
"https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz";,
-          "integrity": 
"sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^8.0.0",
-            "is-fullwidth-code-point": "^3.0.0",
-            "strip-ansi": "^6.0.1"
-          }
-        },
-        "strip-ansi": {
-          "version": "6.0.1",
-          "resolved": 
"https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz";,
-          "integrity": 
"sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^5.0.1"
-          }
-        }
-      }
-    },
     "yallist": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz";,
diff --git a/airavata-research-portal/src/components/add/AddGitUrl.tsx 
b/airavata-research-portal/src/components/add/AddGitUrl.tsx
index 79f2a2183..94948f023 100644
--- a/airavata-research-portal/src/components/add/AddGitUrl.tsx
+++ b/airavata-research-portal/src/components/add/AddGitUrl.tsx
@@ -2,6 +2,8 @@ import {Button, Code, Input, Text, VStack} from 
"@chakra-ui/react";
 import {useState} from "react";
 import yaml from "js-yaml";
 import {CreateResourceRequest} from 
"@/interfaces/Requests/CreateResourceRequest";
+import {isPrivacyEnum, PrivacyEnum} from "@/interfaces/PrivacyEnum.ts";
+import {toaster} from "@/components/ui/toaster.tsx";
 
 export const AddGitUrl = ({
                             nextStage,
@@ -47,6 +49,16 @@ export const AddGitUrl = ({
 
       // eslint-disable-next-line @typescript-eslint/no-explicit-any
       const parsed = yaml.load(fileContent) as any;
+
+      console.log(parsed);
+      if (!isPrivacyEnum(parsed.project.privacy)) {
+        toaster.create({
+          title: "Invalid `privacy` field",
+          description: `Received '${parsed.privacy}'. Valid values are 
'PUBLIC' or 'PRIVATE'`,
+          type: "error"
+        })
+        return;
+      }
       setCreateResourceRequest({
         ...createResourceRequest,
         name: parsed.project.name,
@@ -54,6 +66,7 @@ export const AddGitUrl = ({
         description: parsed.project.description,
         tags: parsed.project.tags,
         authors: parsed.project.authors,
+        privacy: parsed.project.privacy as PrivacyEnum
       });
       nextStage();
     } catch (error) {
diff --git a/airavata-research-portal/src/components/add/DatasetSearch.tsx 
b/airavata-research-portal/src/components/add/DatasetSearch.tsx
index 915f24189..9df9f97bb 100644
--- a/airavata-research-portal/src/components/add/DatasetSearch.tsx
+++ b/airavata-research-portal/src/components/add/DatasetSearch.tsx
@@ -39,7 +39,7 @@ export const DatasetSearchInput = ({
 
     const timeout = setTimeout(async () => {
       try {
-        const response = await 
api.get(`${CONTROLLER.resources}/public/search`, {
+        const response = await api.get(`${CONTROLLER.resources}/search`, {
           params: {
             type: ResourceTypeEnum.DATASET,
             name: datasetSearch,
diff --git a/airavata-research-portal/src/components/add/RepoSearch.tsx 
b/airavata-research-portal/src/components/add/RepoSearch.tsx
index cc230b0d1..b49ceb634 100644
--- a/airavata-research-portal/src/components/add/RepoSearch.tsx
+++ b/airavata-research-portal/src/components/add/RepoSearch.tsx
@@ -60,7 +60,7 @@ const RepoSearchInput = ({
 
     const timeout = setTimeout(async () => {
       try {
-        const response = await 
api.get(`${CONTROLLER.resources}/public/search`, {
+        const response = await api.get(`${CONTROLLER.resources}/search`, {
           params: {
             type: ResourceTypeEnum.REPOSITORY,
             name: repoSearch,
diff --git a/airavata-research-portal/src/components/home/ResourceCard.tsx 
b/airavata-research-portal/src/components/home/ResourceCard.tsx
index 6846a1909..c612e959c 100644
--- a/airavata-research-portal/src/components/home/ResourceCard.tsx
+++ b/airavata-research-portal/src/components/home/ResourceCard.tsx
@@ -27,6 +27,8 @@ import {ModelCardButton} from "../models/ModelCardButton";
 import {useState} from "react";
 import {Link} from 'react-router';
 import {ResourceOptions} from "@/components/resources/ResourceOptions.tsx";
+import {PrivacyEnum} from "@/interfaces/PrivacyEnum.ts";
+import {PrivateResourceTooltip} from 
"@/components/resources/PrivateResourceTooltip.tsx";
 
 export const ResourceCard = ({
                                resource,
@@ -87,8 +89,14 @@ export const ResourceCard = ({
         <Card.Header>
           <HStack justifyContent={'space-between'} alignItems={'center'} 
flexWrap={'wrap'}>
             <Card.Title>{resource.name}</Card.Title>
-            <ResourceOptions deleteable={deletable} resource={resource} 
onDeleteSuccess={hideCardCallback}
-                             onUnStarSuccess={removeOnUnStar ? 
hideCardCallback : dummyOnUnStarSuccess}/>
+
+            <HStack alignItems={'center'}>
+              {resource.privacy === PrivacyEnum.PRIVATE &&
+                  <PrivateResourceTooltip/>
+              }
+              <ResourceOptions deleteable={deletable} resource={resource} 
onDeleteSuccess={hideCardCallback}
+                               onUnStarSuccess={removeOnUnStar ? 
hideCardCallback : dummyOnUnStarSuccess}/>
+            </HStack>
           </HStack>
         </Card.Header>
 
diff --git 
a/airavata-research-portal/src/components/resources/PrivateResourceTooltip.tsx 
b/airavata-research-portal/src/components/resources/PrivateResourceTooltip.tsx
new file mode 100644
index 000000000..160d930a2
--- /dev/null
+++ 
b/airavata-research-portal/src/components/resources/PrivateResourceTooltip.tsx
@@ -0,0 +1,10 @@
+import {Tooltip} from "@/components/ui/tooltip.tsx";
+import {IoEyeOffOutline} from "react-icons/io5";
+
+export const PrivateResourceTooltip = () => {
+  return (
+      <Tooltip content={"This resource is private and can only be seen by the 
resource's authors"}>
+        <IoEyeOffOutline/>
+      </Tooltip>
+  )
+}
\ No newline at end of file
diff --git 
a/airavata-research-portal/src/components/resources/ResourceDetails.tsx 
b/airavata-research-portal/src/components/resources/ResourceDetails.tsx
index fdb3c4a35..ee9ad28a1 100644
--- a/airavata-research-portal/src/components/resources/ResourceDetails.tsx
+++ b/airavata-research-portal/src/components/resources/ResourceDetails.tsx
@@ -54,6 +54,8 @@ import {CONTROLLER} from "@/lib/controller";
 import {DatasetSpecificDetails} from "../datasets/DatasetSpecificDetails";
 import {ResourceOptions} from "@/components/resources/ResourceOptions.tsx";
 import {toaster} from "@/components/ui/toaster.tsx";
+import {PrivacyEnum} from "@/interfaces/PrivacyEnum.ts";
+import {PrivateResourceTooltip} from 
"@/components/resources/PrivateResourceTooltip.tsx";
 
 async function getResource(id: string) {
   const response = await api.get(`${CONTROLLER.resources}/public/${id}`);
@@ -148,13 +150,18 @@ const ResourceDetails = () => {
                   {resource.name}
                 </Heading>
 
-                <ResourceOptions
-                    resource={resource}
-                    onDeleteSuccess={goToResources}
-                    deleteable={true}
-                    onUnStarSuccess={() => {
-                    }}
-                />
+                <HStack>
+                  {resource.privacy === PrivacyEnum.PRIVATE &&
+                      <PrivateResourceTooltip/>
+                  }
+                  <ResourceOptions
+                      resource={resource}
+                      onDeleteSuccess={goToResources}
+                      deleteable={true}
+                      onUnStarSuccess={() => {
+                      }}
+                  />
+                </HStack>
 
               </HStack>
 
diff --git a/airavata-research-portal/src/interfaces/PrivacyEnum.ts 
b/airavata-research-portal/src/interfaces/PrivacyEnum.ts
index be9aade1d..a23747c72 100644
--- a/airavata-research-portal/src/interfaces/PrivacyEnum.ts
+++ b/airavata-research-portal/src/interfaces/PrivacyEnum.ts
@@ -1,4 +1,12 @@
 export enum PrivacyEnum {
   PUBLIC = "PUBLIC",
   PRIVATE = "PRIVATE",
+}
+
+export function isPrivacyEnum(value: string | undefined): boolean {
+  if (value === undefined || value === null) {
+    return false;
+  }
+
+  return Object.values(PrivacyEnum).includes(value as PrivacyEnum);
 }
\ No newline at end of file

Reply via email to