View Javadoc
1   package de.dlr.shepard.neo4Core.services;
2   
3   import de.dlr.shepard.exceptions.InvalidBodyException;
4   import de.dlr.shepard.neo4Core.dao.CollectionDAO;
5   import de.dlr.shepard.neo4Core.dao.DataObjectDAO;
6   import de.dlr.shepard.neo4Core.dao.UserDAO;
7   import de.dlr.shepard.neo4Core.dao.VersionDAO;
8   import de.dlr.shepard.neo4Core.entities.Collection;
9   import de.dlr.shepard.neo4Core.entities.DataObject;
10  import de.dlr.shepard.neo4Core.entities.User;
11  import de.dlr.shepard.neo4Core.io.DataObjectIO;
12  import de.dlr.shepard.util.DateHelper;
13  import de.dlr.shepard.util.QueryParamHelper;
14  import io.quarkus.logging.Log;
15  import jakarta.enterprise.context.RequestScoped;
16  import jakarta.inject.Inject;
17  import java.util.ArrayList;
18  import java.util.Date;
19  import java.util.List;
20  import java.util.UUID;
21  
22  @RequestScoped
23  public class DataObjectService {
24  
25    private DataObjectDAO dataObjectDAO;
26    private CollectionDAO collectionDAO;
27    private UserDAO userDAO;
28    private DateHelper dateHelper;
29    private VersionDAO versionDAO;
30  
31    DataObjectService() {}
32  
33    @Inject
34    public DataObjectService(
35      DataObjectDAO dataObjectDAO,
36      CollectionDAO collectionDAO,
37      UserDAO userDAO,
38      DateHelper dateHelper,
39      VersionDAO versionDAO
40    ) {
41      this.dataObjectDAO = dataObjectDAO;
42      this.collectionDAO = collectionDAO;
43      this.userDAO = userDAO;
44      this.dateHelper = dateHelper;
45      this.versionDAO = versionDAO;
46    }
47  
48    /**
49     * Creates a DataObject and stores it in Neo4J
50     *
51     * @param collectionShepardId identifies the Collection
52     * @param dataObject          to be stored
53     * @param username            of the related user
54     * @return the stored DataObject with the auto generated id
55     */
56    public DataObject createDataObjectByCollectionShepardId(
57      long collectionShepardId,
58      DataObjectIO dataObject,
59      String username
60    ) {
61      Collection collection = collectionDAO.findByShepardId(collectionShepardId);
62      User user = userDAO.find(username);
63      DataObject parent = findRelatedDataObjectByShepardId(collection.getShepardId(), dataObject.getParentId(), null);
64      List<DataObject> predecessors = findRelatedDataObjectsByShepardIds(
65        collection.getShepardId(),
66        dataObject.getPredecessorIds(),
67        null
68      );
69      DataObject toCreate = new DataObject();
70      toCreate.setAttributes(dataObject.getAttributes());
71      toCreate.setDescription(dataObject.getDescription());
72      toCreate.setName(dataObject.getName());
73      toCreate.setCollection(collection);
74      toCreate.setParent(parent);
75      toCreate.setPredecessors(predecessors);
76      toCreate.setCreatedAt(dateHelper.getDate());
77      toCreate.setCreatedBy(user);
78      DataObject created = dataObjectDAO.createOrUpdate(toCreate);
79      created.setShepardId(created.getId());
80      created = dataObjectDAO.createOrUpdate(created);
81      versionDAO.createLink(created.getId(), collection.getVersion().getUid());
82      return created;
83    }
84  
85    /**
86     * Searches the neo4j database for a dataObject
87     *
88     * @param shepardId identifies the searched dataObject
89     * @return the DataObject with the given id or null
90     */
91    public DataObject getDataObjectByShepardId(long shepardId, UUID versionUID) {
92      DataObject ret;
93      String errorMsg;
94      if (versionUID == null) {
95        ret = dataObjectDAO.findByShepardId(shepardId);
96        errorMsg = String.format("DataObject with id %s is null or deleted", shepardId);
97      } else {
98        ret = dataObjectDAO.findByShepardId(shepardId, versionUID);
99        errorMsg = String.format("DataObject with id %s and versionUID %s is null or deleted", shepardId, versionUID);
100     }
101     if (ret == null || ret.isDeleted()) {
102       Log.error(errorMsg);
103       return null;
104     }
105     cutDeleted(ret);
106     return ret;
107   }
108 
109   public DataObject getDataObjectByShepardId(long shepardId) {
110     return getDataObjectByShepardId(shepardId, null);
111   }
112 
113   /**
114    * Searches the database for DataObjects.
115    *
116    * @param collectionShepardId  identifies the collection
117    * @param paramsWithShepardIds encapsulates possible parameters
118    * @param versionUID identifies the version
119    * @return a List of DataObjects
120    */
121 
122   public List<DataObject> getAllDataObjectsByShepardIds(
123     long collectionShepardId,
124     QueryParamHelper paramsWithShepardIds,
125     UUID versionUID
126   ) {
127     var unfiltered = dataObjectDAO.findByCollectionByShepardIds(collectionShepardId, paramsWithShepardIds, versionUID);
128     var dataObjects = unfiltered.stream().map(this::cutDeleted).toList();
129     return dataObjects;
130   }
131 
132   /**
133    * Updates a DataObject with new attributes. Hereby only not null attributes
134    * will replace the old attributes.
135    *
136    * @param dataObjectShepardId Identifies the dataObject
137    * @param dataObject          DataObject entity for updating.
138    * @param username            of the related user
139    * @return updated DataObject.
140    */
141   public DataObject updateDataObjectByShepardId(long dataObjectShepardId, DataObjectIO dataObject, String username) {
142     DataObject old = dataObjectDAO.findByShepardId(dataObjectShepardId);
143     User user = userDAO.find(username);
144     DataObject parent = findRelatedDataObjectByShepardId(
145       old.getCollection().getShepardId(),
146       dataObject.getParentId(),
147       dataObjectShepardId
148     );
149     List<DataObject> predecessors = findRelatedDataObjectsByShepardIds(
150       old.getCollection().getShepardId(),
151       dataObject.getPredecessorIds(),
152       dataObjectShepardId
153     );
154     old.setAttributes(dataObject.getAttributes());
155     old.setDescription(dataObject.getDescription());
156     old.setName(dataObject.getName());
157     old.setParent(parent);
158     old.setPredecessors(predecessors);
159     old.setUpdatedAt(dateHelper.getDate());
160     old.setUpdatedBy(user);
161     DataObject updated = dataObjectDAO.createOrUpdate(old);
162     cutDeleted(updated);
163     return updated;
164   }
165 
166   /**
167    * set the deleted flag for the DataObject
168    *
169    * @param dataObjectShepardId identifies the DataObject to be deleted
170    * @param username            of the related user
171    * @return a boolean to identify if the DataObject was successfully removed
172    */
173   public boolean deleteDataObjectByShepardId(long dataObjectShepardId, String username) {
174     Date date = dateHelper.getDate();
175     User user = userDAO.find(username);
176     boolean result = dataObjectDAO.deleteDataObjectByShepardId(dataObjectShepardId, user, date);
177     return result;
178   }
179 
180   private DataObject cutDeleted(DataObject dataObject) {
181     var incoming = dataObject.getIncoming().stream().filter(i -> !i.isDeleted()).toList();
182     dataObject.setIncoming(incoming);
183     if (dataObject.getParent() != null && dataObject.getParent().isDeleted()) {
184       dataObject.setParent(null);
185     }
186     var children = dataObject.getChildren().stream().filter(s -> !s.isDeleted()).toList();
187     dataObject.setChildren(children);
188     var predecessors = dataObject.getPredecessors().stream().filter(s -> !s.isDeleted()).toList();
189     dataObject.setPredecessors(predecessors);
190     var sucessors = dataObject.getSuccessors().stream().filter(s -> !s.isDeleted()).toList();
191     dataObject.setSuccessors(sucessors);
192     var references = dataObject.getReferences().stream().filter(ref -> !ref.isDeleted()).toList();
193     dataObject.setReferences(references);
194     return dataObject;
195   }
196 
197   private List<DataObject> findRelatedDataObjectsByShepardIds(
198     long collectionShepardId,
199     long[] referencedShepardIds,
200     Long dataObjectShepardId
201   ) {
202     if (referencedShepardIds == null) return new ArrayList<>();
203 
204     var result = new ArrayList<DataObject>(referencedShepardIds.length);
205     /*
206      * TODO: seems to be inefficient since this loops generates referencedIds.length
207      * calls to Neo4j this could possibly be packed into one query (or in chunks of
208      * queries in case of a large referencedIds array)
209      */
210     for (var shepardId : referencedShepardIds) {
211       result.add(findRelatedDataObjectByShepardId(collectionShepardId, shepardId, dataObjectShepardId));
212     }
213     return result;
214   }
215 
216   private DataObject findRelatedDataObjectByShepardId(
217     long collectionShepardId,
218     Long referencedShepardId,
219     Long dataObjectShepardId
220   ) {
221     if (referencedShepardId == null) return null;
222     else if (referencedShepardId.equals(dataObjectShepardId)) throw new InvalidBodyException(
223       "Self references are not allowed."
224     );
225 
226     var dataObject = dataObjectDAO.findByShepardId(referencedShepardId);
227     if (dataObject == null || dataObject.isDeleted()) throw new InvalidBodyException(
228       String.format("The DataObject with id %d could not be found.", referencedShepardId)
229     );
230 
231     // Prevent cross collection references
232     if (!dataObject.getCollection().getShepardId().equals(collectionShepardId)) throw new InvalidBodyException(
233       "Related data objects must belong to the same collection as the new data object"
234     );
235 
236     return dataObject;
237   }
238 
239   public Long getCollectionId(Long dataObjectId) {
240     DataObject dataObject = dataObjectDAO.findByShepardId(dataObjectId);
241     if (null == dataObject) return null;
242     return dataObject.getCollection().getShepardId();
243   }
244 }