Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More consistent status codes, added 'query' endpoint #183

Merged
merged 1 commit into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ The command line tool is available at `cli/lib/ovsx`.
* `./gradlew build` — build and test the server
* `./gradlew assemble -t` — build continuously (the server is restarted after every change)
* `./gradlew runServer` — start the Spring server on port 8080
* `./scripts/test-report.sh` — display test results on port 8081

The Spring server is started automatically in Gitpod. It includes `spring-boot-devtools` which detects changes in the compiled class files and restarts the server.

Expand Down
7 changes: 4 additions & 3 deletions cli/src/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,10 @@ export class Registry {
if (response.statusCode !== undefined && (response.statusCode < 200 || response.statusCode > 299)) {
if (json.startsWith('{')) {
try {
const error = JSON.parse(json) as ErrorResponse;
if (error.message) {
reject(new Error(error.message));
const parsed = JSON.parse(json) as ErrorResponse;
const message = parsed.message || parsed.error;
if (message) {
reject(new Error(message));
return;
}
} catch (err) {
Expand Down
1 change: 1 addition & 0 deletions server/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
.classpath
.project
DEPENDENCIES
/src/dev/resources/static/
/src/dev/resources/application-ovsx.properties
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.eclipse.openvsx.util.LicenseDetection;
import org.elasticsearch.common.Strings;
import org.springframework.data.util.Pair;
import org.springframework.http.HttpStatus;

/**
* Processes uploaded extension files and extracts their metadata.
Expand Down Expand Up @@ -82,7 +83,7 @@ private void readInputStream() {
try {
content = ByteStreams.toByteArray(inputStream);
if (content.length > MAX_CONTENT_SIZE)
throw new ErrorResultException("The extension package exceeds the size limit of 512 MB.");
throw new ErrorResultException("The extension package exceeds the size limit of 512 MB.", HttpStatus.PAYLOAD_TOO_LARGE);
var tempFile = File.createTempFile("extension_", ".vsix");
Files.write(content, tempFile);
zipFile = new ZipFile(tempFile);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

import org.eclipse.openvsx.json.ExtensionJson;
import org.eclipse.openvsx.json.NamespaceJson;
import org.eclipse.openvsx.json.QueryParamJson;
import org.eclipse.openvsx.json.QueryResultJson;
import org.eclipse.openvsx.json.ReviewListJson;
import org.eclipse.openvsx.json.SearchResultJson;
import org.eclipse.openvsx.search.SearchService;
Expand All @@ -33,4 +35,6 @@ public interface IExtensionRegistry {

SearchResultJson search(SearchService.Options options);

QueryResultJson query(QueryParamJson param);

}
101 changes: 99 additions & 2 deletions server/src/main/java/org/eclipse/openvsx/LocalRegistryService.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import javax.transaction.Transactional;

import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

Expand All @@ -36,6 +37,8 @@
import org.eclipse.openvsx.entities.UserData;
import org.eclipse.openvsx.json.ExtensionJson;
import org.eclipse.openvsx.json.NamespaceJson;
import org.eclipse.openvsx.json.QueryParamJson;
import org.eclipse.openvsx.json.QueryResultJson;
import org.eclipse.openvsx.json.ResultJson;
import org.eclipse.openvsx.json.ReviewJson;
import org.eclipse.openvsx.json.ReviewListJson;
Expand Down Expand Up @@ -210,6 +213,100 @@ private List<SearchEntryJson> toSearchEntries(Page<ExtensionSearch> page, int si
return CollectionUtil.map(page.getContent(), es -> toSearchEntry(es, serverUrl, options));
}

@Override
public QueryResultJson query(QueryParamJson param) {
if (!Strings.isNullOrEmpty(param.extensionId)) {
var split = param.extensionId.split("\\.");
if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty())
throw new ErrorResultException("The 'extensionId' parameter must have the format 'namespace.extension'.");
if (!Strings.isNullOrEmpty(param.namespaceName) && !param.namespaceName.equals(split[0]))
throw new ErrorResultException("Conflicting parameters 'extensionId' and 'namespaceName'");
if (!Strings.isNullOrEmpty(param.extensionName) && !param.extensionName.equals(split[1]))
throw new ErrorResultException("Conflicting parameters 'extensionId' and 'extensionName'");
param.namespaceName = split[0];
param.extensionName = split[1];
}
var result = new QueryResultJson();
result.extensions = new ArrayList<>();
// Add extension by UUID (public_id)
if (!Strings.isNullOrEmpty(param.extensionUuid)) {
var extension = repositories.findExtensionByPublicId(param.extensionUuid);
addToResult(extension, result, param);
}
// Add extensions by namespace UUID (public_id)
if (!Strings.isNullOrEmpty(param.namespaceUuid)) {
var namespace = repositories.findNamespaceByPublicId(param.namespaceUuid);
addToResult(namespace, result, param);
}
// Add a specific version of an extension
if (!Strings.isNullOrEmpty(param.namespaceName) && !Strings.isNullOrEmpty(param.extensionName)
&& !Strings.isNullOrEmpty(param.extensionVersion) && !param.includeAllVersions) {
var extVersion = repositories.findVersion(param.extensionVersion, param.extensionName, param.namespaceName);
addToResult(extVersion, result, param);
// Add extension by namespace and name
} else if (!Strings.isNullOrEmpty(param.namespaceName) && !Strings.isNullOrEmpty(param.extensionName)) {
var extension = repositories.findExtension(param.extensionName, param.namespaceName);
addToResult(extension, result, param);
// Add extensions by namespace
} else if (!Strings.isNullOrEmpty(param.namespaceName)) {
var namespace = repositories.findNamespace(param.namespaceName);
addToResult(namespace, result, param);
// Add extensions by name
} else if (!Strings.isNullOrEmpty(param.extensionName)) {
var extensions = repositories.findExtensions(param.extensionName);
for (var extension : extensions) {
addToResult(extension, result, param);
}
}
return result;
}

private void addToResult(Namespace namespace, QueryResultJson result, QueryParamJson param) {
if (namespace == null)
return;
for (var extension : repositories.findExtensions(namespace)) {
addToResult(extension, result, param);
}
}

private void addToResult(Extension extension, QueryResultJson result, QueryParamJson param) {
if (extension == null)
return;
if (param.includeAllVersions) {
var allVersions = Lists.newArrayList(repositories.findVersions(extension));
Collections.sort(allVersions, ExtensionVersion.SORT_COMPARATOR);
for (var extVersion : allVersions) {
addToResult(extVersion, result, param);
}
} else {
addToResult(extension.getLatest(), result, param);
}
}

private void addToResult(ExtensionVersion extVersion, QueryResultJson result, QueryParamJson param) {
if (extVersion == null)
return;
if (mismatch(extVersion.getVersion(), param.extensionVersion))
return;
var extension = extVersion.getExtension();
if (mismatch(extension.getName(), param.extensionName))
return;
var namespace = extension.getNamespace();
if (mismatch(namespace.getName(), param.namespaceName))
return;
if (mismatch(extension.getPublicId(), param.extensionUuid) || mismatch(namespace.getPublicId(), param.namespaceUuid))
return;
if (result.extensions == null)
result.extensions = new ArrayList<>();
result.extensions.add(toJson(extVersion));
}

private static boolean mismatch(String s1, String s2) {
return s1 != null && s2 != null
&& !s1.isEmpty() && !s2.isEmpty()
&& !s1.equalsIgnoreCase(s2);
}

@Transactional(rollbackOn = ErrorResultException.class)
public ResultJson createNamespace(NamespaceJson json, String tokenValue) {
var namespaceIssue = validator.validateNamespace(json.name);
Expand Down Expand Up @@ -343,7 +440,7 @@ private void storeResources(List<FileResource> resources, ExtensionVersion extVe

private void addDependency(String dependency, ExtensionVersion extVersion) {
var split = dependency.split("\\.");
if (split.length != 2) {
if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty()) {
throw new ErrorResultException("Invalid 'extensionDependencies' format. Expected: '${namespace}.${name}'");
}
var extension = repositories.findExtension(split[1], split[0]);
Expand All @@ -360,7 +457,7 @@ private void addDependency(String dependency, ExtensionVersion extVersion) {

private void addBundledExtension(String bundled, ExtensionVersion extVersion) {
var split = bundled.split("\\.");
if (split.length != 2) {
if (split.length != 2 || split[0].isEmpty() || split[1].isEmpty()) {
throw new ErrorResultException("Invalid 'extensionPack' format. Expected: '${namespace}.${name}'");
}
var extension = repositories.findExtension(split[1], split[0]);
Expand Down
Loading