1   /*
2    * Copyright 2018 LINE Corporation
3    *
4    * LINE Corporation licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package com.linecorp.centraldogma.server.internal.api.converter;
18  
19  import java.lang.reflect.ParameterizedType;
20  
21  import javax.annotation.Nullable;
22  
23  import com.google.common.base.Splitter;
24  import com.google.common.collect.ImmutableList;
25  import com.google.common.collect.ImmutableList.Builder;
26  
27  import com.linecorp.armeria.common.AggregatedHttpRequest;
28  import com.linecorp.armeria.server.ServiceRequestContext;
29  import com.linecorp.armeria.server.annotation.RequestConverterFunction;
30  import com.linecorp.centraldogma.common.MergeQuery;
31  import com.linecorp.centraldogma.common.MergeSource;
32  
33  import io.netty.handler.codec.http.QueryStringDecoder;
34  
35  public class MergeQueryRequestConverter implements RequestConverterFunction {
36  
37      private static final Splitter querySplitter = Splitter.on('&').trimResults().omitEmptyStrings();
38  
39      @Override
40      public MergeQuery<?> convertRequest(
41              ServiceRequestContext ctx, AggregatedHttpRequest request, Class<?> expectedResultType,
42              @Nullable ParameterizedType expectedParameterizedResultType) throws Exception {
43  
44          final String queryString = ctx.query();
45          if (queryString != null) {
46              // Decode queryString manually so that the original order of "path" and "optional_path" is
47              // preserved. For example if a user specifies the query as
48              // "path=/a.json&optional_path=b.json&path=c.json", the mergeQuery will merge the files
49              // in the order of "/a.json", "/b.json" and "/c.json".
50              final String decodedString = QueryStringDecoder.decodeComponent(queryString);
51              final Iterable<String> queries = querySplitter.split(decodedString);
52              final Builder<MergeSource> mergeSourceBuilder = ImmutableList.builder();
53              final Builder<String> jsonPathsBuilder = ImmutableList.builder();
54              for (String query : queries) {
55                  final int index = query.indexOf('=');
56                  if (index < 0) {
57                      continue;
58                  }
59                  final String key = query.substring(0, index);
60                  final String value = query.substring(index + 1);
61                  switch (key) {
62                      case "path":
63                          mergeSourceBuilder.add(MergeSource.ofRequired(value));
64                          break;
65                      case "optional_path":
66                          mergeSourceBuilder.add(MergeSource.ofOptional(value));
67                          break;
68                      case "jsonpath":
69                          jsonPathsBuilder.add(value);
70                          break;
71                  }
72              }
73  
74              return MergeQuery.ofJsonPath(mergeSourceBuilder.build(), jsonPathsBuilder.build());
75          }
76          return RequestConverterFunction.fallthrough();
77      }
78  }