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

com.artipie.npm.MetaUpdate Maven / Gradle / Ivy

There is a newer version: 0.9.1
Show newest version
/*
 * The MIT License (MIT) Copyright (c) 2020-2023 artipie.com
 * https://github.com/artipie/artipie/blob/master/LICENSE.txt
 */
package com.artipie.npm;

import com.artipie.asto.Content;
import com.artipie.asto.Key;
import com.artipie.asto.Storage;
import com.artipie.asto.ext.ContentDigest;
import com.artipie.asto.ext.Digests;
import com.artipie.npm.misc.JsonFromPublisher;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonPatchBuilder;
import org.apache.commons.codec.binary.Hex;

/**
 * Updating `meta.json` file.
 * @since 0.9
 */
public interface MetaUpdate {
    /**
     * Update `meta.json` file by the specified prefix.
     * @param prefix The package prefix
     * @param storage Abstract storage
     * @return Completion or error signal.
     */
    CompletableFuture update(Key prefix, Storage storage);

    /**
     * Update `meta.json` by adding information from the uploaded json.
     * @since 0.9
     */
    class ByJson implements MetaUpdate {
        /**
         * The uploaded json.
         */
        private final JsonObject json;

        /**
         * Ctor.
         * @param json Uploaded json. Usually this file is generated when
         *  command `npm publish` is completed
         */
        public ByJson(final JsonObject json) {
            this.json = json;
        }

        @Override
        public CompletableFuture update(final Key prefix, final Storage storage) {
            final Key keymeta = new Key.From(prefix, "meta.json");
            return storage.exists(keymeta)
                .thenCompose(
                    exists -> {
                        final CompletionStage meta;
                        if (exists) {
                            meta = storage.value(keymeta)
                                .thenApply(JsonFromPublisher::new)
                                .thenCompose(JsonFromPublisher::json)
                                .thenApply(Meta::new);
                        } else {
                            meta = CompletableFuture.completedFuture(
                                new Meta(
                                    new NpmPublishJsonToMetaSkelethon(this.json).skeleton()
                                )
                            );
                        }
                        return meta;
                    })
                .thenApply(meta -> meta.updatedMeta(this.json))
                .thenCompose(
                    meta -> storage.save(
                        keymeta, new Content.From(meta.toString().getBytes(StandardCharsets.UTF_8))
                    )
                );
        }
    }

    /**
     * Update `meta.json` by adding information from the package file
     * from uploaded archive.
     * @since 0.9
     */
    class ByTgz implements MetaUpdate {
        /**
         * Uploaded tgz archive.
         */
        private final TgzArchive tgz;

        /**
         * Ctor.
         * @param tgz Uploaded tgz file
         */
        public ByTgz(final TgzArchive tgz) {
            this.tgz = tgz;
        }

        @Override
        public CompletableFuture update(final Key prefix, final Storage storage) {
            final String version = "version";
            final JsonPatchBuilder patch = Json.createPatchBuilder();
            patch.add("/dist", Json.createObjectBuilder().build());
            return ByTgz.hash(this.tgz, Digests.SHA512, true)
                .thenAccept(sha -> patch.add("/dist/integrity", String.format("sha512-%s", sha)))
                .thenCombine(
                    ByTgz.hash(this.tgz, Digests.SHA1, false),
                    (nothing, sha) -> patch.add("/dist/shasum", sha)
                ).thenApply(
                    nothing -> {
                        final JsonObject pkg = this.tgz.packageJson();
                        final String name = pkg.getString("name");
                        final String vers = pkg.getString(version);
                        patch.add("/_id", String.format("%s@%s", name, vers));
                        patch.add(
                            "/dist/tarball",
                            String.format("%s/-/%s-%s.tgz", prefix.string(), name, vers)
                        );
                        return patch.build().apply(pkg);
                    }
                )
                .thenApply(
                    json -> {
                        final JsonObject base = new NpmPublishJsonToMetaSkelethon(json).skeleton();
                        final String vers = json.getString(version);
                        final JsonPatchBuilder upd = Json.createPatchBuilder();
                        upd.add("/dist-tags", Json.createObjectBuilder().build());
                        upd.add("/dist-tags/latest", vers);
                        upd.add(String.format("/versions/%s", vers), json);
                        return upd.build().apply(base);
                    }
                )
                .thenCompose(json -> new ByJson(json).update(prefix, storage))
                .toCompletableFuture();
        }

        /**
         * Obtains specified hash value for passed archive.
         * @param tgz Tgz archive
         * @param dgst Digest mode
         * @param encoded Is encoded64?
         * @return Hash value.
         */
        private static CompletionStage hash(
            final TgzArchive tgz, final Digests dgst, final boolean encoded
        ) {
            return new ContentDigest(new Content.From(tgz.bytes()), dgst)
                .bytes()
                .thenApply(
                    bytes -> {
                        final String res;
                        if (encoded) {
                            res = new String(Base64.getEncoder().encode(bytes));
                        } else {
                            res = Hex.encodeHexString(bytes);
                        }
                        return res;
                    }
                );
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy