All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jreleaser.engine.schema.JsonSchemaGenerator Maven / Gradle / Ivy

The newest version!
/*
 * SPDX-License-Identifier: Apache-2.0
 *
 * Copyright 2020-2024 The JReleaser authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jreleaser.engine.schema;

import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.github.victools.jsonschema.generator.Option;
import com.github.victools.jsonschema.generator.OptionPreset;
import com.github.victools.jsonschema.generator.SchemaGenerationContext;
import com.github.victools.jsonschema.generator.SchemaGenerator;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfig;
import com.github.victools.jsonschema.generator.SchemaGeneratorConfigBuilder;
import com.github.victools.jsonschema.generator.SchemaVersion;
import com.github.victools.jsonschema.generator.impl.DefinitionKey;
import com.github.victools.jsonschema.generator.naming.DefaultSchemaDefinitionNamingStrategy;
import com.github.victools.jsonschema.module.jackson.JacksonModule;
import org.jreleaser.model.JReleaserException;
import org.jreleaser.model.JReleaserVersion;
import org.jreleaser.model.internal.JReleaserModel;
import org.jreleaser.model.internal.announce.Announce;
import org.jreleaser.model.internal.announce.HttpAnnouncer;
import org.jreleaser.model.internal.announce.WebhookAnnouncer;
import org.jreleaser.model.internal.download.AbstractSshDownloader;
import org.jreleaser.model.internal.download.FtpDownloader;
import org.jreleaser.model.internal.download.HttpDownloader;
import org.jreleaser.model.internal.upload.AbstractSshUploader;
import org.jreleaser.model.internal.upload.ArtifactoryUploader;
import org.jreleaser.model.internal.upload.FtpUploader;
import org.jreleaser.model.internal.upload.HttpUploader;

import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.LinkedHashMap;
import java.util.Map;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static java.nio.file.StandardOpenOption.WRITE;
import static java.util.Collections.singletonMap;
import static org.jreleaser.bundle.RB.$;

/**
 * @author Andres Almiray
 * @since 1.4.0
 */
public final class JsonSchemaGenerator {
    private JsonSchemaGenerator() {
        // noop
    }

    public static void generate(PrintWriter out) {
        Map mappings = new LinkedHashMap<>();
        mappings.put("Map", "Properties");
        mappings.put("Map", "StringProperties");
        mappings.put("Map", "WebhookAnnouncerMap");
        mappings.put("Map", "HttpAnnouncerMap");
        mappings.put("Map", "ArchiveAssemblerMap");
        mappings.put("Map", "JavaArchiveAssemblerMap");
        mappings.put("Map", "JlinkAssemblerMap");
        mappings.put("Map", "JpackageAssemblerMap");
        mappings.put("Map", "NativeImageAssemblerMap");
        mappings.put("Map", "DistributionMap");
        mappings.put("Map", "DockerSpecMap");
        mappings.put("Map", "JibSpecMap");
        mappings.put("Map", "ArtifactoryUploaderMap");
        mappings.put("Map", "GiteaUploaderMap");
        mappings.put("Map", "GitlabUploaderMap");
        mappings.put("Map", "FtpUploaderMap");
        mappings.put("Map", "HttpUploaderMap");
        mappings.put("Map", "SftpUploaderMap");
        mappings.put("Map", "ScpUploaderMap");
        mappings.put("Map", "S3UploaderMap");
        mappings.put("Map", "FtpDownloaderMap");
        mappings.put("Map", "HttpDownloaderMap");
        mappings.put("Map", "SftpDownloaderMap");
        mappings.put("Map", "ScpDownloaderMap");
        mappings.put("Map", "ExtensionMap");
        mappings.put("Map", "ArtifactoryMavenDeployerMap");
        mappings.put("Map", "AzureMavenDeployerMap");
        mappings.put("Map", "GiteaMavenDeployerMap");
        mappings.put("Map", "GithubMavenDeployerMap");
        mappings.put("Map", "GitlabMavenDeployerMap");
        mappings.put("Map", "Nexus2MavenDeployerMap");
        mappings.put("Map", "MavenCentralMavenDeployerMap");
        mappings.put("Map", "SwidTagMap");

        try {
            SchemaGeneratorConfigBuilder configBuilder = new SchemaGeneratorConfigBuilder(SchemaVersion.DRAFT_7, OptionPreset.PLAIN_JSON);
            configBuilder.getObjectMapper().enable(SerializationFeature.INDENT_OUTPUT);
            configBuilder.with(Option.SCHEMA_VERSION_INDICATOR);
            configBuilder.with(Option.FORBIDDEN_ADDITIONAL_PROPERTIES_BY_DEFAULT);
            configBuilder.with(Option.DEFINITION_FOR_MAIN_SCHEMA);
            configBuilder.with(Option.DEFINITIONS_FOR_ALL_OBJECTS);
            configBuilder.with(Option.MAP_VALUES_AS_ADDITIONAL_PROPERTIES);
            configBuilder.with(Option.GETTER_METHODS);
            configBuilder.with(Option.NONSTATIC_NONVOID_NONGETTER_METHODS);
            configBuilder.with(Option.FIELDS_DERIVED_FROM_ARGUMENTFREE_METHODS);
            configBuilder.with(Option.STRICT_TYPE_INFO);
            JacksonModule jacksonModule = new JacksonModule();
            configBuilder.with(jacksonModule);
            configBuilder.forTypesInGeneral()
                .withDescriptionResolver(scope -> scope.getType().getErasedType() == JReleaserModel.class ?
                    String.format("JReleaser %s", JReleaserVersion.getPlainVersion()) : null)
                .withPatternPropertiesResolver(scope -> {
                    if (scope.getType().isInstanceOf(Map.class)) {
                        ResolvedType type = scope.getTypeParameterFor(Map.class, 1);
                        if (type.getErasedType() != String.class && type.getErasedType() != Object.class) {
                            return singletonMap("^[a-zA-Z][a-zA-Z0-9-]*[a-zA-Z0-9]?$", type);
                        }
                    }
                    return null;
                })
                .withAdditionalPropertiesResolver(scope -> {
                    if (scope.getType().isInstanceOf(Map.class)) {
                        ResolvedType type = scope.getTypeParameterFor(Map.class, 1);
                        if (type.getErasedType() == String.class || type.getErasedType() == Object.class) {
                            return scope.getTypeParameterFor(Map.class, 0);
                        }
                    }
                    return null;
                })
                .withDefinitionNamingStrategy(new DefaultSchemaDefinitionNamingStrategy() {
                    @Override
                    public String getDefinitionNameForKey(DefinitionKey key, SchemaGenerationContext context) {
                        String definitionNameForKey = super.getDefinitionNameForKey(key, context);
                        return mappings.getOrDefault(definitionNameForKey, definitionNameForKey);
                    }
                });
            configBuilder.forMethods()
                .withIgnoreCheck(method -> {
                    if (method.isVoid() || !method.getName().startsWith("get") || method.getArgumentCount() != 0) {
                        return true;
                    }
                    Class declaringType = method.getDeclaringType().getErasedType();
                    return declaringType != Announce.class &&
                        declaringType != HttpAnnouncer.class &&
                        declaringType != HttpDownloader.class &&
                        declaringType != HttpUploader.class &&
                        declaringType != ArtifactoryUploader.class &&
                        declaringType != FtpDownloader.class &&
                        declaringType != FtpUploader.class &&
                        declaringType != AbstractSshDownloader.class &&
                        declaringType != AbstractSshUploader.class &&
                        declaringType != WebhookAnnouncer.class;
                });

            SchemaGeneratorConfig config = configBuilder.build();
            SchemaGenerator generator = new SchemaGenerator(config);
            JsonNode jsonSchema = generator.generateSchema(JReleaserModel.class);

            String fileName = String.format("jreleaser-schema-%s.json", JReleaserVersion.getPlainVersion());
            Path schemaPath = Paths.get(fileName);
            String json = configBuilder.getObjectMapper().writeValueAsString(jsonSchema);
            Files.write(schemaPath, json.getBytes(UTF_8), CREATE, WRITE, TRUNCATE_EXISTING);
            out.println("Schema written to " + schemaPath.toAbsolutePath());
        } catch (Exception e) {
            throw new JReleaserException($("ERROR_unexpected_error"), e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy