View Javadoc
1   package de.dlr.shepard.common.search.endpoints;
2   
3   import de.dlr.shepard.common.search.io.CollectionSearchBody;
4   import de.dlr.shepard.common.search.io.CollectionSearchResult;
5   import de.dlr.shepard.common.search.io.ContainerSearchBody;
6   import de.dlr.shepard.common.search.io.ContainerSearchResult;
7   import de.dlr.shepard.common.search.io.ResponseBody;
8   import de.dlr.shepard.common.search.io.SearchBody;
9   import de.dlr.shepard.common.search.io.TimeseriesSearchBody;
10  import de.dlr.shepard.common.search.io.TimeseriesSearchResult;
11  import de.dlr.shepard.common.search.io.UserGroupSearchBody;
12  import de.dlr.shepard.common.search.io.UserGroupSearchResult;
13  import de.dlr.shepard.common.search.io.UserSearchBody;
14  import de.dlr.shepard.common.search.io.UserSearchResult;
15  import de.dlr.shepard.common.search.services.CollectionSearchService;
16  import de.dlr.shepard.common.search.services.ContainerSearchService;
17  import de.dlr.shepard.common.search.services.PaginatedCollectionList;
18  import de.dlr.shepard.common.search.services.SearchService;
19  import de.dlr.shepard.common.search.services.TimeseriesSearchService;
20  import de.dlr.shepard.common.search.services.UserGroupSearchService;
21  import de.dlr.shepard.common.search.services.UserSearchService;
22  import de.dlr.shepard.common.util.Constants;
23  import de.dlr.shepard.common.util.PaginationHelper;
24  import de.dlr.shepard.common.util.SortingHelper;
25  import de.dlr.shepard.context.collection.io.CollectionIO;
26  import jakarta.enterprise.context.RequestScoped;
27  import jakarta.inject.Inject;
28  import jakarta.validation.Valid;
29  import jakarta.validation.constraints.NotNull;
30  import jakarta.validation.constraints.Positive;
31  import jakarta.validation.constraints.PositiveOrZero;
32  import jakarta.ws.rs.Consumes;
33  import jakarta.ws.rs.POST;
34  import jakarta.ws.rs.Path;
35  import jakarta.ws.rs.PathParam;
36  import jakarta.ws.rs.Produces;
37  import jakarta.ws.rs.QueryParam;
38  import jakarta.ws.rs.core.MediaType;
39  import jakarta.ws.rs.core.Response;
40  import java.util.Optional;
41  import org.eclipse.microprofile.openapi.annotations.Operation;
42  import org.eclipse.microprofile.openapi.annotations.media.Content;
43  import org.eclipse.microprofile.openapi.annotations.media.Schema;
44  import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
45  import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;
46  import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
47  import org.eclipse.microprofile.openapi.annotations.tags.Tag;
48  
49  @Path(Constants.SEARCH)
50  @Produces(MediaType.APPLICATION_JSON)
51  @Consumes(MediaType.APPLICATION_JSON)
52  @RequestScoped
53  public class SearchRest {
54  
55    @Inject
56    SearchService searchService;
57  
58    @Inject
59    UserSearchService userSearchService;
60  
61    @Inject
62    UserGroupSearchService userGroupSearchService;
63  
64    @Inject
65    ContainerSearchService containerSearchService;
66  
67    @Inject
68    CollectionSearchService collectionSearchService;
69  
70    @Inject
71    TimeseriesSearchService timeseriesSearchService;
72  
73    @POST
74    @Tag(name = Constants.SEARCH)
75    @Operation(description = "search")
76    @APIResponse(
77      description = "ok",
78      responseCode = "200",
79      content = @Content(schema = @Schema(implementation = ResponseBody.class))
80    )
81    @APIResponse(responseCode = "400", description = "bad request")
82    @APIResponse(responseCode = "401", description = "not authorized")
83    public Response search(
84      @RequestBody(
85        required = true,
86        content = @Content(schema = @Schema(implementation = SearchBody.class))
87      ) @Valid SearchBody body
88    ) {
89      ResponseBody ret = searchService.search(body);
90      return Response.ok(ret).build();
91    }
92  
93    @POST
94    @Path("/" + Constants.COLLECTIONS)
95    @Tag(name = Constants.SEARCH)
96    @Operation(description = "Search collections with paginated response")
97    @APIResponse(
98      description = "ok",
99      responseCode = "200",
100     content = @Content(schema = @Schema(implementation = CollectionSearchResult.class))
101   )
102   @APIResponse(responseCode = "400", description = "bad request")
103   @APIResponse(responseCode = "401", description = "not authorized")
104   @Parameter(name = Constants.QP_PAGE, description = "Pagination starts at 0")
105   @Parameter(name = Constants.QP_SIZE)
106   @Parameter(name = Constants.QP_ORDER_BY_ATTRIBUTE, description = "Defaults to 'createdAt'")
107   @Parameter(name = Constants.QP_ORDER_DESC, description = "Defaults to 'true'")
108   public Response searchCollections(
109     @RequestBody(
110       required = true,
111       content = @Content(schema = @Schema(implementation = CollectionSearchBody.class)),
112       description = "You can search by ID, name or createdBy. Connect search parameters like this: {\"OR\":[{\"property\":\"name\",\"value\":\"ABC\",\"operator\":\"contains\"},{\"property\":\"id\",\"value\":123,\"operator\":\"contains\"}]}\"" +
113       " "
114     ) @Valid CollectionSearchBody collectionSearchBody,
115     @QueryParam(Constants.QP_PAGE) @PositiveOrZero Integer page,
116     @QueryParam(Constants.QP_SIZE) @Positive Integer size,
117     @QueryParam(Constants.QP_ORDER_BY_ATTRIBUTE) BasicCollectionAttributes orderBy,
118     @QueryParam(Constants.QP_ORDER_DESC) Boolean orderDesc
119   ) {
120     PaginatedCollectionList paginatedCollectionList = collectionSearchService.search(
121       collectionSearchBody.getSearchParams().getQuery(),
122       Optional.ofNullable(page),
123       Optional.ofNullable(size),
124       Optional.ofNullable(orderBy).orElse(BasicCollectionAttributes.createdAt),
125       Optional.ofNullable(orderDesc).orElse(true)
126     );
127 
128     CollectionSearchResult collectionSearchResult = new CollectionSearchResult(
129       paginatedCollectionList.getResults().stream().map(CollectionIO::new).toList(),
130       collectionSearchBody.getSearchParams(),
131       paginatedCollectionList.getTotalResults()
132     );
133 
134     return Response.ok(collectionSearchResult).build();
135   }
136 
137   @POST
138   @Path("/" + Constants.CONTAINERS)
139   @Tag(name = Constants.SEARCH)
140   @Operation(description = "Search containers")
141   @APIResponse(
142     description = "ok",
143     responseCode = "200",
144     content = @Content(schema = @Schema(implementation = ContainerSearchResult.class))
145   )
146   @APIResponse(responseCode = "400", description = "bad request")
147   @APIResponse(responseCode = "401", description = "not authorized")
148   @Parameter(name = Constants.QP_PAGE, description = "Pagination starts at 0")
149   @Parameter(name = Constants.QP_SIZE)
150   @Parameter(name = Constants.QP_ORDER_BY_ATTRIBUTE, description = "Defaults to 'createdAt'")
151   @Parameter(name = Constants.QP_ORDER_DESC, description = "Defaults to 'true'")
152   public Response searchContainers(
153     @RequestBody(
154       required = true,
155       content = @Content(schema = @Schema(implementation = ContainerSearchBody.class))
156     ) @Valid ContainerSearchBody containerSearchBody,
157     @QueryParam(Constants.QP_PAGE) @PositiveOrZero Integer page,
158     @QueryParam(Constants.QP_SIZE) @Positive Integer size,
159     @QueryParam(Constants.QP_ORDER_BY_ATTRIBUTE) BasicContainerAttributes orderBy,
160     @QueryParam(Constants.QP_ORDER_DESC) Boolean orderDesc
161   ) {
162     PaginationHelper pagination = null;
163     if (page != null && size != null) pagination = new PaginationHelper(page, size);
164     SortingHelper sortingHelper = new SortingHelper(
165       Optional.ofNullable(orderBy).orElse(BasicContainerAttributes.createdAt),
166       Optional.ofNullable(orderDesc).orElse(true)
167     );
168     ContainerSearchResult ret = containerSearchService.search(containerSearchBody, pagination, sortingHelper);
169     return Response.ok(ret).build();
170   }
171 
172   @POST
173   @Path("/" + Constants.USERS)
174   @Tag(name = Constants.SEARCH)
175   @Operation(description = "Search users")
176   @APIResponse(
177     description = "ok",
178     responseCode = "200",
179     content = @Content(schema = @Schema(implementation = UserSearchResult.class))
180   )
181   @APIResponse(responseCode = "400", description = "bad request")
182   @APIResponse(responseCode = "401", description = "not authorized")
183   public Response searchUsers(
184     @RequestBody(
185       required = true,
186       content = @Content(schema = @Schema(implementation = UserSearchBody.class))
187     ) @Valid UserSearchBody userSearchBody
188   ) {
189     UserSearchResult ret = userSearchService.search(userSearchBody);
190     return Response.ok(ret).build();
191   }
192 
193   @POST
194   @Path("/" + Constants.TIMESERIES_CONTAINER + "/{" + Constants.TIMESERIES_CONTAINER_ID + "}/" + Constants.TIMESERIES)
195   @Tag(name = Constants.SEARCH)
196   @Operation(description = "Search timeseries in a container")
197   @APIResponse(
198     description = "ok",
199     responseCode = "200",
200     content = @Content(schema = @Schema(implementation = TimeseriesSearchResult.class))
201   )
202   @APIResponse(responseCode = "400", description = "bad request")
203   @APIResponse(responseCode = "401", description = "not authorized")
204   public Response searchTimeseriesInContainer(
205     @RequestBody(
206       required = true,
207       content = @Content(schema = @Schema(implementation = TimeseriesSearchBody.class))
208     ) @Valid TimeseriesSearchBody timeseriesSearchBody,
209     @PathParam(Constants.TIMESERIES_CONTAINER_ID) @NotNull @PositiveOrZero Long timeseriesContainerId
210   ) {
211     TimeseriesSearchResult ret = timeseriesSearchService.search(timeseriesContainerId, timeseriesSearchBody);
212     return Response.ok(ret).build();
213   }
214 
215   @POST
216   @Path("/" + Constants.USERGROUPS)
217   @Tag(name = Constants.SEARCH)
218   @Operation(description = "Search user groups")
219   @APIResponse(
220     description = "ok",
221     responseCode = "200",
222     content = @Content(schema = @Schema(implementation = UserGroupSearchResult.class))
223   )
224   @APIResponse(responseCode = "400", description = "bad request")
225   @APIResponse(responseCode = "401", description = "not authorized")
226   public Response searchUserGroups(
227     @RequestBody(
228       required = true,
229       content = @Content(schema = @Schema(implementation = UserSearchBody.class))
230     ) @Valid UserGroupSearchBody userGroupSearchBody
231   ) {
232     UserGroupSearchResult ret = userGroupSearchService.search(userGroupSearchBody);
233     return Response.ok(ret).build();
234   }
235 }