Skip to content

Commit

Permalink
Resolve Keycloak JS from NPM package metadata (keycloak#499)
Browse files Browse the repository at this point in the history
Closes keycloak#492

Signed-off-by: Jon Koops <[email protected]>
  • Loading branch information
jonkoops authored Oct 2, 2024
1 parent 7fa3097 commit 15182c2
Show file tree
Hide file tree
Showing 15 changed files with 294 additions and 66 deletions.
8 changes: 6 additions & 2 deletions pages/app/index.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,12 @@

<@tmpl.page current="test-app" title="Test application" noindex=true nocsp=true>

<script src="keycloak.js" type="text/javascript"></script>
<script src="app.js" type="text/javascript"></script>
<#if version.majorVersion < 26>
<script src="keycloak.js" type="text/javascript"></script>
<script src="app-legacy.js" type="text/javascript"></script>
<#else>
<script src="app.js" type="module"></script>
</#if>

<div class="jumbotron jumbotron-fluid bg-light kc-bg-triangles py-5 kc-app">
<div class="container">
Expand Down
14 changes: 4 additions & 10 deletions src/main/java/org/keycloak/webbuilder/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,8 @@ public class Context {

private FreeMarker freeMarker;
private AsciiDoctor asciiDoctor;
private JsonParser jsonParser;

public Context(File rootDir) throws Exception {
jsonParser = new JsonParser();
freeMarker = new FreeMarker(rootDir);
asciiDoctor = new AsciiDoctor(rootDir);

Expand All @@ -62,12 +60,12 @@ public void init() throws Exception {
config = loadConfig();
links = new Links(config);

versions = new Versions(versionsDir, jsonParser);
extensions = new Extensions(extensionsDir, jsonParser);
versions = new Versions(versionsDir);
extensions = new Extensions(extensionsDir);
blogs = new Blogs(blogDir, versions, config, freeMarker, asciiDoctor);
guidesMetadata = new YamlParser().read(new File(getWebSrcDir(),"/guides.yaml"), GuidesMetadata.class);
guides = new Guides(guidesMetadata, tmpDir, getWebSrcDir(), asciiDoctor);
news = new News(newsDir, blogs, jsonParser, config);
news = new News(newsDir, blogs, config);

freeMarker.init(this);
asciiDoctor.init(this);
Expand All @@ -78,7 +76,7 @@ public void close() {
}

private Config loadConfig() {
Config config = jsonParser.read(new File(getWebSrcDir(),"/config.json"), Config.class);
Config config = JsonParser.read(new File(getWebSrcDir(),"/config.json"), Config.class);
config.setPublish(System.getProperties().containsKey("publish"));

if (System.getenv().containsKey("KC_URL")) {
Expand Down Expand Up @@ -113,10 +111,6 @@ public News news() {
return news;
}

public JsonParser json() {
return jsonParser;
}

public FreeMarker freeMarker() {
return freeMarker;
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/keycloak/webbuilder/Extensions.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

public class Extensions extends LinkedList<Extensions.Extension> {

public Extensions(File extensionsDir, JsonParser json) {
public Extensions(File extensionsDir) {
for (File extensionFile : extensionsDir.listFiles((dir, name) -> name.endsWith(".json"))) {
add(json.read(extensionFile, Extension.class));
add(JsonParser.read(extensionFile, Extension.class));
}
Collections.sort(this);
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/org/keycloak/webbuilder/News.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@

public class News extends LinkedList<News.NewsItem> {

public News(File newsDir, Blogs blogs, JsonParser json, Config config) throws ParseException {
public News(File newsDir, Blogs blogs, Config config) throws ParseException {
File[] newsFiles = newsDir.listFiles((dir, name) -> name.endsWith(".json"));
if (newsFiles != null) {
for (int i = 0; i < newsFiles.length && i < config.getMaxNews(); i++) {
NewsItem news = json.read(newsFiles[i], NewsItem.class);
NewsItem news = JsonParser.read(newsFiles[i], NewsItem.class);
news.setDate(Constants.DATE_IN.parse(newsFiles[i].getName()));
add(news);
}
Expand Down
10 changes: 7 additions & 3 deletions src/main/java/org/keycloak/webbuilder/Versions.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import org.keycloak.webbuilder.utils.JsonParser;

import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
Expand All @@ -15,9 +14,9 @@

public class Versions extends LinkedList<Versions.Version> {

public Versions(File versionsDir, JsonParser json) {
public Versions(File versionsDir) {
for (File versionFile : versionsDir.listFiles((dir, name) -> name.endsWith(".json"))) {
add(json.read(versionFile, Version.class));
add(JsonParser.read(versionFile, Version.class));
}
Collections.sort(this);

Expand Down Expand Up @@ -74,6 +73,11 @@ public String getVersionShorter() {
return split[0] + "." + split[1];
}

public int getMajorVersion() {
String[] split = version.split("\\.");
return Integer.parseInt(split[0]);
}

public Date getDate() {
return date;
}
Expand Down
46 changes: 28 additions & 18 deletions src/main/java/org/keycloak/webbuilder/builders/AppBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,44 @@

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.io.IOUtils;
import org.keycloak.webbuilder.misc.NpmPackageInfo;
import org.keycloak.webbuilder.npm.Package;
import org.keycloak.webbuilder.npm.Registry;
import org.keycloak.webbuilder.npm.Version;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.net.URL;
import java.nio.file.Path;

public class AppBuilder extends AbstractBuilder {

@Override
protected void build() throws Exception {
NpmPackageInfo npmPackageInfo = context.json().read(new URL("https://registry.npmjs.org/keycloak-js/"), NpmPackageInfo.class);

File f = new File(context.getTargetDir(), "app/keycloak.js");
if (!f.isFile()) {
URL u = new URL("https://registry.npmjs.org/keycloak-js/-/keycloak-js-" + npmPackageInfo.getDistTags().getLatest() + ".tgz");
try (ArchiveInputStream i = new TarArchiveInputStream(new GzipCompressorInputStream(new BufferedInputStream(u.openStream())))) {
for (ArchiveEntry e = i.getNextEntry(); e != null; e = i.getNextEntry()) {
if (e.getName().equals("package/dist/keycloak.js")) {
try (OutputStream o = new FileOutputStream(f)) {
IOUtils.copy(i, o);
}
}
}
Package packageInfo = Registry.getPackage("keycloak-js");
Version latestVersion = packageInfo.getVersionByTag("latest");
File targetFile = new File(context.getTargetDir(), "app/keycloak.js");

// Skip if target file already exists.
if (targetFile.isFile()) {
return;
}

boolean useLegacy = latestVersion.getSemanticVersion().getMajor() < 26;
String entryPoint = latestVersion.resolveEntryPoint(useLegacy);
String sourcePath = Path.of("package", entryPoint).normalize().toString();
ArchiveInputStream<TarArchiveEntry> tarball = latestVersion.getDist().getTarballStream();

for (ArchiveEntry entry = tarball.getNextEntry(); entry != null; entry = tarball.getNextEntry()) {
// Loop trough until we find a file that matches the package entrypoint.
if (!entry.getName().equals(sourcePath)) {
continue;
}

// Copy the file over when found.
try (OutputStream outputStream = new FileOutputStream(targetFile)) {
IOUtils.copy(tarball, outputStream);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import org.keycloak.webbuilder.Versions;
import org.keycloak.webbuilder.misc.ChangeLogEntry;
import org.keycloak.webbuilder.utils.JsonParser;
import org.kohsuke.github.GHIssue;
import org.kohsuke.github.GitHub;
import org.kohsuke.github.GitHubBuilder;
Expand Down Expand Up @@ -49,7 +50,7 @@ protected void build() throws Exception {
File changeLogFile = new File(releaseCacheDir, "changelog.json");

if (changeLogFile.exists()) {
Versions.ChangeLog changeLog = new Versions.ChangeLog(Arrays.asList(context.json().read(changeLogFile, ChangeLogEntry[].class)));
Versions.ChangeLog changeLog = new Versions.ChangeLog(Arrays.asList(JsonParser.read(changeLogFile, ChangeLogEntry[].class)));

if (v.getBlogTemplate() >= 3) {
for (ChangeLogEntry e : changeLog.getAll()) {
Expand Down
24 changes: 0 additions & 24 deletions src/main/java/org/keycloak/webbuilder/misc/NpmPackageInfo.java

This file was deleted.

20 changes: 20 additions & 0 deletions src/main/java/org/keycloak/webbuilder/npm/Package.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.keycloak.webbuilder.npm;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

import java.util.Map;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Package {
@JsonProperty("dist-tags")
private Map<String, String> distTags;

@JsonProperty("versions")
private Map<String, Version> versions;

public Version getVersionByTag(String tag) {
String versionName = distTags.get(tag);
return versions.get(versionName);
}
}
23 changes: 23 additions & 0 deletions src/main/java/org/keycloak/webbuilder/npm/Registry.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.keycloak.webbuilder.npm;

import org.keycloak.webbuilder.utils.JsonParser;

import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;

public class Registry {
public static URI REGISTRY_URI;

static {
try {
REGISTRY_URI = new URI("https://registry.npmjs.org");
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}

public static Package getPackage(String name) throws MalformedURLException {
return JsonParser.read(REGISTRY_URI.resolve("/" + name).toURL(), Package.class);
}
}
44 changes: 44 additions & 0 deletions src/main/java/org/keycloak/webbuilder/npm/SemanticVersion.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.keycloak.webbuilder.npm;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SemanticVersion {
private static final Pattern VERSION_PATTERN = Pattern.compile("^(\\d+)\\.(\\d+)\\.(\\d+)$");

private final int major;
private final int minor;
private final int patch;

private SemanticVersion(int major, int minor, int patch) {
this.major = major;
this.minor = minor;
this.patch = patch;
}

public int getMajor() {
return major;
}

public int getMinor() {
return minor;
}

public int getPatch() {
return patch;
}

public static SemanticVersion fromString(String versionString) {
Matcher matcher = VERSION_PATTERN.matcher(versionString);

if (!matcher.matches()) {
return null;
}

int major = Integer.parseInt(matcher.group(1));
int minor = Integer.parseInt(matcher.group(2));
int patch = Integer.parseInt(matcher.group(3));

return new SemanticVersion(major, minor, patch);
}
}
71 changes: 71 additions & 0 deletions src/main/java/org/keycloak/webbuilder/npm/Version.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.keycloak.webbuilder.npm;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Map;

@JsonIgnoreProperties(ignoreUnknown = true)
public class Version {
@JsonProperty("version")
private String version;

@JsonProperty("main")
private String main;

@JsonProperty("exports")
private Map<String, Export> exports;

@JsonProperty("dist")
private Dist dist;

public SemanticVersion getSemanticVersion() {
return SemanticVersion.fromString(version);
}

public Dist getDist() {
return dist;
}

public String resolveEntryPoint(boolean useLegacy) {
if (useLegacy) {
return main;
}

Export defaultExport = exports.get(".");
return defaultExport != null ? defaultExport.getDefaultPath() : null;
}

@JsonIgnoreProperties(ignoreUnknown = true)
public static class Export {
@JsonProperty("default")
private String defaultPath;

public String getDefaultPath() {
return defaultPath;
}
}

@JsonIgnoreProperties(ignoreUnknown = true)
public static class Dist {
@JsonProperty("tarball")
private String tarball;

public ArchiveInputStream<TarArchiveEntry> getTarballStream() throws IOException {
URL url = new URL(tarball);

return new TarArchiveInputStream(
new GzipCompressorInputStream(
new BufferedInputStream(url.openStream())
)
);
}
}
}
Loading

0 comments on commit 15182c2

Please sign in to comment.