Passed
Push — master ( 1647d0...c25ca7 )
by
unknown
01:17
created

com.hltech.pact.gen.domain.pact.PactFactory   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 181
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 117
dl 0
loc 181
rs 10
c 1
b 0
f 0
wmc 26

15 Methods

Rating   Name   Duplication   Size   Complexity  
A extractPathPrefix(Class) 0 3 1
A mapHeaders(List) 0 3 1
A createInteractionsFromMethod(ClientMethodRepresentationExtractor,Method,ObjectMapper,String) 0 18 1
A createFromFeignClient(Class,String,ObjectMapper) 0 12 1
A createDescription(String,ResponseRepresentation) 0 6 2
A parseParametersToQuery(List) 0 15 2
A prependPrefix(String,String) 0 16 4
A getParamValue(Param) 0 10 3
A createInteractionResponse(ResponseRepresentation,ObjectMapper) 0 9 1
A buildBody(ResponseRepresentation,ObjectMapper) 0 4 2
A extractProviderName(Class) 0 3 2
A manufacturePojo(Class) 0 8 2
A createInteractionsFromMethods(ClientMethodRepresentationExtractor,Method[],ObjectMapper,String) 0 11 1
A createInteractionRequest(RequestRepresentation,ObjectMapper,String) 0 10 1
A parsePath(String,String,List) 0 8 2
1
package com.hltech.pact.gen.domain.pact;
2
3
import com.fasterxml.jackson.databind.JsonNode;
4
import com.fasterxml.jackson.databind.ObjectMapper;
5
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
6
import com.hltech.pact.gen.PactGenerationException;
7
import com.hltech.pact.gen.domain.client.ClientMethodRepresentationExtractor;
8
import com.hltech.pact.gen.domain.client.annotation.handlers.AnnotatedMethodHandler;
9
import com.hltech.pact.gen.domain.client.annotation.handlers.DeleteMappingMethodsHandler;
10
import com.hltech.pact.gen.domain.client.annotation.handlers.GetMappingMethodsHandler;
11
import com.hltech.pact.gen.domain.client.annotation.handlers.PatchMappingMethodsHandler;
12
import com.hltech.pact.gen.domain.client.annotation.handlers.PostMappingMethodsHandler;
13
import com.hltech.pact.gen.domain.client.annotation.handlers.PutMappingMethodsHandler;
14
import com.hltech.pact.gen.domain.client.annotation.handlers.RequestMappingMethodsHandler;
15
import com.hltech.pact.gen.domain.client.feign.FeignMethodRepresentationExtractor;
16
import com.hltech.pact.gen.domain.client.model.ClientMethodRepresentation;
17
import com.hltech.pact.gen.domain.client.model.Param;
18
import com.hltech.pact.gen.domain.client.model.RequestRepresentation;
19
import com.hltech.pact.gen.domain.client.model.ResponseRepresentation;
20
import com.hltech.pact.gen.domain.pact.model.Interaction;
21
import com.hltech.pact.gen.domain.pact.model.InteractionRequest;
22
import com.hltech.pact.gen.domain.pact.model.InteractionResponse;
23
import com.hltech.pact.gen.domain.pact.model.Metadata;
24
import com.hltech.pact.gen.domain.pact.model.Pact;
25
import org.apache.commons.lang3.StringUtils;
26
import org.springframework.cloud.openfeign.FeignClient;
27
import uk.co.jemos.podam.api.PodamFactory;
28
import uk.co.jemos.podam.api.PodamFactoryImpl;
29
30
import java.lang.reflect.Method;
31
import java.util.Arrays;
32
import java.util.Collection;
33
import java.util.List;
34
import java.util.Map;
35
import java.util.stream.Collectors;
36
37
public class PactFactory {
38
39
    private static final PodamFactory podamFactory;
40
    private static final Collection<AnnotatedMethodHandler> annotatedMethodHandlers;
41
42
    static {
43
        podamFactory = new PodamFactoryImpl();
44
        podamFactory.getStrategy().addOrReplaceTypeManufacturer(String.class, new EnumStringManufacturer());
45
        podamFactory.getStrategy().setDefaultNumberOfCollectionElements(1);
46
    }
47
48
    static {
49
        annotatedMethodHandlers = Arrays.asList(
50
            new DeleteMappingMethodsHandler(), new GetMappingMethodsHandler(), new PatchMappingMethodsHandler(),
51
            new PostMappingMethodsHandler(), new PutMappingMethodsHandler(), new RequestMappingMethodsHandler());
52
    }
53
54
    public Pact createFromFeignClient(Class<?> feignClient, String consumerName, ObjectMapper objectMapper) {
55
56
        ClientMethodRepresentationExtractor methodExtractor =
57
            new FeignMethodRepresentationExtractor(annotatedMethodHandlers);
58
59
        return Pact.builder()
60
            .provider(new Service(extractProviderName(feignClient)))
61
            .consumer(new Service(consumerName))
62
            .interactions(createInteractionsFromMethods(
63
                methodExtractor, feignClient.getMethods(), objectMapper, extractPathPrefix(feignClient)))
64
            .metadata(new Metadata("1.0.0"))
65
            .build();
66
    }
67
68
    private String extractPathPrefix(Class<?> feignClient) {
69
        FeignClient feignClientAnnotation = feignClient.getAnnotation(FeignClient.class);
70
        return feignClientAnnotation.path();
71
    }
72
73
    private String extractProviderName(Class<?> feignClient) {
74
        FeignClient feignClientAnnotation = feignClient.getAnnotation(FeignClient.class);
75
        return feignClientAnnotation.value().isEmpty() ? feignClientAnnotation.name() : feignClientAnnotation.value();
76
    }
77
78
    private static List<Interaction> createInteractionsFromMethods(
79
        ClientMethodRepresentationExtractor extractor,
80
        Method[] clientMethods,
81
        ObjectMapper objectMapper,
82
        String pathPrefix) {
83
84
        return Arrays.stream(clientMethods)
85
            .filter(method -> annotatedMethodHandlers.stream()
86
                .anyMatch(handler -> handler.isSupported(method)))
87
            .flatMap(method -> createInteractionsFromMethod(extractor, method, objectMapper, pathPrefix).stream())
88
            .collect(Collectors.toList());
89
    }
90
91
    private static List<Interaction> createInteractionsFromMethod(
92
        ClientMethodRepresentationExtractor extractor,
93
        Method clientMethod,
94
        ObjectMapper objectMapper,
95
        String pathPrefix) {
96
97
        ClientMethodRepresentation methodRepresentation = extractor.extractClientMethodRepresentation(clientMethod);
98
99
        PojoValidator.validateAll(PojoExtractor.extractPojoTypes(methodRepresentation));
100
101
        return methodRepresentation.getResponseRepresentationList().stream()
102
            .map(interactionResponseRepresentation -> Interaction.builder()
103
                .description(createDescription(clientMethod.getName(), interactionResponseRepresentation))
104
                .request(
105
                    createInteractionRequest(methodRepresentation.getRequestRepresentation(), objectMapper, pathPrefix))
106
                .response(createInteractionResponse(interactionResponseRepresentation, objectMapper))
107
                .build())
108
            .collect(Collectors.toList());
109
    }
110
111
    private static String createDescription(String feignMethodName, ResponseRepresentation response) {
112
        if (response.getDescription().isEmpty()) {
113
            return String.format("%s request; %s response", feignMethodName, response.getStatus());
114
        }
115
116
        return response.getDescription();
117
    }
118
119
    private static InteractionRequest createInteractionRequest(
120
        RequestRepresentation requestRepresentation, ObjectMapper objectMapper, String pathPrefix) {
121
122
        return InteractionRequest.builder()
123
            .method(requestRepresentation.getHttpMethod().name())
124
            .path(parsePath(pathPrefix, requestRepresentation.getPath(), requestRepresentation.getPathParameters()))
125
            .headers(mapHeaders(requestRepresentation.getHeaders()))
126
            .query(parseParametersToQuery(requestRepresentation.getRequestParameters()))
127
            .body(BodySerializer.serializeBody(requestRepresentation.getBody(), objectMapper, podamFactory))
128
            .build();
129
    }
130
131
    private static String parsePath(String pathPrefix, String path, List<Param> pathParameters) {
132
        String resultPath = path;
133
        for (Param param : pathParameters) {
134
            Object paramValue = getParamValue(param);
135
136
            resultPath = resultPath.replace("{" + param.getName() + "}", String.valueOf(paramValue));
137
        }
138
        return prependPrefix(pathPrefix, resultPath);
139
    }
140
141
    private static String prependPrefix(String pathPrefix, String path) {
142
        if (pathPrefix.length() == 0) {
143
            return path;
144
        }
145
146
        StringBuilder builder = new StringBuilder(pathPrefix);
147
148
        if (pathPrefix.charAt(pathPrefix.length() - 1) == '/') {
149
            builder.deleteCharAt(pathPrefix.length() - 1);
150
        }
151
152
        if (path.charAt(0) != '/') {
153
            builder.append('/');
154
        }
155
156
        return builder.append(path).toString();
157
    }
158
159
    private static Object getParamValue(Param param) {
160
        if (param.getDefaultValue() != null) {
161
            return param.getDefaultValue();
162
        }
163
164
        if (param.getGenericArgumentType() != null) {
165
            return manufacturePojo(param.getGenericArgumentType());
166
        }
167
168
        return manufacturePojo(param.getType());
169
    }
170
171
    private static Object manufacturePojo(Class<?> type) {
172
        Object manufacturedPojo = podamFactory.manufacturePojo(type);
173
174
        if (manufacturedPojo == null) {
175
            throw new PactGenerationException("Podam manufacturing failed");
176
        }
177
178
        return manufacturedPojo;
179
    }
180
181
    private static String parseParametersToQuery(List<Param> requestParameters) {
182
        StringBuilder queryBuilder = new StringBuilder();
183
184
        requestParameters
185
            .forEach(param -> queryBuilder
186
                .append(param.getName())
187
                .append("=")
188
                .append(String.valueOf(getParamValue(param)))
189
                .append("&"));
190
191
        if (queryBuilder.length() != 0) {
192
            queryBuilder.deleteCharAt(queryBuilder.length() - 1);
193
        }
194
195
        return queryBuilder.toString();
196
    }
197
198
    private static InteractionResponse createInteractionResponse(
199
        ResponseRepresentation responseRepresentation,
200
        ObjectMapper objectMapper) {
201
202
        return InteractionResponse.builder()
203
                .status(responseRepresentation.getStatus().toString())
204
                .headers(mapHeaders(responseRepresentation.getHeaders()))
205
                .body(buildBody(responseRepresentation, objectMapper))
206
                .build();
207
    }
208
209
    private static Map<String, String> mapHeaders(List<Param> headers) {
210
        return headers.stream()
211
            .collect(Collectors.toMap(Param::getName, header -> String.valueOf(getParamValue(header))));
212
    }
213
214
    private static JsonNode buildBody(ResponseRepresentation responseRepresentation, ObjectMapper objectMapper) {
215
        return responseRepresentation.isEmptyBodyExpected()
216
            ? JsonNodeFactory.instance.textNode(StringUtils.EMPTY)
217
            : BodySerializer.serializeBody(responseRepresentation.getBody(), objectMapper, podamFactory);
218
    }
219
}
220