View Javadoc
1   /*
2    * Copyright 2012-2025 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.app.web.api;
17  
18  import java.util.HashMap;
19  import java.util.List;
20  import java.util.Locale;
21  import java.util.Map;
22  import java.util.stream.Collectors;
23  
24  import org.codelibs.fess.entity.SearchRenderData;
25  import org.codelibs.fess.mylasta.action.FessMessages;
26  import org.codelibs.fess.util.ComponentUtil;
27  import org.codelibs.fess.util.FacetResponse;
28  import org.lastaflute.web.util.LaRequestUtil;
29  import org.lastaflute.web.validation.VaMessenger;
30  
31  import jakarta.servlet.http.HttpServletRequest;
32  
33  /**
34   * This class represents the base response structure for API results.
35   * It encapsulates the API response and provides methods to build different types of API responses.
36   */
37  public class ApiResult {
38  
39      /**
40       * The API response object.
41       */
42      protected ApiResponse response = null;
43  
44      /**
45       * Constructs an ApiResult with the specified ApiResponse.
46       * @param response The API response object.
47       */
48      public ApiResult(final ApiResponse response) {
49          this.response = response;
50      }
51  
52      /**
53       * Represents the status of an API response.
54       */
55      public enum Status {
56          /** Successful response status. */
57          OK(0),
58          /** Bad request status indicating client error. */
59          BAD_REQUEST(1),
60          /** System error status indicating server error. */
61          SYSTEM_ERROR(2),
62          /** Unauthorized status indicating authentication failure. */
63          UNAUTHORIZED(3),
64          /** General failure status. */
65          FAILED(9);
66  
67          private final int id;
68  
69          Status(final int id) {
70              this.id = id;
71          }
72  
73          /**
74           * Gets the numeric ID of the status.
75           * @return The numeric ID of the status.
76           */
77          public int getId() {
78              return id;
79          }
80      }
81  
82      /**
83       * Represents the base API response structure.
84       */
85      public static class ApiResponse {
86          /** The version of the product. */
87          protected String version = ComponentUtil.getSystemHelper().getProductVersion();
88          /** The status code of the response. */
89          protected int status;
90  
91          /**
92           * Default constructor for ApiResponse.
93           */
94          public ApiResponse() {
95              // Default constructor
96          }
97  
98          /**
99           * Sets the status of the response.
100          * @param status The status to set.
101          * @return This ApiResponse instance.
102          */
103         public ApiResponse status(final Status status) {
104             this.status = status.getId();
105             return this;
106         }
107 
108         /**
109          * Returns a new ApiResult instance with this ApiResponse.
110          * @return A new ApiResult instance.
111          */
112         public ApiResult result() {
113             return new ApiResult(this);
114         }
115     }
116 
117     /**
118      * Represents an API response for an update operation.
119      */
120     public static class ApiUpdateResponse extends ApiResponse {
121         /** The ID of the updated item. */
122         protected String id;
123         /** Whether the item was created (true) or updated (false). */
124         protected boolean created;
125 
126         /**
127          * Default constructor for ApiUpdateResponse.
128          */
129         public ApiUpdateResponse() {
130             super();
131         }
132 
133         /**
134          * Sets the ID of the updated item.
135          * @param id The ID to set.
136          * @return This ApiUpdateResponse instance.
137          */
138         public ApiUpdateResponse id(final String id) {
139             this.id = id;
140             return this;
141         }
142 
143         /**
144          * Sets whether the item was created or updated.
145          * @param created True if created, false if updated.
146          * @return This ApiUpdateResponse instance.
147          */
148         public ApiUpdateResponse created(final boolean created) {
149             this.created = created;
150             return this;
151         }
152 
153         @Override
154         public ApiResult result() {
155             return new ApiResult(this);
156         }
157     }
158 
159     /**
160      * Represents an API response for a start job operation.
161      */
162     public static class ApiStartJobResponse extends ApiResponse {
163         /** The pre-generated job log ID. Null when job logging is disabled. */
164         protected String jobLogId;
165 
166         /**
167          * Default constructor for ApiStartJobResponse.
168          */
169         public ApiStartJobResponse() {
170             super();
171         }
172 
173         /**
174          * Sets the job log ID.
175          * @param jobLogId The job log ID to set. Null when job logging is disabled.
176          * @return This ApiStartJobResponse instance.
177          */
178         public ApiStartJobResponse jobLogId(final String jobLogId) {
179             this.jobLogId = jobLogId;
180             return this;
181         }
182 
183         @Override
184         public ApiResult result() {
185             return new ApiResult(this);
186         }
187     }
188 
189     /**
190      * Represents an API response for a delete operation.
191      */
192     public static class ApiDeleteResponse extends ApiResponse {
193         /**
194          * Constructs an empty ApiDeleteResponse.
195          */
196         public ApiDeleteResponse() {
197             // NOP
198         }
199 
200         /**
201          * The number of deleted items.
202          */
203         protected long count = 1;
204 
205         /**
206          * Sets the count of deleted items.
207          * @param count The number of deleted items.
208          * @return The ApiDeleteResponse instance.
209          */
210         public ApiDeleteResponse count(final long count) {
211             this.count = count;
212             return this;
213         }
214 
215         @Override
216         public ApiResult result() {
217             return new ApiResult(this);
218         }
219     }
220 
221     /**
222      * Represents an API response for configuration settings.
223      */
224     public static class ApiConfigResponse extends ApiResponse {
225         /**
226          * Constructs an empty ApiConfigResponse.
227          */
228         public ApiConfigResponse() {
229             // NOP
230         }
231 
232         /**
233          * The configuration setting object.
234          */
235         protected Object setting;
236 
237         /**
238          * Sets the configuration setting object.
239          * @param setting The configuration setting object.
240          * @return The ApiConfigResponse instance.
241          */
242         public ApiConfigResponse setting(final Object setting) {
243             this.setting = setting;
244             return this;
245         }
246 
247         @Override
248         public ApiResult result() {
249             return new ApiResult(this);
250         }
251     }
252 
253     /**
254      * Represents an API response for a list of configuration settings.
255      * @param <T> the type of the configuration settings
256      */
257     public static class ApiConfigsResponse<T> extends ApiResponse {
258         /**
259          * Constructs an empty ApiConfigsResponse.
260          */
261         public ApiConfigsResponse() {
262             // NOP
263         }
264 
265         /**
266          * The list of configuration settings.
267          */
268         protected List<T> settings;
269         /**
270          * The total number of configuration settings.
271          */
272         protected long total = 0;
273 
274         /**
275          * Sets the list of configuration settings and updates the total count.
276          * @param settings The list of configuration settings.
277          * @return The ApiConfigsResponse instance.
278          */
279         public ApiConfigsResponse<T> settings(final List<T> settings) {
280             this.settings = settings;
281             total = settings.size();
282             return this;
283         }
284 
285         /**
286          * Sets the total number of configuration settings.
287          * @param total The total number of configuration settings.
288          * @return The ApiConfigsResponse instance.
289          */
290         public ApiConfigsResponse<T> total(final long total) {
291             this.total = total;
292             return this;
293         }
294 
295         @Override
296         public ApiResult result() {
297             return new ApiResult(this);
298         }
299     }
300 
301     /**
302      * Represents an API response for a single document.
303      */
304     public static class ApiDocResponse extends ApiResponse {
305         /**
306          * Constructs an empty ApiDocResponse.
307          */
308         public ApiDocResponse() {
309             // NOP
310         }
311 
312         /**
313          * The document object.
314          */
315         protected Object doc;
316 
317         /**
318          * Sets the document object.
319          * @param doc The document object.
320          * @return The ApiDocResponse instance.
321          */
322         public ApiDocResponse doc(final Object doc) {
323             this.doc = doc;
324             return this;
325         }
326 
327         @Override
328         public ApiResult result() {
329             return new ApiResult(this);
330         }
331     }
332 
333     /**
334      * Represents an API response for search results, including document list, pagination, and facet information.
335      */
336     public static class ApiDocsResponse extends ApiResponse {
337         /**
338          * The ID of the search query.
339          */
340         protected String queryId;
341 
342         /**
343          * Default constructor for ApiDocsResponse.
344          */
345         public ApiDocsResponse() {
346             super();
347         }
348 
349         /**
350          * The list of documents returned in the search results.
351          */
352         protected List<Map<String, Object>> docs;
353         /**
354          * Parameters for highlighting search results.
355          */
356         protected String highlightParams;
357         /**
358          * The execution time of the search query.
359          */
360         protected String execTime;
361         /**
362          * The page size of the search results.
363          */
364         protected int pageSize;
365         /**
366          * The current page number of the search results.
367          */
368         protected int pageNumber;
369         /**
370          * The total number of records found.
371          */
372         protected long recordCount;
373         /**
374          * The relation of the record count (e.g., "eq" for exact, "gte" for greater than or equal to).
375          */
376         protected String recordCountRelation;
377         /**
378          * The total number of pages in the search results.
379          */
380         protected int pageCount;
381         /**
382          * Indicates if there is a next page of search results.
383          */
384         protected boolean nextPage;
385         /**
386          * Indicates if there is a previous page of search results.
387          */
388         protected boolean prevPage;
389         /**
390          * The starting record number for the current page of search results.
391          */
392         protected long startRecordNumber;
393         /**
394          * The ending record number for the current page of search results.
395          */
396         protected long endRecordNumber;
397         /**
398          * The list of page numbers for pagination.
399          */
400         protected List<String> pageNumbers;
401         /**
402          * Indicates if the search results are partial.
403          */
404         protected boolean partial;
405         /**
406          * The time taken for the search query in milliseconds.
407          */
408         protected long queryTime;
409         /**
410          * The search query string.
411          */
412         protected String searchQuery;
413         /**
414          * The time when the search request was made.
415          */
416         protected long requestedTime;
417         /**
418          * The list of facet fields and their values.
419          */
420         protected List<Map<String, Object>> facetField;
421         /**
422          * The list of facet queries and their counts.
423          */
424         protected List<Map<String, Object>> facetQuery;
425 
426         /**
427          * Populates this response with search render data.
428          * @param data The search render data to populate from.
429          * @return This ApiDocsResponse instance.
430          */
431         public ApiDocsResponse renderData(final SearchRenderData data) {
432             queryId = data.getQueryId();
433             docs = data.getDocumentItems();
434             highlightParams = data.getAppendHighlightParams();
435             execTime = data.getExecTime();
436             pageSize = data.getPageSize();
437             pageNumber = data.getCurrentPageNumber();
438             recordCount = data.getAllRecordCount();
439             recordCountRelation = data.getAllRecordCountRelation();
440             pageCount = data.getAllPageCount();
441             nextPage = data.isExistNextPage();
442             prevPage = data.isExistPrevPage();
443             startRecordNumber = data.getCurrentStartRecordNumber();
444             endRecordNumber = data.getCurrentEndRecordNumber();
445             pageNumbers = data.getPageNumberList();
446             partial = data.isPartialResults();
447             queryTime = data.getQueryTime();
448             searchQuery = data.getSearchQuery();
449             requestedTime = data.getRequestedTime();
450             final FacetResponse facetResponse = data.getFacetResponse();
451             if (facetResponse != null && facetResponse.hasFacetResponse()) {
452                 // facet field
453                 if (facetResponse.getFieldList() != null) {
454                     facetField = facetResponse.getFieldList().stream().map(field -> {
455                         final Map<String, Object> fieldMap = new HashMap<>(2, 1f);
456                         fieldMap.put("name", field.getName());
457                         fieldMap.put("result", field.getValueCountMap().entrySet().stream().map(e -> {
458                             final Map<String, Object> valueCount = new HashMap<>(2, 1f);
459                             valueCount.put("value", e.getKey());
460                             valueCount.put("count", e.getValue());
461                             return valueCount;
462                         }).collect(Collectors.toList()));
463                         return fieldMap;
464                     }).collect(Collectors.toList());
465                 }
466                 // facet q
467                 if (facetResponse.getQueryCountMap() != null) {
468                     facetQuery = facetResponse.getQueryCountMap().entrySet().stream().map(e -> {
469                         final Map<String, Object> valueCount = new HashMap<>(2, 1f);
470                         valueCount.put("value", e.getKey());
471                         valueCount.put("count", e.getValue());
472                         return valueCount;
473                     }).collect(Collectors.toList());
474 
475                 }
476             }
477             return this;
478         }
479 
480         @Override
481         public ApiResult result() {
482             return new ApiResult(this);
483         }
484     }
485 
486     /**
487      * Represents an API response for a log entry.
488      */
489     public static class ApiLogResponse extends ApiResponse {
490         /** The log entry object. */
491         protected Object log;
492 
493         /**
494          * Default constructor for ApiLogResponse.
495          */
496         public ApiLogResponse() {
497             super();
498         }
499 
500         /**
501          * Sets the log entry object.
502          * @param log The log entry object.
503          * @return This ApiLogResponse instance.
504          */
505         public ApiLogResponse log(final Object log) {
506             this.log = log;
507             return this;
508         }
509 
510         @Override
511         public ApiResult result() {
512             return new ApiResult(this);
513         }
514     }
515 
516     /**
517      * Represents an API response for a list of logs.
518      * @param <T> the type of the logs
519      */
520     public static class ApiLogsResponse<T> extends ApiResponse {
521         /** The list of log entries. */
522         protected List<T> logs;
523 
524         /**
525          * Default constructor for ApiLogsResponse.
526          */
527         public ApiLogsResponse() {
528             super();
529         }
530 
531         /**
532          * The total number of logs.
533          */
534         protected long total = 0;
535 
536         /**
537          * Sets the list of log entries.
538          * @param logs The list of log entries.
539          * @return This ApiLogsResponse instance.
540          */
541         public ApiLogsResponse<T> logs(final List<T> logs) {
542             this.logs = logs;
543             return this;
544         }
545 
546         /**
547          * Sets the total number of logs.
548          * @param total The total number of logs.
549          * @return This ApiLogsResponse instance.
550          */
551         public ApiLogsResponse<T> total(final long total) {
552             this.total = total;
553             return this;
554         }
555 
556         @Override
557         public ApiResult result() {
558             return new ApiResult(this);
559         }
560     }
561 
562     /**
563      * Represents an API response containing a list of log files.
564      */
565     public static class ApiLogFilesResponse extends ApiResponse {
566         /** The list of log files. */
567         protected List<Map<String, Object>> files;
568 
569         /**
570          * Default constructor for ApiLogFilesResponse.
571          */
572         public ApiLogFilesResponse() {
573             super();
574         }
575 
576         /**
577          * The total number of log files.
578          */
579         protected long total = 0;
580 
581         /**
582          * Sets the list of log files.
583          * @param files The list of log files.
584          * @return This ApiLogFilesResponse instance.
585          */
586         public ApiLogFilesResponse files(final List<Map<String, Object>> files) {
587             this.files = files;
588             return this;
589         }
590 
591         /**
592          * Sets the total number of log files.
593          * @param total The total number of log files.
594          * @return This ApiLogFilesResponse instance.
595          */
596         public ApiLogFilesResponse total(final long total) {
597             this.total = total;
598             return this;
599         }
600 
601         @Override
602         public ApiResult result() {
603             return new ApiResult(this);
604         }
605     }
606 
607     /**
608      * Represents an API response containing a list of backup files.
609      */
610     public static class ApiBackupFilesResponse extends ApiResponse {
611         /**
612          * The list of backup files, where each file is represented by a map of strings.
613          */
614         protected List<Map<String, String>> files;
615         /**
616          * The total number of backup files.
617          */
618         protected long total = 0;
619 
620         /**
621          * Constructs an empty ApiBackupFilesResponse.
622          */
623         public ApiBackupFilesResponse() {
624             // NOP
625         }
626 
627         /**
628          * Sets the list of backup files.
629          * @param files The list of backup files, where each file is represented by a map of strings.
630          * @return The ApiBackupFilesResponse instance.
631          */
632         public ApiBackupFilesResponse files(final List<Map<String, String>> files) {
633             this.files = files;
634             return this;
635         }
636 
637         /**
638          * Sets the total number of backup files.
639          * @param total The total number of backup files.
640          * @return The ApiBackupFilesResponse instance.
641          */
642         public ApiBackupFilesResponse total(final long total) {
643             this.total = total;
644             return this;
645         }
646 
647         @Override
648         public ApiResult result() {
649             return new ApiResult(this);
650         }
651     }
652 
653     /**
654      * Represents an API response containing system information.
655      */
656     public static class ApiSystemInfoResponse extends ApiResponse {
657         /** Environment properties. */
658         protected List<Map<String, String>> envProps;
659 
660         /**
661          * Default constructor for ApiSystemInfoResponse.
662          */
663         public ApiSystemInfoResponse() {
664             super();
665         }
666 
667         /** System properties. */
668         protected List<Map<String, String>> systemProps;
669         /** Fess-specific properties. */
670         protected List<Map<String, String>> fessProps;
671         /** Bug report properties. */
672         protected List<Map<String, String>> bugReportProps;
673 
674         /**
675          * Sets the environment properties.
676          * @param envProps The environment properties.
677          * @return This ApiSystemInfoResponse instance.
678          */
679         public ApiSystemInfoResponse envProps(final List<Map<String, String>> envProps) {
680             this.envProps = envProps;
681             return this;
682         }
683 
684         /**
685          * Sets the system properties.
686          * @param systemProps The system properties.
687          * @return This ApiSystemInfoResponse instance.
688          */
689         public ApiSystemInfoResponse systemProps(final List<Map<String, String>> systemProps) {
690             this.systemProps = systemProps;
691             return this;
692         }
693 
694         /**
695          * Sets the Fess-specific properties.
696          * @param fessProps The Fess-specific properties.
697          * @return This ApiSystemInfoResponse instance.
698          */
699         public ApiSystemInfoResponse fessProps(final List<Map<String, String>> fessProps) {
700             this.fessProps = fessProps;
701             return this;
702         }
703 
704         /**
705          * Sets the bug report properties.
706          * @param bugReportProps The bug report properties.
707          * @return This ApiSystemInfoResponse instance.
708          */
709         public ApiSystemInfoResponse bugReportProps(final List<Map<String, String>> bugReportProps) {
710             this.bugReportProps = bugReportProps;
711             return this;
712         }
713 
714         @Override
715         public ApiResult result() {
716             return new ApiResult(this);
717         }
718     }
719 
720     /**
721      * Represents an API response for an error.
722      */
723     public static class ApiErrorResponse extends ApiResponse {
724         /** The error message. */
725         protected String message;
726 
727         /**
728          * Default constructor for ApiErrorResponse.
729          */
730         public ApiErrorResponse() {
731             super();
732         }
733 
734         /**
735          * Sets the error message.
736          * @param message The error message.
737          * @return This ApiErrorResponse instance.
738          */
739         public ApiErrorResponse message(final String message) {
740             this.message = message;
741             return this;
742         }
743 
744         /**
745          * Sets the error message from validation messages.
746          * @param validationMessagesLambda Lambda function to process validation messages.
747          * @return This ApiErrorResponse instance.
748          */
749         public ApiErrorResponse message(final VaMessenger<FessMessages> validationMessagesLambda) {
750             final FessMessages messages = new FessMessages();
751             validationMessagesLambda.message(messages);
752             message = ComponentUtil.getMessageManager()
753                     .toMessageList(LaRequestUtil.getOptionalRequest().map(HttpServletRequest::getLocale).orElse(Locale.ENGLISH), messages)
754                     .stream()
755                     .collect(Collectors.joining(" "));
756             return this;
757         }
758     }
759 
760     /**
761      * Represents an API response for plugin information.
762      */
763     public static class ApiPluginResponse extends ApiResponse {
764         /** The list of plugins. */
765         protected List<Map<String, String>> plugins;
766 
767         /**
768          * Default constructor for ApiPluginResponse.
769          */
770         public ApiPluginResponse() {
771             super();
772         }
773 
774         /**
775          * Sets the list of plugins.
776          * @param plugins The list of plugins.
777          * @return This ApiPluginResponse instance.
778          */
779         public ApiPluginResponse plugins(final List<Map<String, String>> plugins) {
780             this.plugins = plugins;
781             return this;
782         }
783 
784         @Override
785         public ApiResult result() {
786             return new ApiResult(this);
787         }
788     }
789 
790     /**
791      * Represents an API response for storage-related operations, typically containing a list of items.
792      */
793     public static class ApiStorageResponse extends ApiResponse {
794         /** The list of storage items. */
795         protected List<Map<String, Object>> items;
796 
797         /**
798          * Default constructor for ApiStorageResponse.
799          */
800         public ApiStorageResponse() {
801             super();
802         }
803 
804         /**
805          * Sets the list of storage items.
806          * @param items The list of storage items.
807          * @return This ApiStorageResponse instance.
808          */
809         public ApiStorageResponse items(final List<Map<String, Object>> items) {
810             this.items = items;
811             return this;
812         }
813 
814         @Override
815         public ApiResult result() {
816             return new ApiResult(this);
817         }
818     }
819 
820     /**
821      * Represents an API response containing statistical information.
822      */
823     public static class ApiStatsResponse extends ApiResponse {
824         /** The statistical data. */
825         protected Map<String, Object> stats;
826 
827         /**
828          * Default constructor for ApiStatsResponse.
829          */
830         public ApiStatsResponse() {
831             super();
832         }
833 
834         /**
835          * Sets the statistical data.
836          * @param stats The statistical data.
837          * @return This ApiStatsResponse instance.
838          */
839         public ApiStatsResponse stats(final Map<String, Object> stats) {
840             this.stats = stats;
841             return this;
842         }
843 
844         @Override
845         public ApiResult result() {
846             return new ApiResult(this);
847         }
848     }
849 
850     /**
851      * Represents an API response for bulk operations, containing a list of processed items.
852      */
853     public static class ApiBulkResponse extends ApiResponse {
854         /**
855          * Constructs an empty ApiBulkResponse.
856          */
857         public ApiBulkResponse() {
858             // NOP
859         }
860 
861         /**
862          * The list of items processed in the bulk operation.
863          */
864         protected List<Map<String, Object>> items;
865 
866         /**
867          * Sets the list of items processed in the bulk operation.
868          * @param items The list of items, where each item is represented by a map.
869          * @return The ApiBulkResponse instance.
870          */
871         public ApiBulkResponse items(final List<Map<String, Object>> items) {
872             this.items = items;
873             return this;
874         }
875 
876         @Override
877         public ApiResult result() {
878             return new ApiResult(this);
879         }
880     }
881 }