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 }