View Javadoc
1   /*
2    * The contents of this file are subject to the terms of the Common Development and
3    * Distribution License (the License). You may not use this file except in compliance with the
4    * License.
5    *
6    * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
7    * specific language governing permission and limitations under the License.
8    *
9    * When distributing Covered Software, include this CDDL Header Notice in each file and include
10   * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
11   * Header, with the fields enclosed by brackets [] replaced by your own identifying
12   * information: "Portions copyright [year] [name of copyright owner]".
13   *
14   * Copyright 2016 ForgeRock AS.
15   */
16  
17  package org.forgerock.api;
18  
19  import static java.util.Collections.*;
20  import static org.forgerock.api.models.ApiDescription.*;
21  import static org.forgerock.api.models.Definitions.*;
22  import static org.forgerock.api.models.Errors.*;
23  import static org.forgerock.api.models.Paths.*;
24  import static org.forgerock.api.models.Services.services;
25  import static org.forgerock.api.models.VersionedPath.*;
26  
27  import java.util.List;
28  import java.util.Set;
29  
30  import org.forgerock.api.models.ApiDescription;
31  import org.forgerock.api.models.Definitions;
32  import org.forgerock.api.models.Errors;
33  import org.forgerock.util.i18n.LocalizableString;
34  import org.forgerock.api.models.Paths;
35  import org.forgerock.api.models.Services;
36  import org.forgerock.api.models.VersionedPath;
37  import org.forgerock.http.routing.Version;
38  import org.forgerock.http.ApiProducer;
39  import org.slf4j.Logger;
40  import org.slf4j.LoggerFactory;
41  
42  /**
43   * An {@link ApiProducer} implementation for CREST resources, that provides {@code ApiDescription} descriptors.
44   */
45  public class CrestApiProducer implements ApiProducer<ApiDescription> {
46  
47      private final String version;
48      private final String id;
49      private final LocalizableString description;
50      private static final Logger LOGGER = LoggerFactory.getLogger(CrestApiProducer.class);
51  
52      /**
53       * Construct a new producer.
54       * @param id The API ID fragment for this producer.
55       * @param apiVersion The version of the API being described.
56       * @param description The API description.
57       */
58      public CrestApiProducer(String id, String apiVersion, LocalizableString description) {
59          this.id = id;
60          this.version = apiVersion;
61          this.description = description;
62      }
63  
64      /**
65       * Construct a new producer.
66       * @param id The API ID fragment for this producer.
67       * @param apiVersion The version of the API being described.
68       */
69      public CrestApiProducer(String id, String apiVersion) {
70          this(id, apiVersion, null);
71      }
72  
73      @Override
74      public ApiDescription withPath(ApiDescription api, String parentPath) {
75          Paths.Builder paths = paths();
76          Set<String> names = api.getPaths().getNames();
77          for (String subpath : names) {
78              paths.put(subpath.equals("") ? parentPath : parentPath + "/" + subpath,
79                      api.getPaths().get(subpath));
80          }
81          return createApi(api.getDefinitions(), api.getErrors(), api.getServices(), paths.build());
82      }
83  
84      @Override
85      public ApiDescription withVersion(ApiDescription api, Version version) {
86          Paths.Builder paths = paths();
87          Set<String> names = api.getPaths().getNames();
88          for (String path : names) {
89              VersionedPath versionedPath = api.getPaths().get(path);
90              if (singleton(UNVERSIONED).equals(versionedPath.getVersions())) {
91                  paths.put(path, versionedPath().put(version, versionedPath.get(UNVERSIONED)).build());
92              } else {
93                  throw new IllegalStateException("Trying to version something already versioned: " + versionedPath);
94              }
95          }
96          return createApi(api.getDefinitions(), api.getErrors(), api.getServices(), paths.build());
97      }
98  
99      @Override
100     public ApiDescription merge(List<ApiDescription> descriptions) {
101         Paths.Builder paths = paths();
102         Definitions.Builder definitions = definitions();
103         Errors.Builder errors = errors();
104         Services.Builder services = services();
105         for (ApiDescription description : descriptions) {
106             if (description != null) {
107                 try {
108                     if (description.getDefinitions() != null) {
109                         for (String definition : description.getDefinitions().getNames()) {
110                             definitions.put(definition, description.getDefinitions().get(definition));
111                         }
112                     }
113                     if (description.getErrors() != null) {
114                         for (String error : description.getErrors().getNames()) {
115                             errors.put(error, description.getErrors().get(error));
116                         }
117                     }
118                     if (description.getServices() != null) {
119                         for (String service : description.getServices().getNames()) {
120                             services.put(service, description.getServices().get(service));
121                         }
122                     }
123                     if (description.getPaths() != null) {
124                         for (String path : description.getPaths().getNames()) {
125                             paths.merge(path, description.getPaths().get(path));
126                         }
127                     }
128                 } catch (RuntimeException re) {
129                     LOGGER.error(re.getMessage(), re.fillInStackTrace());
130                     throw re;
131                 }
132             }
133         }
134         return createApi(definitions.build(), errors.build(), services.build(), paths.build());
135     }
136 
137     @Override
138     public ApiDescription addApiInfo(ApiDescription api) {
139         return createApi(api.getDefinitions(), api.getErrors(), api.getServices(), api.getPaths());
140     }
141 
142     private ApiDescription createApi(Definitions definitions, Errors errors, Services services, Paths paths) {
143         return apiDescription()
144                 .definitions(definitions)
145                 .errors(errors)
146                 .services(services)
147                 .paths(paths)
148                 .id(this.id)
149                 .version(this.version)
150                 .description(this.description)
151                 .build();
152     }
153 
154     @Override
155     public ApiProducer<ApiDescription> newChildProducer(String idFragment) {
156         return new CrestApiProducer(id + idFragment, version);
157     }
158 }