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.admin.dict.stemmeroverride;
17  
18  import static org.codelibs.fess.app.web.admin.dict.stemmeroverride.AdminDictStemmeroverrideAction.createStemmerOverrideItem;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.util.stream.Collectors;
24  
25  import org.apache.logging.log4j.LogManager;
26  import org.apache.logging.log4j.Logger;
27  import org.codelibs.fess.app.pager.StemmerOverridePager;
28  import org.codelibs.fess.app.service.StemmerOverrideService;
29  import org.codelibs.fess.app.web.CrudMode;
30  import org.codelibs.fess.app.web.admin.dict.stemmeroverride.UploadForm;
31  import org.codelibs.fess.app.web.api.ApiResult;
32  import org.codelibs.fess.app.web.api.admin.FessApiAdminAction;
33  import org.codelibs.fess.dict.stemmeroverride.StemmerOverrideFile;
34  import org.codelibs.fess.dict.stemmeroverride.StemmerOverrideItem;
35  import org.lastaflute.web.Execute;
36  import org.lastaflute.web.response.JsonResponse;
37  import org.lastaflute.web.response.StreamResponse;
38  
39  import jakarta.annotation.Resource;
40  
41  /**
42   * API action for admin Stemmer Override dictionary management.
43   * Provides REST endpoints for managing stemmer override dictionary items in the Fess search engine.
44   */
45  public class ApiAdminDictStemmeroverrideAction extends FessApiAdminAction {
46  
47      /**
48       * Default constructor.
49       */
50      public ApiAdminDictStemmeroverrideAction() {
51          super();
52      }
53  
54      private static final Logger logger = LogManager.getLogger(ApiAdminDictStemmeroverrideAction.class);
55  
56      @Resource
57      private StemmerOverrideService stemmerOverrideService;
58  
59      /**
60       * Retrieves stemmer override dictionary settings with pagination support.
61       *
62       * @param dictId the dictionary ID
63       * @param body the search body containing pagination and filter parameters
64       * @return JSON response containing list of stemmer override dictionary items
65       */
66      // GET /api/admin/dict/stemmerOverride/settings/{dictId}
67      @Execute
68      public JsonResponse<ApiResult> get$settings(final String dictId, final SearchBody body) {
69          body.dictId = dictId;
70          validateApi(body, messages -> {});
71          final StemmerOverridePager pager = copyBeanToNewBean(body, StemmerOverridePager.class);
72          return asJson(
73                  new ApiResult.ApiConfigsResponse<EditBody>().settings(stemmerOverrideService.getStemmerOverrideList(body.dictId, pager)
74                          .stream()
75                          .map(protwordsItem -> createEditBody(protwordsItem, dictId))
76                          .collect(Collectors.toList())).status(ApiResult.Status.OK).result());
77      }
78  
79      /**
80       * Retrieves a specific stemmer override dictionary item by ID.
81       *
82       * @param dictId the dictionary ID
83       * @param id the ID of the stemmer override item to retrieve
84       * @return JSON response containing the stemmer override dictionary item
85       */
86      // GET /api/admin/dict/stemmerOverride/setting/{dictId}/{id}
87      @Execute
88      public JsonResponse<ApiResult> get$setting(final String dictId, final long id) {
89          return asJson(new ApiResult.ApiConfigResponse().setting(
90                  stemmerOverrideService.getStemmerOverrideItem(dictId, id).map(entity -> createEditBody(entity, dictId)).orElseGet(() -> {
91                      throwValidationErrorApi(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, String.valueOf(id)));
92                      return null;
93                  })).status(ApiResult.Status.OK).result());
94      }
95  
96      /**
97       * Creates a new stemmer override dictionary item.
98       *
99       * @param dictId the dictionary ID
100      * @param body the request body containing stemmer override item information
101      * @return JSON response with result status
102      */
103     // POST /api/admin/dict/stemmerOverride/setting/{dictId}
104     @Execute
105     public JsonResponse<ApiResult> post$setting(final String dictId, final CreateBody body) {
106         body.dictId = dictId;
107         validateApi(body, messages -> {});
108         body.crudMode = CrudMode.CREATE;
109         final StemmerOverrideItem entity = createStemmerOverrideItem(this, body, () -> {
110             throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL));
111             return null;
112         }).orElseGet(() -> {
113             throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL));
114             return null;
115         });
116         stemmerOverrideService.store(body.dictId, entity);
117         return asJson(
118                 new ApiResult.ApiUpdateResponse().id(String.valueOf(entity.getId())).created(true).status(ApiResult.Status.OK).result());
119     }
120 
121     /**
122      * Updates an existing stemmer override dictionary item.
123      *
124      * @param dictId the dictionary ID
125      * @param body the request body containing updated stemmer override item information
126      * @return JSON response with result status
127      */
128     // PUT /api/admin/dict/stemmerOverride/setting/{dictId}
129     @Execute
130     public JsonResponse<ApiResult> put$setting(final String dictId, final EditBody body) {
131         body.dictId = dictId;
132         validateApi(body, messages -> {});
133         body.crudMode = CrudMode.EDIT;
134         final StemmerOverrideItem entity = createStemmerOverrideItem(this, body, () -> {
135             throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, String.valueOf(body.id)));
136             return null;
137         }).orElseGet(() -> {
138             throwValidationErrorApi(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, String.valueOf(body.id)));
139             return null;
140         });
141         stemmerOverrideService.store(body.dictId, entity);
142         return asJson(
143                 new ApiResult.ApiUpdateResponse().id(String.valueOf(entity.getId())).created(false).status(ApiResult.Status.OK).result());
144     }
145 
146     /**
147      * Deletes a stemmer override dictionary item by ID.
148      *
149      * @param dictId the dictionary ID
150      * @param id the ID of the stemmer override item to delete
151      * @return JSON response indicating the deletion status
152      */
153     // DELETE /api/admin/dict/stemmerOverride/setting/{dictId}/{id}
154     @Execute
155     public JsonResponse<ApiResult> delete$setting(final String dictId, final long id) {
156         stemmerOverrideService.getStemmerOverrideItem(dictId, id).ifPresent(entity -> {
157             stemmerOverrideService.delete(dictId, entity);
158             saveInfo(messages -> messages.addSuccessCrudDeleteCrudTable(GLOBAL));
159         }).orElse(() -> {
160             throwValidationErrorApi(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, String.valueOf(id)));
161         });
162         return asJson(new ApiResult.ApiUpdateResponse().id(String.valueOf(id)).created(false).status(ApiResult.Status.OK).result());
163     }
164 
165     /**
166      * Uploads stemmer override dictionary file.
167      *
168      * @param dictId the dictionary ID
169      * @param form the upload form containing the dictionary file
170      * @return JSON response with result status
171      */
172     // PUT /api/admin/dict/stemmerOverride/upload/{dictId}
173     @Execute
174     public JsonResponse<ApiResult> put$upload(final String dictId, final UploadForm form) {
175         form.dictId = dictId;
176         validateApi(form, messages -> {});
177         final StemmerOverrideFile file = stemmerOverrideService.getStemmerOverrideFile(form.dictId).orElseGet(() -> {
178             throwValidationErrorApi(messages -> messages.addErrorsFailedToUploadProtwordsFile(GLOBAL));
179             return null;
180         });
181         try (InputStream inputStream = form.stemmerOverrideFile.getInputStream()) {
182             file.update(inputStream);
183         } catch (final IOException e) {
184             logger.warn("Failed to process a request.", e);
185             throwValidationErrorApi(messages -> messages.addErrorsFailedToUploadProtwordsFile(GLOBAL));
186         }
187         return asJson(new ApiResult.ApiResponse().status(ApiResult.Status.OK).result());
188     }
189 
190     /**
191      * Downloads stemmer override dictionary file.
192      *
193      * @param dictId the dictionary ID
194      * @param body the download request body
195      * @return stream response containing the dictionary file data
196      */
197     // GET /api/admin/dict/stemmerOverride/download/{dictId}
198     @Execute
199     public StreamResponse get$download(final String dictId, final DownloadBody body) {
200         body.dictId = dictId;
201         validateApi(body, messages -> {});
202         return stemmerOverrideService.getStemmerOverrideFile(body.dictId)
203                 .map(file -> asStream(new File(file.getPath()).getName()).contentTypeOctetStream().stream(out -> {
204                     file.writeOut(out);
205                 }))
206                 .orElseGet(() -> {
207                     throwValidationErrorApi(messages -> messages.addErrorsFailedToDownloadProtwordsFile(GLOBAL));
208                     return null;
209                 });
210     }
211 
212     /**
213      * Creates an EditBody from a StemmerOverrideItem entity for API responses.
214      *
215      * @param entity the StemmerOverrideItem entity to convert
216      * @param dictId the dictionary ID
217      * @return the converted EditBody object
218      */
219     protected EditBody createEditBody(final StemmerOverrideItem entity, final String dictId) {
220         final EditBody body = new EditBody();
221         body.id = entity.getId();
222         body.dictId = dictId;
223         body.input = entity.getInput();
224         body.output = entity.getOutput();
225         return body;
226     }
227 }