001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.bean.openapi3;
018
019import static org.apache.juneau.commons.utils.AssertionUtils.*;
020import static org.apache.juneau.commons.utils.CollectionUtils.*;
021import static org.apache.juneau.commons.utils.Utils.*;
022import static org.apache.juneau.internal.ConverterUtils.*;
023
024import java.util.*;
025
026import org.apache.juneau.commons.collections.*;
027
028/**
029 * Configuration details for a supported OAuth Flow.
030 *
031 * <p>
032 * The OAuthFlow Object provides configuration details for a supported OAuth Flow. This object contains the URLs and
033 * scopes needed to configure a specific OAuth 2.0 flow. Different flows require different combinations of URLs and
034 * have different security characteristics.
035 *
036 * <h5 class='section'>OpenAPI Specification:</h5>
037 * <p>
038 * The OAuthFlow Object is composed of the following fields:
039 * <ul class='spaced-list'>
040 *    <li><c>authorizationUrl</c> (string) - The authorization URL to be used for this flow. This MUST be in the form of a URL
041 *    <li><c>tokenUrl</c> (string) - The token URL to be used for this flow. This MUST be in the form of a URL
042 *    <li><c>refreshUrl</c> (string) - The URL to be used for obtaining refresh tokens. This MUST be in the form of a URL
043 *    <li><c>scopes</c> (map of strings) - The available scopes for the OAuth2 security scheme. A map between the scope name and a short description for it
044 * </ul>
045 *
046 * <h5 class='section'>Example:</h5>
047 * <p class='bcode'>
048 *    <jc>// Construct using SwaggerBuilder.</jc>
049 *    OAuthFlow <jv>x</jv> = <jsm>oauthFlow</jsm>()
050 *       .setAuthorizationUrl(<js>"https://example.com/oauth/authorize"</js>)
051 *       .setTokenUrl(<js>"https://example.com/oauth/token"</js>)
052 *       .setScopes(<jsm>map</jsm>(<js>"read"</js>, <js>"Read access"</js>, <js>"write"</js>, <js>"Write access"</js>));
053 *
054 *    <jc>// Serialize using JsonSerializer.</jc>
055 *    String <jv>json</jv> = Json.<jsm>from</jsm>(<jv>x</jv>);
056 *
057 *    <jc>// Or just use toString() which does the same as above.</jc>
058 *    <jv>json</jv> = <jv>x</jv>.toString();
059 * </p>
060 * <p class='bcode'>
061 *    <jc>// Output</jc>
062 *    {
063 *       <js>"authorizationUrl"</js>: <js>"https://example.com/oauth/authorize"</js>,
064 *       <js>"tokenUrl"</js>: <js>"https://example.com/oauth/token"</js>,
065 *       <js>"scopes"</js>: {
066 *          <js>"read"</js>: <js>"Read access"</js>,
067 *          <js>"write"</js>: <js>"Write access"</js>
068 *       }
069 *    }
070 * </p>
071 *
072 * <h5 class='section'>See Also:</h5><ul>
073 *    <li class='link'><a class="doclink" href="https://spec.openapis.org/oas/v3.0.0#oauth-flow-object">OpenAPI Specification &gt; OAuth Flow Object</a>
074 *    <li class='link'><a class="doclink" href="https://swagger.io/docs/specification/authentication/oauth2/">OpenAPI OAuth2 Authentication</a>
075 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanOpenApi3">juneau-bean-openapi-v3</a>
076 * </ul>
077 */
078public class OAuthFlow extends OpenApiElement {
079
080   private String authorizationUrl;
081   private String tokenUrl;
082   private String refreshUrl;
083   private Map<String,String> scopes = map();
084
085   /**
086    * Default constructor.
087    */
088   public OAuthFlow() {}
089
090   /**
091    * Copy constructor.
092    *
093    * @param copyFrom The object to copy.
094    */
095   public OAuthFlow(OAuthFlow copyFrom) {
096      super(copyFrom);
097
098      this.authorizationUrl = copyFrom.authorizationUrl;
099      this.tokenUrl = copyFrom.tokenUrl;
100      this.refreshUrl = copyFrom.refreshUrl;
101      if (nn(copyFrom.scopes))
102         scopes.putAll(copyFrom.scopes);
103   }
104
105   /**
106    * Adds a single value to the <property>examples</property> property.
107    *
108    * @param name The mime-type string.  Must not be <jk>null</jk>.
109    * @param description The example.  Must not be <jk>null</jk>.
110    * @return This object
111    */
112   public OAuthFlow addScope(String name, String description) {
113      assertArgNotNull("name", name);
114      assertArgNotNull("description", description);
115      scopes.put(name, description);
116      return this;
117   }
118
119   /**
120    * Make a deep copy of this object.
121    *
122    * @return A deep copy of this object.
123    */
124   public OAuthFlow copy() {
125      return new OAuthFlow(this);
126   }
127
128   @Override /* Overridden from OpenApiElement */
129   public <T> T get(String property, Class<T> type) {
130      assertArgNotNull("property", property);
131      return switch (property) {
132         case "refreshUrl" -> toType(getRefreshUrl(), type);
133         case "tokenUrl" -> toType(getTokenUrl(), type);
134         case "authorizationUrl" -> toType(getAuthorizationUrl(), type);
135         case "scopes" -> toType(getScopes(), type);
136         default -> super.get(property, type);
137      };
138   }
139
140   /**
141    * Bean property getter:  <property>operationRef</property>.
142    *
143    * <p>
144    * The identifying name of the contact person/organization.
145    *
146    * @return The property value, or <jk>null</jk> if it is not set.
147    */
148   public String getAuthorizationUrl() { return authorizationUrl; }
149
150   /**
151    * Bean property getter:  <property>externalValue</property>.
152    *
153    * <p>
154    * The email address of the contact person/organization.
155    *
156    * @return The property value, or <jk>null</jk> if it is not set.
157    */
158   public String getRefreshUrl() { return refreshUrl; }
159
160   /**
161    * Bean property getter:  <property>examples</property>.
162    *
163    * <p>
164    * An example of the response message.
165    *
166    * @return The property value, or <jk>null</jk> if it is not set.
167    */
168   public Map<String,String> getScopes() { return nullIfEmpty(scopes); }
169
170   /**
171    * Bean property getter:  <property>description</property>.
172    *
173    * <p>
174    * The URL pointing to the contact information.
175    *
176    * @return The property value, or <jk>null</jk> if it is not set.
177    */
178   public String getTokenUrl() { return tokenUrl; }
179
180   @Override /* Overridden from OpenApiElement */
181   public Set<String> keySet() {
182      // @formatter:off
183      var s = setb(String.class)
184         .addIf(nn(authorizationUrl), "authorizationUrl")
185         .addIf(nn(refreshUrl), "refreshUrl")
186         .addIf(ne(scopes), "scopes")
187         .addIf(nn(tokenUrl), "tokenUrl")
188         .build();
189      // @formatter:on
190      return new MultiSet<>(s, super.keySet());
191   }
192
193   @Override /* Overridden from OpenApiElement */
194   public OAuthFlow set(String property, Object value) {
195      assertArgNotNull("property", property);
196      return switch (property) {
197         case "authorizationUrl" -> setAuthorizationUrl(s(value));
198         case "refreshUrl" -> setRefreshUrl(s(value));
199         case "scopes" -> setScopes(toMapBuilder(value, String.class, String.class).sparse().build());
200         case "tokenUrl" -> setTokenUrl(s(value));
201         default -> {
202            super.set(property, value);
203            yield this;
204         }
205      };
206   }
207
208   /**
209    * Bean property setter:  <property>operationRef</property>.
210    *
211    * <p>
212    * The identifying name of the contact person/organization.
213    *
214    * @param value
215    *    The new value for this property.
216    *    <br>Can be <jk>null</jk> to unset the property.
217    * @return This object
218    */
219   public OAuthFlow setAuthorizationUrl(String value) {
220      authorizationUrl = value;
221      return this;
222   }
223
224   /**
225    * Bean property setter:  <property>externalValue</property>.
226    *
227    * <p>
228    * The email address of the contact person/organization.
229    *
230    * @param value
231    *    The new value for this property.
232    *    <br>MUST be in the format of an email address.
233    *    <br>Can be <jk>null</jk> to unset the property.
234    * @return This object
235    */
236   public OAuthFlow setRefreshUrl(String value) {
237      refreshUrl = value;
238      return this;
239   }
240
241   /**
242    * Bean property setter:  <property>examples</property>.
243    *
244    * <p>
245    * An example of the response message.
246    *
247    * @param value
248    *    The new value for this property.
249    *    <br>Keys must be MIME-type strings.
250    *    <br>Can be <jk>null</jk> to unset the property.
251    * @return This object
252    */
253   public OAuthFlow setScopes(Map<String,String> value) {
254      scopes.clear();
255      if (nn(value))
256         scopes.putAll(value);
257      return this;
258   }
259
260   /**
261    * Bean property setter:  <property>description</property>.
262    * @param value
263    *    The new value for this property.
264    *    <br>Can be <jk>null</jk> to unset the property.
265    * @return This object
266    */
267   public OAuthFlow setTokenUrl(String value) {
268      tokenUrl = value;
269      return this;
270   }
271
272   @Override /* Overridden from OpenApiElement */
273   public OAuthFlow strict() {
274      super.strict();
275      return this;
276   }
277
278   @Override /* Overridden from OpenApiElement */
279   public OAuthFlow strict(Object value) {
280      super.strict(value);
281      return this;
282   }
283}