ã¯ããã«
ã§OpenAPIï¼MicroProfileï¼ã®å®è£
ããã¦ã¿ã¾ããããããå°ã楽ãã§ããããã«ãããã¨æãã¾ããã
ãã¨ãã£ã¦å®è£
ãã®ãã®ãæ¡å¼µããã大ããããªãã®ãã¤ããã»ã©ã§ãç¡ããç¨ããç¨åº¦ã«ãæ¡å¼µããã¦ã¿ã¾ããã
æ¡å¼µãã¤ã³ã
ããã¸ã§ã¯ãå ¨ä½ã®å®ç¾©
OpenApiModelReader
ï¼OASModelReaderã®å®è£
ï¼ã«
- Lisence
- ããã¥ã¡ã³ã
- ã»ãã¥ãªãã£ï¼ãã¼ã¯ã³ã®è¨å®ï¼
ãªã©ã®ããã¸ã§ã¯ãã®åºæ¬æ å ±ãè¨å®ãã¾ãã
ãªãã¸ã§ã¯ãã®Schemaã追å
OpenApiSchema
ã«å¤æããããã¯ã©ã¹ã¨ ref
ã§ä½¿ç¨ãããã¼å¤ãç»é²ãã¾ãã
public static final String Gender = prifixPath + "Gender";
ãã¼å¤ã®ãã¢ã¨ãªããSchemaçæé¢æ°ãç»é²ãã¾ã.
// openApiã®Utilã¯å®è£ ä¾åã¨ãªãããå®è¡æã«æ±ºå®ããããã«é¢æ°ã§æå®ãã¾ã. private static final Map<String, Supplier<Schema>> schemaMap = Map.ofEntries( entry(Gender, () -> OpenApiSchemaUtil.createEnumSchema(Gender.class)), entry(Genders, () -> OpenApiSchemaUtil.createEnumListSchema(Gender.class)), entry(UploadFile, () -> OpenApiSchemaUtil.createUploadFileSchema()), entry(UploadFiles, () -> OpenApiSchemaUtil.createUploadFileListSchema()));
å®éã«çæããã®ã¯ OpenApiModelReader#buildModel
å
ã§OpenApiSchema.appendSchema(components);
ã§è¡ãã¾ãã
ç»é²ãããã¼å¤ã¯@Schema
ã®ref
ã¨ãã¦æå®ããã¾ãã
@Parameters({ @Parameter(name = "gender", description = "æ§å¥", schema = @Schema(ref = OpenApiSchema.Gender)), @Parameter(name = "name", description = "ã¦ã¼ã¶ã¼å", example = "user name") }) public Response getUsersByQuery( @QueryParam("gender") Gender gender, @QueryParam("name") String name) {
ãªã«ãããããã
ã¹ãã¼ãã®æå®ãã¯ã©ã¹ã§æå®ã§ããã®ã§ãæ¸ãééãã«ããå®è¡æã¨ã©ã¼ãåé¿ã§ãã¾ãã
å®è£
ref
ã®æå®ã¨ãã¦ã¡ã½ããã使ç¨ã§ããªããããå®è£
ã¨ãã¦ã¯ãçä¼¼ Enumãªã¹ãã©ãã¸ã¼ããä½ã£ã¦å¯¾å¿ããã¾ããã
/** OpenApiSchemaã®å¤æããã³Schemaã«refã«æå®ããå®æ°ã管çãã¾ã. */ public class OpenApiSchema { private static final String prifixPath = "#/components/schemas/"; public static final String Gender = prifixPath + "Gender"; public static final String Genders = prifixPath + "Genders"; public static final String UploadFile = prifixPath + "UploadFile"; public static final String UploadFiles = prifixPath + "UploadFiles"; // openApiã®Utilã¯å®è£ ä¾åã¨ãªãããå®è¡æã«æ±ºå®ããããã«é¢æ°ã§æå®ãã¾ã. private static final Map<String, Supplier<Schema>> schemaMap = Map.ofEntries( entry(Gender, () -> OpenApiSchemaUtil.createEnumSchema(Gender.class)), entry(Genders, () -> OpenApiSchemaUtil.createEnumListSchema(Gender.class)), entry(UploadFile, () -> OpenApiSchemaUtil.createUploadFileSchema()), entry(UploadFiles, () -> OpenApiSchemaUtil.createUploadFileListSchema())); /** * ããããã£ã§æå®ããã¯ã©ã¹ãSchemaã¸å¤æãã¦OpenAPIã®ã³ã³ãã¼ãã³ãã¸è¿½è¨ãã¾ã. * * @param components OpenAPIã®components */ public static void appendSchema(Components components) { validate(); int startIndex = prifixPath.length(); schemaMap.entrySet().stream() .forEach( entrySet -> { var key = entrySet.getKey().substring(startIndex); components.addSchema(key, entrySet.getValue().get()); }); } /** Publicãã£ã¼ã«ãã¨ã¹ãã¼ãã®è¨å®ãããMapã®æ´åæ§ãåãã¦ãããã¨ãæ¤è¨¼ãã¾ã. */ private static void validate() { var fieldList = Stream.of(OpenApiSchema.class.getFields()) .filter(f -> f.getType().isPrimitive() == false) .filter(f -> f.getType().isInstance("")) .map(f -> f.getName()) .collect(Collectors.toSet()); if (schemaMap.entrySet().size() != fieldList.size()) { throw new IllegalArgumentException( "public static field is not match schemaMap. append Schema must match."); } } }
@ExampleObjectã«jsonãæå®
@ExampleObject
ã®value
ã«jsonãè¨è¿°ããããã¨ãã§ãã¾ãããresourceé
ä¸ã®jsonãã¡ã¤ã«ãæå®ãããã¨ã¯ã§ãã¾ããã
æ¬æ¥ãexternalValue
ã¯http
ã使ç¨ãã¦å¤é¨ãªã½ã¼ã¹ãåç
§ãããã®ã§ããããã¾ã使ç¨ãããã¨ã¯ç¡ãã¨èããexternalValue
ã«ãªã½ã¼ã¹ãã¹ãæå®ãã¦èªã¿è¾¼ããããã«ãã¾ããã
externalValue = "openapi/user/get_response_default.json"
src/main/resources/openapi/user/get_response_default.json
å
é¨çã«ã¯ãexternalValue
ã®jsonãå±éããçµæãvalue
ã¸è»¢è¨ãã¦ãOpenAPI.yamlã¨ãã¦ããã®ã¾ã¾ä½¿ããããã«ãã¦ãã¾ãã
ãªã«ãããããã
åãã¬ã¹ãã³ã¹ã®åã使ã£ãExampleã®è¨è¿°ãå®çµã«è¨è¼ã§ãã¾ãã
ã¾ãã¨ã¹ã±ã¼ãã®ç¡ãjsonã®è¨è¿°ãã§ããã®ã§è¦ãããã§ãã
å®è£
OASFilter
ã®å®è£
ã§ããOpenApiFilter
ã§ãªã¯ã¨ã¹ãããã£ã¨ã¬ã¹ãã³ã¹ããã£ã®@ExampleObject
ã®ä¸èº«ãæ¸ãæãã¾ã.
/** OpenAPIã®OASFilterã®å®è£ . */ public class OpenApiFilter implements OASFilter { @Override public RequestBody filterRequestBody(RequestBody requestBody) { OpenApiExampleObjectUtil.convertExternalValueToValue(requestBody.getContent()); return OASFilter.super.filterRequestBody(requestBody); } @Override @SuppressWarnings("checkstyle:AbbreviationAsWordInName") public APIResponse filterAPIResponse(APIResponse apiResponse) { OpenApiExampleObjectUtil.convertExternalValueToValue(apiResponse.getContent()); return OASFilter.super.filterAPIResponse(apiResponse); } }
/** OpenApiExampleObjectUtil. */ public class OpenApiExampleObjectUtil { /** * ExampleObjectã®ExternalValueã§æå®ããJsonãValueã¨ãã¦å±éãã¾ã. * * <p>Contentãç´æ¥ä¸æ¸ããã¾ã. * * <p>ä¸æ¸ãã«ä½¿ç¨ãã externalValue ã¯æ¶å»ãã¾ã. * * <p>valueã«è¨è¿°ãããå ´åã¯valueã®è¨è¿°ãåªå ãã¾ã. * * @param content openApiã®@Content * @throws UncheckedIOException IOExceptionãçºçãããå¦çãä¸æãã¾ã. */ public static void convertExternalValueToValue(Content content) { content.getMediaTypes().entrySet().stream() .filter(e -> Objects.nonNull(e)) .forEach( e1 -> { e1.getValue().getExamples().entrySet().stream() .filter( e2 -> Objects.isNull(e2.getValue().getValue()) || e2.getValue().getValue().equals("")) .filter(e2 -> Objects.nonNull(e2.getValue().getExternalValue())) .forEach( example -> { var externalValue = example.getValue().getExternalValue(); ClassLoader loader = OpenApiExampleObjectUtil.class.getClassLoader(); try (var inputStream = loader.getResourceAsStream(externalValue)) { if (Objects.isNull(inputStream)) { throw new FileNotFoundException( "externalValue =[" + externalValue + "] cloud not find resource path."); } String json = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); example.getValue().setValue(json); example.getValue().setExternalValue(""); } catch (IOException ex) { throw new UncheckedIOException( "externalValue =[" + externalValue + "] could not find resource path or could not read resource file.", ex); } }); }); } }
ã¢ãã¯ã¨ãã¦jsonã使ç¨ãã
OpenAPIã¨ããããããRESTfulAPIã便å©ã«ãããã®ã§ãã
éçºåæã§ã¯ãã¤ã³ã¿ã¼ãã§ã¼ã¹ã¨ãã¦ã®OpenAPIã®å®ç¾©è¨å®ã«ããã㦠ã¢ããªã±ã¼ã·ã§ã³ãµã¼ãã¼ãã¢ãã¯ãµã¼ãã¨ãã¦ä½¿ç¨ãããã±ã¼ã¹ãããã¾ãã
ã¡ã½ããã®æ»ãå¤ã®åã¯Response
ã§ã¯ãªããç´æ¥ã¬ã¹ãã³ã¹ãè¿å´ãã¦ãã¾ãã
ï¼ãã ããå®è¡çµæã®Httpã200ãåºå®ã«ãªãã¾ãï¼
public UserResponse.UserResponseBody getJsonUserById(@PathParam("id") String id) { var response = JsonUtil.readFromResource( "openapi/user/get_response_default.json", UserResponse.UserResponseBody.class); return response.get(); }
ãªã«ãããããã
ãã§ã«ä½ææ¸ã¿ã®@ExampleObject
ã®jsonããã®ã¾ã¾ä½¿ç¨ãããã¨ãã§ããã®ã§æ¥½ãã§ãã¾ãã
å
¥åå¤ã§è¿å´å¤ãå¤ãããå ´åã¯ãè¤æ°ã®jsonãä½æãã¦å¼æ°ãå
ã«è¿å´ãåãæ¿ããããã«ããã ãã§å¯¾å¿ã§ãã¾ãã
å®è£
jsonã®èªã¿è¾¼ã¿ã§jackson
ã使ç¨ããã®ã§ãpom.xmlã«ä¾åã追å ãã¾ãã
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.16.1</version> <type>jar</type> </dependency>
ã¢ãã¯ã§ã®ä½¿ç¨ãåæã¨ãã¦ããã®ã§ãJsonã®æä½æã®ã¨ã©ã¼ãã³ããªã³ã°ãç°¡ç¥åãã¦*1å®è¡æä¾å¤ã¯ããã¦å¡ãã¤ã¶ãã¦ãã¾ãã
/** JsonUtil. */ public class JsonUtil { private static final Logger logger = Logger.getLogger(JsonUtil.class.getName()); /** * Jsonæååãã¯ã©ã¹ã«ãããã³ã°ãã¾ã. * * @param <T> ãããã³ã°ããã¯ã©ã¹ã®å * @param json jsonæåå * @param classType ãããã³ã°ããã¯ã©ã¹ã®å * @return ãããã³ã°ããã¤ã³ã¹ã¿ã³ã¹.ä¾å¤ããã£ãå ´å㯠{@code Optional.empty()} */ public static <T> Optional<T> read(String json, Class<T> classType) { ObjectMapper mapper = new ObjectMapper(); try { T object = mapper.readValue(json, classType); return Optional.of(object); } catch (JsonProcessingException ex) { logger.log(Level.SEVERE, "Json could not parse.", ex); return Optional.empty(); } } /** * Jsonãªã½ã¼ã¹ãã¯ã©ã¹ã«ãããã³ã°ãã¾ã. * * @param <T> ãããã³ã°ããã¯ã©ã¹ã®å * @param resourcePath ãªã½ã¼ã¹ãã¹ * @param classType ãããã³ã°ããã¯ã©ã¹ã®å * @return ãããã³ã°ããã¤ã³ã¹ã¿ã³ã¹.ä¾å¤ããã£ãå ´å㯠{@code Optional.empty()} */ public static <T> Optional<T> readFromResource(String resourcePath, Class<T> classType) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); try (var inputStream = loader.getResourceAsStream(resourcePath)) { if (Objects.isNull(inputStream)) { throw new FileNotFoundException( "resourcePath =[" + resourcePath + "] cloud not find resource path."); } String json = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8); return read(json, classType); } catch (IOException ex) { logger.log(Level.SEVERE, "resourcePath =[" + resourcePath + "] ioexception.", ex); } return Optional.empty(); } }
Code
experimentation/ee10-02-openapi at openapi-extend · vermeerlab/experimentation · GitHub
ãããã«
è²ã
ã¨èªåçæçãªãã¨ããããã¨ãæã£ãã®ã§ãããæ¨æºæ©è½ã«ããæ¡å¼µãã¤ã³ãã§åºæ¥ããã¨ã«ããã¦éå®ãã¦ããã»ãããEEç³»ã®å ´åã«ã¯è¯ãããªï¼ã¨æã£ã¦ ãã®ãããã«ãã¾ããããããã§ãéåã¨åé·ãªè¨è¿°ãæ¸ãã®ã§ã¯ãªãããªï¼ã¨æã£ã¦ãã¾ãã
ããã§ã¯Payaraã使ã£ã¦ãããããMicroProfileã®å®è£
ã¨ãã¦ã¯æ£ç´è²§å¼±ã ã¨æãã¾ãããQuarkusãHelidonã¨ãã£ãMicroProfileã®å®è£
ã使ãã°ï¼importã«Quarkusã®ã©ã¤ãã©ãªã使ããããªæ¡å¼µã使ãã°ï¼ããã£ã¨ä¾¿å©ãªãã®ããã§ã«æä¾ããã¦ããå¯è½æ§ã¯å¤§ãã«ããã¾ãã
*1:Eitherã®ä»£ããã«Optionalã§ç°¡æçã«å¦ç½®