diff --git a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/config/DevDataInitializer.java b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/config/DevDataInitializer.java index ceb321fbbf..93d3b264f0 100644 --- a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/config/DevDataInitializer.java +++ b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/config/DevDataInitializer.java @@ -19,6 +19,9 @@ */ package org.apache.airavata.research.service.config; +import static org.apache.airavata.research.service.enums.AuthorRoleEnum.PRIMARY; +import static org.apache.airavata.research.service.enums.StateEnum.ACTIVE; + import java.util.HashSet; import java.util.Set; import org.apache.airavata.research.service.enums.PrivacyEnum; @@ -26,6 +29,7 @@ import org.apache.airavata.research.service.model.entity.DatasetResource; import org.apache.airavata.research.service.model.entity.Project; import org.apache.airavata.research.service.model.entity.RepositoryResource; +import org.apache.airavata.research.service.model.entity.ResourceAuthor; import org.apache.airavata.research.service.model.entity.Tag; import org.apache.airavata.research.service.model.repo.ProjectRepository; import org.apache.airavata.research.service.model.repo.ResourceRepository; @@ -54,7 +58,7 @@ public DevDataInitializer( } private void createProject( - String name, String description, String repoUrl, String datasetUrl, String[] tags, String user) { + String name, String description, String repoUrl, String datasetUrl, String[] tags, Set authors) { Set tagSet = new HashSet<>(); for (String tag : tags) { Tag t = tagRepository.findByValue(tag); @@ -68,12 +72,6 @@ private void createProject( } } - Set authors = new HashSet<>() { - { - add(user); - } - }; - RepositoryResource repo = new RepositoryResource(); repo.setName(name); repo.setDescription(description); @@ -82,7 +80,16 @@ private void createProject( repo.setStatus(StatusEnum.VERIFIED); repo.setPrivacy(PrivacyEnum.PUBLIC); repo.setTags(tagSet); - repo.setAuthors(authors); + repo.setState(ACTIVE); + Set repoResourceAuthors = new HashSet<>(); + for (String author : authors) { + ResourceAuthor a = new ResourceAuthor(); + a.setResource(repo); + a.setRole(PRIMARY); + a.setAuthorId(author); + repoResourceAuthors.add(a); + } + repo.setAuthors(repoResourceAuthors); repo = resourceRepository.save(repo); DatasetResource dataset = new DatasetResource(); @@ -93,14 +100,24 @@ private void createProject( dataset.setStatus(StatusEnum.VERIFIED); dataset.setPrivacy(PrivacyEnum.PUBLIC); dataset.setTags(tagSet); - dataset.setAuthors(authors); + dataset.setState(ACTIVE); + Set datasetResourceAuthors = new HashSet<>(); + for (String author : authors) { + ResourceAuthor a = new ResourceAuthor(); + a.setResource(repo); + a.setRole(PRIMARY); + a.setAuthorId(author); + datasetResourceAuthors.add(a); + } + dataset.setAuthors(datasetResourceAuthors); dataset = resourceRepository.save(dataset); Project project = new Project(); project.setRepositoryResource(repo); project.getDatasetResources().add(dataset); project.setName(name); - project.setOwnerId(user); + project.setState(ACTIVE); + project.setOwnerId(String.join(", ", authors.stream().toString())); projectRepository.save(project); System.out.println("Initialized Project with id: " + project.getId()); @@ -108,18 +125,21 @@ private void createProject( @Override public void run(String... args) { + System.out.println("HRSDSF"); if (projectRepository.count() > 0) { System.out.println("Dev data already initialized. Skipping initialization."); return; } + System.out.println("Initializing dev data..."); + createProject( "Bio-realistic multiscale simulations of cortical circuits", "Running the AllenAI V1 model, with thalamacortical (LGN) and background (BKG) inputs", "https://github.com/cyber-shuttle/allenai-v1", "allenai-v1", new String[] {"neurodata25", "allenai", "visual_cortex"}, - "Anton Arkhipov, Laura Green"); + Set.of("Anton Arkhipov", "Laura Green")); createProject( "Apache Cerebrum", @@ -127,7 +147,7 @@ public void run(String... args) { "https://github.com/cyber-shuttle/airavata-cerebrum", "apache-airavata-cerebrum", new String[] {"neurodata25", "apache", "cerebrum"}, - "Sriram Chockalingam"); + Set.of("Sriram Chockalingam")); createProject( "Spatio-temporal dynamics of sleep in large-scale brain models", @@ -135,7 +155,7 @@ public void run(String... args) { "https://github.com/cyber-shuttle/whole-brain-public", "bazhlab-whole-brain", new String[] {"neurodata25", "bazhlab", "whole-brain"}, - "Maxim Bazhenov, Gabriela Navas Zuloaga"); + Set.of("Maxim Bazhenov", "Gabriela Navas Zuloaga")); createProject( "Biologically Constrained RNNs", @@ -143,7 +163,7 @@ public void run(String... args) { "https://github.com/cyber-shuttle/biologicalRNNs", "hchoilab-biologicalRNNs", new String[] {"neurodata25", "hchoilab", "biological-rnn"}, - "Hannah Choi, Aishwarya Balwani"); + Set.of("Hannah Choi", "Aishwarya Balwani")); createProject( "One-hot Generalized Linear Model for Switching Brain State Discovery", @@ -151,7 +171,7 @@ public void run(String... args) { "https://github.com/cyber-shuttle/onehot-hmmglm", "brainml-onehot-hmmglm", new String[] {"neurodata25", "brainml", "hmm-glm"}, - "Anqi Wu, Chengrui Li"); + Set.of("Anqi Wu", "Chengrui Li")); createProject( "Scaling up neural data analysis with torch_brain and temporaldata", @@ -159,7 +179,7 @@ public void run(String... args) { "https://github.com/cyber-shuttle/neurodata25_torchbrain_notebooks", "nerdslab-neurodata25", new String[] {"neurodata25", "nerdslab", "torch_brain", "temporaldata"}, - "Eva Dyer, Vinam Arora, Mahato Shivashriganesh"); + Set.of("Eva Dyer, Vinam Arora", "Mahato Shivashriganesh")); createProject( "Bridge the Gap between the Structure and Function in the Brain", @@ -167,7 +187,7 @@ public void run(String... args) { "https://github.com/cyber-shuttle/neuroaihub-netformer", "neuroaihub-netformer", new String[] {"neurodata25", "neuroaihub", "netformer"}, - "Lu Mi"); + Set.of("Lu Mi")); createProject( "Computing with Neural Oscillators", @@ -175,7 +195,7 @@ public void run(String... args) { "https://github.com/cyber-shuttle/imamlab-neural-oscillators", "imamlab-neurodata25", new String[] {"neurodata25", "imamlab", "neural-oscillators"}, - "Nabil Imam, Nand Chandravadia"); + Set.of("Nabil Imam, Nand Chandravadia")); createProject( "Getting started with Cybershuttle", @@ -183,7 +203,7 @@ public void run(String... args) { "https://github.com/cyber-shuttle/cybershuttle-reference", "cybershuttle-reference", new String[] {"cybershuttle", "apache-airavata", "reference"}, - "Suresh Marru"); + Set.of("Suresh Marru")); createProject( "Malicious URL Detector", @@ -191,7 +211,7 @@ public void run(String... args) { "https://github.com/airavata-courses/malicious-url-detector", "airavata-courses-malicious-url-detector", new String[] {"airavata-courses", "spring-2025"}, - "Krish Katariya, Jesse Gong, Shreyas Arisa, Devin Fromond"); + Set.of("Krish Katariya", "Jesse Gong", "Shreyas Arisa", "Devin Fromond")); createProject( "Deepseek Remote Execution", @@ -199,7 +219,7 @@ public void run(String... args) { "https://github.com/ZhenmeiOng/proj2-llama", "airavata-courses-deepseek-chat", new String[] {"airavata-courses", "spring-2025", "llm"}, - "Yashkaran Chauhan, Zhenmei Ong, Varenya Amagowni"); + Set.of("Yashkaran Chauhan", "Zhenmei Ong", "Varenya Amagowni")); createProject( "Fast Chat", @@ -207,6 +227,6 @@ public void run(String... args) { "https://github.com/riccog/cybershuttle", "airavata-courses-fast-chat", new String[] {"airavata-courses", "spring-2025"}, - "Ricco Goss, Mason Graham, Talam, Ruchira"); + Set.of("Ricco Goss", "Mason Graham", "Talam", "Ruchira")); } } diff --git a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/dto/CreateResourceRequest.java b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/dto/CreateResourceRequest.java index b3898febbe..c8b056640c 100644 --- a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/dto/CreateResourceRequest.java +++ b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/dto/CreateResourceRequest.java @@ -21,6 +21,7 @@ import java.util.Set; import org.apache.airavata.research.service.enums.PrivacyEnum; +import org.apache.airavata.research.service.model.entity.ResourceAuthor; public class CreateResourceRequest { @@ -28,7 +29,7 @@ public class CreateResourceRequest { public String description; public String headerImage; Set tags; - Set authors; + Set authors; PrivacyEnum privacy; public PrivacyEnum getPrivacy() { @@ -39,11 +40,11 @@ public void setPrivacy(PrivacyEnum privacy) { this.privacy = privacy; } - public Set getAuthors() { + public Set getAuthors() { return authors; } - public void setAuthors(Set authors) { + public void setAuthors(Set authors) { this.authors = authors; } diff --git a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/enums/AuthorRoleEnum.java b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/enums/AuthorRoleEnum.java new file mode 100644 index 0000000000..f2609fe135 --- /dev/null +++ b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/enums/AuthorRoleEnum.java @@ -0,0 +1,28 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.research.service.enums; + +public enum AuthorRoleEnum { + PRIMARY, + SECONDARY, + TERTIARY, + QUATERNARY, + QUINARY +} diff --git a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/handlers/ResourceHandler.java b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/handlers/ResourceHandler.java index 0e84a2b36f..958544253d 100644 --- a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/handlers/ResourceHandler.java +++ b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/handlers/ResourceHandler.java @@ -36,6 +36,7 @@ import org.apache.airavata.research.service.model.UserContext; import org.apache.airavata.research.service.model.entity.RepositoryResource; import org.apache.airavata.research.service.model.entity.Resource; +import org.apache.airavata.research.service.model.entity.ResourceAuthor; import org.apache.airavata.research.service.model.entity.ResourceStar; import org.apache.airavata.research.service.model.entity.Tag; import org.apache.airavata.research.service.model.repo.ProjectRepository; @@ -74,14 +75,18 @@ public ResourceHandler( } public void initializeResource(Resource resource) { - Set userSet = new HashSet<>(); - for (String authorId : resource.getAuthors()) { + Set userSet = new HashSet<>(); + for (ResourceAuthor author : resource.getAuthors()) { try { - UserProfile fetchedUser = airavataService.getUserProfile(authorId); - userSet.add(fetchedUser.getUserId()); + UserProfile fetchedUser = airavataService.getUserProfile(author.getAuthorId()); + ResourceAuthor newAuthor = new ResourceAuthor(); + newAuthor.setAuthorId(fetchedUser.getUserId()); + newAuthor.setRole(author.getRole()); + userSet.add(newAuthor); } catch (Exception e) { - LOGGER.error("Error while fetching user profile with the userId: {}", authorId, e); - throw new EntityNotFoundException("Error while fetching user profile with the userId: " + authorId, e); + LOGGER.error("Error while fetching user profile with the userId: {}", author.getAuthorId(), e); + throw new EntityNotFoundException( + "Error while fetching user profile with the userId: " + author.getAuthorId(), e); } } @@ -113,10 +118,10 @@ public void transferResourceRequestFields(Resource resource, CreateResourceReque // check that the logged in author is at least one of the authors making the request String currentUserId = UserContext.userId(); boolean found = false; - for (String authorId : createResourceRequest.getAuthors()) { - if (authorId.equalsIgnoreCase(currentUserId)) { + for (ResourceAuthor author : createResourceRequest.getAuthors()) { + author.setAuthorId(author.getAuthorId().toLowerCase()); + if (author.getAuthorId().equalsIgnoreCase(currentUserId)) { found = true; - break; } } if (!found) { @@ -126,9 +131,7 @@ public void transferResourceRequestFields(Resource resource, CreateResourceReque resource.setName(createResourceRequest.getName()); resource.setDescription(createResourceRequest.getDescription()); - resource.setAuthors(createResourceRequest.getAuthors().stream() - .map(String::toLowerCase) - .collect(Collectors.toSet())); + resource.setAuthors(createResourceRequest.getAuthors()); Set tagsSet = new HashSet<>(); for (String tag : createResourceRequest.getTags()) { org.apache.airavata.research.service.model.entity.Tag t = @@ -162,8 +165,8 @@ public Resource modifyResource(ModifyResourceRequest resourceRequest) { // ensure that the user making the request is one of the current authors boolean found = false; - for (String authorId : resource.getAuthors()) { - if (authorId.equalsIgnoreCase(UserContext.userId())) { + for (ResourceAuthor author : resource.getAuthors()) { + if (author.getAuthorId().equalsIgnoreCase(UserContext.userId())) { found = true; break; } diff --git a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/Resource.java b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/Resource.java index 97814f3163..b78cf72942 100644 --- a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/Resource.java +++ b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/Resource.java @@ -19,10 +19,9 @@ */ package org.apache.airavata.research.service.model.entity; +import com.fasterxml.jackson.annotation.JsonManagedReference; import jakarta.persistence.CascadeType; -import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; -import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; import jakarta.persistence.EntityListeners; import jakarta.persistence.EnumType; @@ -35,6 +34,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinTable; import jakarta.persistence.ManyToMany; +import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import java.time.Instant; import java.util.HashSet; @@ -69,10 +69,9 @@ public abstract class Resource { @Column(nullable = false) private String headerImage; - @ElementCollection(fetch = FetchType.EAGER) - @CollectionTable(name = "resource_authors", joinColumns = @JoinColumn(name = "resource_id")) - @Column(name = "author_id") - private Set authors = new HashSet<>(); + @OneToMany(mappedBy = "resource", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true) + @JsonManagedReference + private Set authors = new HashSet<>(); @ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.EAGER) @JoinTable( @@ -135,11 +134,12 @@ public void setDescription(String description) { this.description = description; } - public Set getAuthors() { + public Set getAuthors() { return authors; } - public void setAuthors(Set authors) { + public void setAuthors(Set authors) { + authors.forEach(author -> author.setResource(this)); this.authors = authors; } diff --git a/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/ResourceAuthor.java b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/ResourceAuthor.java new file mode 100644 index 0000000000..71ff006c2c --- /dev/null +++ b/modules/research-framework/research-service/src/main/java/org/apache/airavata/research/service/model/entity/ResourceAuthor.java @@ -0,0 +1,79 @@ +/** +* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ +package org.apache.airavata.research.service.model.entity; + +import com.fasterxml.jackson.annotation.JsonBackReference; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import org.apache.airavata.research.service.enums.AuthorRoleEnum; +import org.hibernate.annotations.UuidGenerator; + +@Entity +@Table(name = "resource_authors") +public class ResourceAuthor { + @Id + @GeneratedValue + @UuidGenerator + private String id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "resource_id", nullable = false) + @JsonBackReference + private Resource resource; + + @Column(name = "author_id") + private String authorId; + + @Enumerated(EnumType.STRING) + @Column(nullable = false) + private AuthorRoleEnum role; + + public Resource getResource() { + return resource; + } + + public void setResource(Resource resource) { + this.resource = resource; + } + + public String getAuthorId() { + return authorId; + } + + public void setAuthorId(String authorId) { + this.authorId = authorId; + } + + public AuthorRoleEnum getRole() { + return role; + } + + public void setRole(AuthorRoleEnum role) { + this.role = role; + } +}