diff --git a/src/main/java/dev/dinauer/maven/Dev.java b/src/main/java/dev/dinauer/maven/Dev.java index 3047143..590ffc5 100644 --- a/src/main/java/dev/dinauer/maven/Dev.java +++ b/src/main/java/dev/dinauer/maven/Dev.java @@ -1,20 +1,80 @@ package dev.dinauer.maven; -import dev.dinauer.maven.maven.core.release.ReleaseService; +import at.favre.lib.crypto.bcrypt.BCrypt; +import dev.dinauer.maven.maven.core.MavenService; +import dev.dinauer.maven.maven.token.TokenEntity; +import dev.dinauer.maven.maven.token.TokenRepo; +import dev.dinauer.maven.maven.token.TokenService; +import dev.dinauer.maven.maven.token.dto.TokenCreation; +import dev.dinauer.maven.maven.token.dto.TokenSecret; import io.quarkus.arc.profile.IfBuildProfile; +import io.quarkus.narayana.jta.QuarkusTransaction; +import io.quarkus.oidc.client.Tokens; +import io.quarkus.runtime.Startup; +import io.quarkus.security.identity.AuthenticationRequestContext; +import io.quarkus.security.identity.SecurityIdentity; +import io.quarkus.security.identity.SecurityIdentityAugmentor; +import io.quarkus.security.runtime.QuarkusPrincipal; +import io.quarkus.security.runtime.QuarkusSecurityIdentity; +import io.smallrye.mutiny.Uni; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.control.ActivateRequestContext; import jakarta.inject.Inject; -import jakarta.ws.rs.GET; +import jakarta.transaction.Transactional; +import jakarta.ws.rs.core.SecurityContext; +import org.jspecify.annotations.NonNull; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import java.io.InputStream; +import java.time.LocalDate; +import java.time.ZonedDateTime; +import java.util.Objects; +import java.util.UUID; -@jakarta.ws.rs.Path("/dev") +@ApplicationScoped @IfBuildProfile("dev") public class Dev { - @GET - public void init() + private static final BCrypt.Hasher HASHER = BCrypt.withDefaults(); + + @Inject + TokenRepo tokenRepo; + @Inject + MavenService mavenService; + + @Startup + public void create() throws IOException { + QuarkusTransaction.begin(); + TokenEntity entity = new TokenEntity() + .setName("test") + .setToken(HASHER.hashToString(11, "c693b486-0f3a-4fe4-8716-de5a5a7fb566".toCharArray())) + .setUserId("test") + .setExpiresAt(LocalDate.now().plusDays(2)) + .setCreatedAt(ZonedDateTime.now()); + tokenRepo.persist(entity); + QuarkusTransaction.commit(); + + uploadSnapshot_01(); + uploadSnapshot_02(); + } + + private void uploadSnapshot_01() throws IOException + { + mavenService.upload("/org/postgresql/postgresql/42.7.9-SNAPSHOT/postgresql-42.7.9-20250419.123456-1.jar", readFile("/jar/postgresql-42.7.9.jar")); + mavenService.upload("/org/postgresql/postgresql/42.7.9-SNAPSHOT/postgresql-42.7.9-20250419.123456-1.pom", readFile("/jar/postgresql-42.7.9.pom")); + } + + private void uploadSnapshot_02() throws IOException + { + mavenService.upload("/org/postgresql/postgresql/42.7.9-SNAPSHOT/postgresql-42.7.9-20250419.133456-1.jar", readFile("/jar/postgresql-42.7.9.jar")); + mavenService.upload("/org/postgresql/postgresql/42.7.9-SNAPSHOT/postgresql-42.7.9-20250419.133456-1-javadoc.jar", readFile("/jar/postgresql-42.7.9.jar")); + mavenService.upload("/org/postgresql/postgresql/42.7.9-SNAPSHOT/postgresql-42.7.9-20250419.133456-1-tests.jar", readFile("/jar/postgresql-42.7.9.jar")); + mavenService.upload("/org/postgresql/postgresql/42.7.9-SNAPSHOT/postgresql-42.7.9-20250419.133456-1.pom", readFile("/jar/postgresql-42.7.9.pom")); + } + + private byte[] readFile(String path) throws IOException + { + return Objects.requireNonNull(getClass().getResourceAsStream(path)).readAllBytes(); } } \ No newline at end of file diff --git a/src/main/java/dev/dinauer/maven/event/Resource.java b/src/main/java/dev/dinauer/maven/event/Resource.java index d626bac..00d7bad 100644 --- a/src/main/java/dev/dinauer/maven/event/Resource.java +++ b/src/main/java/dev/dinauer/maven/event/Resource.java @@ -19,6 +19,9 @@ public class Resource private String version; + @Column(name = "is_snapshot") + private boolean isSnapshot; + @OneToOne(mappedBy = "resource") @JsonBackReference private Event event; @@ -77,4 +80,15 @@ public class Resource this.event = event; return this; } + + public boolean isSnapshot() + { + return isSnapshot; + } + + public Resource setSnapshot(boolean snapshot) + { + isSnapshot = snapshot; + return this; + } } diff --git a/src/main/java/dev/dinauer/maven/maven/core/ArtifactService.java b/src/main/java/dev/dinauer/maven/maven/core/ArtifactService.java index a9a05d8..7ed47f9 100644 --- a/src/main/java/dev/dinauer/maven/maven/core/ArtifactService.java +++ b/src/main/java/dev/dinauer/maven/maven/core/ArtifactService.java @@ -3,6 +3,8 @@ package dev.dinauer.maven.maven.core; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.ws.rs.NotFoundException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.time.ZonedDateTime; import java.util.Optional; @@ -10,6 +12,8 @@ import java.util.Optional; @ApplicationScoped public class ArtifactService { + private static final Logger LOG = LoggerFactory.getLogger(ArtifactService.class); + @Inject ArtifactRepo artifactRepo; @@ -43,6 +47,7 @@ public class ArtifactService } else { + LOG.error("Cannot find artifact {}:{}", groupId, artifactId); throw new NotFoundException(); } } diff --git a/src/main/java/dev/dinauer/maven/maven/core/MavenService.java b/src/main/java/dev/dinauer/maven/maven/core/MavenService.java index 8be0f89..359ddbd 100644 --- a/src/main/java/dev/dinauer/maven/maven/core/MavenService.java +++ b/src/main/java/dev/dinauer/maven/maven/core/MavenService.java @@ -1,28 +1,23 @@ package dev.dinauer.maven.maven.core; -import com.fasterxml.jackson.core.JsonProcessingException; import dev.dinauer.maven.maven.core.context.ArtifactContext; import dev.dinauer.maven.maven.core.context.ReleaseContext; import dev.dinauer.maven.maven.core.context.SnapshotContext; import dev.dinauer.maven.maven.core.context.VersionContext; import dev.dinauer.maven.maven.core.model.*; -import dev.dinauer.maven.maven.core.model.Version; import dev.dinauer.maven.maven.core.release.ReleaseService; import dev.dinauer.maven.maven.core.release.parser.ReleaseFile; import dev.dinauer.maven.maven.core.release.parser.ReleaseFileParser; import dev.dinauer.maven.maven.core.snapshot.SnapshotFile; -import dev.dinauer.maven.maven.core.snapshot.SnapshotPom; import dev.dinauer.maven.maven.core.snapshot.SnapshotService; +import dev.dinauer.maven.maven.core.snapshot.metadata.SnapshotMetadataService; import dev.dinauer.maven.maven.core.snapshot.parser.SnapshotFileParser; -import dev.dinauer.maven.maven.shared.Extensions; -import dev.dinauer.maven.maven.shared.MavenCoordinates; import dev.dinauer.maven.maven.shared.ParserResult; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.ws.rs.BadRequestException; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; -import org.apache.commons.lang3.NotImplementedException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,6 +34,8 @@ public class MavenService @Inject SnapshotService snapshotService; + @Inject + SnapshotMetadataService snapshotMetadataService; public void upload(String path, byte[] body) { @@ -87,7 +84,7 @@ public class MavenService } if (Objects.equals(FileExt.XML, context.extensions().ext())) { - return Response.status(404).build(); + return Response.status(200).type(MediaType.APPLICATION_XML).entity(snapshotMetadataService.generate(versionContext)).build(); } } if (context.getClass() == ArtifactContext.class) @@ -99,7 +96,7 @@ public class MavenService private SnapshotContext toSnapshotContext(VersionContext versionContext) { - ParserResult result = new SnapshotFileParser(versionContext.artifactId(), versionContext.base()).parse(versionContext.filename()); + ParserResult result = new SnapshotFileParser(versionContext.artifactId(), versionContext.plainVersion()).parse(versionContext.filename()); return new SnapshotContext( versionContext.extensions(), versionContext.groupId(), @@ -115,7 +112,7 @@ public class MavenService private ReleaseContext toReleaseContext(VersionContext versionContext) { - ParserResult result = new ReleaseFileParser(versionContext.artifactId(), versionContext.base()).parse(versionContext.filename()); + ParserResult result = new ReleaseFileParser(versionContext.artifactId(), versionContext.plainVersion()).parse(versionContext.filename()); return new ReleaseContext( versionContext.extensions(), versionContext.groupId(), diff --git a/src/main/java/dev/dinauer/maven/maven/core/context/VersionContext.java b/src/main/java/dev/dinauer/maven/maven/core/context/VersionContext.java index bccf98b..66c0bff 100644 --- a/src/main/java/dev/dinauer/maven/maven/core/context/VersionContext.java +++ b/src/main/java/dev/dinauer/maven/maven/core/context/VersionContext.java @@ -25,7 +25,7 @@ public class VersionContext extends ArtifactContext return Strings.CI.endsWith(version, SNAPSHOT_SUFFIX); } - public String base() + public String plainVersion() { return Strings.CI.removeEnd(version, SNAPSHOT_SUFFIX); } diff --git a/src/main/java/dev/dinauer/maven/maven/core/release/ReleasePomService.java b/src/main/java/dev/dinauer/maven/maven/core/release/ReleasePomService.java index f8c2c03..d284f69 100644 --- a/src/main/java/dev/dinauer/maven/maven/core/release/ReleasePomService.java +++ b/src/main/java/dev/dinauer/maven/maven/core/release/ReleasePomService.java @@ -1,5 +1,9 @@ package dev.dinauer.maven.maven.core.release; +import dev.dinauer.maven.event.Event; +import dev.dinauer.maven.event.EventType; +import dev.dinauer.maven.event.Resource; +import dev.dinauer.maven.event.repo.EventRepo; import dev.dinauer.maven.maven.core.context.ReleaseContext; import dev.dinauer.maven.maven.core.context.SnapshotContext; import dev.dinauer.maven.maven.core.model.FileHash; @@ -10,6 +14,7 @@ import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.NotImplementedException; import java.nio.charset.StandardCharsets; +import java.time.ZonedDateTime; @ApplicationScoped public class ReleasePomService @@ -19,6 +24,8 @@ public class ReleasePomService @Inject ReleaseVersionService releaseVersionService; + @Inject + EventRepo eventRepo; public void store(ReleaseContext releaseContext, byte[] body) { @@ -26,6 +33,7 @@ public class ReleasePomService if (jar == null) { create(releaseContext, body); + createEvent(releaseContext); } else { @@ -59,4 +67,9 @@ public class ReleasePomService releasePom.setFilename(releaseContext.filename()); releasePomRepo.persist(releasePom); } + + private void createEvent(ReleaseContext releaseContext) + { + eventRepo.persist(new Event().setType(EventType.UPLOAD).setTimestamp(ZonedDateTime.now()).setResource(new Resource().setGroupId(releaseContext.groupId()).setArtifactId(releaseContext.artifactId()).setVersion(releaseContext.version()).setSnapshot(false))); + } } diff --git a/src/main/java/dev/dinauer/maven/maven/core/snapshot/SnapshotBundle.java b/src/main/java/dev/dinauer/maven/maven/core/snapshot/SnapshotBundle.java index 14a307e..f102cd8 100644 --- a/src/main/java/dev/dinauer/maven/maven/core/snapshot/SnapshotBundle.java +++ b/src/main/java/dev/dinauer/maven/maven/core/snapshot/SnapshotBundle.java @@ -45,6 +45,11 @@ public class SnapshotBundle return this; } + public String getDateTime() + { + return String.format("%s.%s", date, time); + } + public String getDate() { return date; diff --git a/src/main/java/dev/dinauer/maven/maven/core/snapshot/SnapshotBundleRepo.java b/src/main/java/dev/dinauer/maven/maven/core/snapshot/SnapshotBundleRepo.java index 9107b9a..3fbfd7c 100644 --- a/src/main/java/dev/dinauer/maven/maven/core/snapshot/SnapshotBundleRepo.java +++ b/src/main/java/dev/dinauer/maven/maven/core/snapshot/SnapshotBundleRepo.java @@ -1,5 +1,7 @@ package dev.dinauer.maven.maven.core.snapshot; +import dev.dinauer.maven.maven.core.context.VersionContext; +import dev.dinauer.maven.maven.core.snapshot.metadata.utils.SnapshotComparator; import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase; import jakarta.enterprise.context.ApplicationScoped; @@ -12,4 +14,12 @@ public class SnapshotBundleRepo implements PanacheRepositoryBase versions = toVersions(versionContext.plainVersion(), latest); + return new SnapshotMetadata(versionContext.groupId(), versionContext.artifactId(), versionContext.version(), new SnapshotVersioning(snapshot, latest.getDate() + latest.getTime(), versions)); + } + + private List toVersions(String version, SnapshotBundle bundle) + { + SnapshotPom pom = bundle.getPom(); + if (pom != null) + { + SnapshotVersion pomVersion = new SnapshotVersion(null, "pom", formatValue(version, bundle.getDate(), bundle.getTime(), bundle.getBuildNumber()), bundle.getDateTime()); + return Stream.concat(bundle.getJars().stream().map(item -> jarToVersion(version, bundle, item)), Stream.of(pomVersion)).toList(); + } + throw new InternalServerErrorException(); + } + + private SnapshotVersion jarToVersion(String version, SnapshotBundle bundle, SnapshotJar jar) + { + return new SnapshotVersion(jar.getClassifier(), "jar", formatValue(version, bundle.getDate(), bundle.getTime(), bundle.getBuildNumber()), bundle.getDateTime()); + } + + private String formatValue(String version, String date, String time, int buildNumber) + { + return String.format("%s-%s.%s-%s", version, date, time, buildNumber); + } +} diff --git a/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/model/Snapshot.java b/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/model/Snapshot.java new file mode 100644 index 0000000..b328c13 --- /dev/null +++ b/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/model/Snapshot.java @@ -0,0 +1,5 @@ +package dev.dinauer.maven.maven.core.snapshot.metadata.model; + +public record Snapshot(String timestamp, int buildNumber) +{ +} diff --git a/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/model/SnapshotMetadata.java b/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/model/SnapshotMetadata.java new file mode 100644 index 0000000..3854006 --- /dev/null +++ b/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/model/SnapshotMetadata.java @@ -0,0 +1,12 @@ +package dev.dinauer.maven.maven.core.snapshot.metadata.model; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement; + +import java.util.List; + +@JacksonXmlRootElement(localName = "metadata") +public record SnapshotMetadata(String groupId, String artifactId, String version, SnapshotVersioning versioning) +{ +} diff --git a/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/model/SnapshotVersion.java b/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/model/SnapshotVersion.java new file mode 100644 index 0000000..f9d41ba --- /dev/null +++ b/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/model/SnapshotVersion.java @@ -0,0 +1,9 @@ +package dev.dinauer.maven.maven.core.snapshot.metadata.model; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +@JsonPropertyOrder({"classifier", "extension", "value", "updated"}) +public record SnapshotVersion(@JsonInclude(JsonInclude.Include.NON_NULL) String classifier, String extension, String value, String updated) +{ +} diff --git a/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/model/SnapshotVersioning.java b/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/model/SnapshotVersioning.java new file mode 100644 index 0000000..7b6eb3d --- /dev/null +++ b/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/model/SnapshotVersioning.java @@ -0,0 +1,10 @@ +package dev.dinauer.maven.maven.core.snapshot.metadata.model; + +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; + +import java.util.List; + +public record SnapshotVersioning(Snapshot snapshot, String lastUpdated, @JacksonXmlElementWrapper(localName = "snapshotVersions") @JacksonXmlProperty(localName = "snapshotVersion") List snapshotVersions) +{ +} diff --git a/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/utils/SnapshotComparator.java b/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/utils/SnapshotComparator.java new file mode 100644 index 0000000..131d4a1 --- /dev/null +++ b/src/main/java/dev/dinauer/maven/maven/core/snapshot/metadata/utils/SnapshotComparator.java @@ -0,0 +1,25 @@ +package dev.dinauer.maven.maven.core.snapshot.metadata.utils; + +import dev.dinauer.maven.maven.core.snapshot.SnapshotBundle; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.Comparator; + +public class SnapshotComparator implements Comparator +{ + private static final DateTimeFormatter MAVEN_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd.HHmmss"); + + @Override + public int compare(SnapshotBundle b0, SnapshotBundle b1) + { + long b0DateTime = LocalDateTime.parse(String.format("%s.%s", b0.getDate(), b0.getTime()), MAVEN_DATE_TIME_FORMATTER).toEpochSecond(ZoneOffset.UTC); + long b1DateTime = LocalDateTime.parse(String.format("%s.%s", b1.getDate(), b1.getTime()), MAVEN_DATE_TIME_FORMATTER).toEpochSecond(ZoneOffset.UTC); + if (b0DateTime == b1DateTime) + { + return Integer.compare(b1.getBuildNumber(), b0.getBuildNumber()); + } + return Long.compare(b1DateTime, b0DateTime); + } +} diff --git a/src/main/java/dev/dinauer/maven/maven/shared/PomNotFoundException.java b/src/main/java/dev/dinauer/maven/maven/shared/PomNotFoundException.java new file mode 100644 index 0000000..ae05d31 --- /dev/null +++ b/src/main/java/dev/dinauer/maven/maven/shared/PomNotFoundException.java @@ -0,0 +1,9 @@ +package dev.dinauer.maven.maven.shared; + +public class PomNotFoundException extends RuntimeException +{ + public PomNotFoundException() + { + super(); + } +} diff --git a/src/main/resources/db/migration/V1.0.0__init.sql b/src/main/resources/db/migration/V1.0.0__init.sql index 3f46847..ceaf892 100644 --- a/src/main/resources/db/migration/V1.0.0__init.sql +++ b/src/main/resources/db/migration/V1.0.0__init.sql @@ -75,6 +75,7 @@ create table resource ( artifact_id varchar(255), group_id varchar(255), + is_snapshot boolean, id varchar(255) not null constraint resource_pkey primary key,