mirror of
https://gitee.com/dcren/initializr.git
synced 2025-07-15 14:04:30 +08:00
Merge pull request #279 from dsyer:feature/url
* pr/279: Polish contribution Add optional links to a dependency
This commit is contained in:
commit
bf306e5ecb
@ -102,6 +102,8 @@ class Dependency extends MetadataElement {
|
||||
|
||||
List<String> keywords = []
|
||||
|
||||
List<Link> links = []
|
||||
|
||||
void setScope(String scope) {
|
||||
if (!SCOPE_ALL.contains(scope)) {
|
||||
throw new InvalidInitializrMetadataException("Invalid scope $scope must be one of $SCOPE_ALL")
|
||||
@ -161,6 +163,9 @@ class Dependency extends MetadataElement {
|
||||
"Invalid dependency, id should have the form groupId:artifactId[:version] but got $id")
|
||||
}
|
||||
}
|
||||
links.forEach { l ->
|
||||
l.resolve()
|
||||
}
|
||||
updateVersionRanges(VersionParser.DEFAULT)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or 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
|
||||
*
|
||||
* http://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 io.spring.initializr.metadata
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore
|
||||
import com.fasterxml.jackson.annotation.JsonInclude
|
||||
import groovy.transform.ToString
|
||||
|
||||
/**
|
||||
* Metadata for a link. Each link has a "relation" that potentially attaches a strong
|
||||
* semantic to the nature of the link. The URI of the link itself can be templated by
|
||||
* including variables in the form `{variableName}`.
|
||||
* <p>
|
||||
* An actual {@code URI} can be generated using {@code expand}, providing a mapping for
|
||||
* those variables.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ToString(ignoreNulls = true, includePackage = false)
|
||||
class Link {
|
||||
|
||||
private static final String VARIABLE_REGEX = "\\{(\\w+)\\}";
|
||||
|
||||
/**
|
||||
* The relation of the link.
|
||||
*/
|
||||
String rel;
|
||||
|
||||
/**
|
||||
* The URI the link is pointing to.
|
||||
*/
|
||||
String href
|
||||
|
||||
/**
|
||||
* Specify if the URI is templated.
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
|
||||
boolean templated
|
||||
|
||||
@JsonIgnore
|
||||
final Set<String> templateVariables = []
|
||||
|
||||
/**
|
||||
* A description of the link.
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
String description
|
||||
|
||||
Set<String> getTemplateVariables() {
|
||||
Collections.unmodifiableSet(templateVariables)
|
||||
}
|
||||
|
||||
void setHref(String href) {
|
||||
this.href = href
|
||||
}
|
||||
|
||||
void resolve() {
|
||||
if (!rel) {
|
||||
throw new InvalidInitializrMetadataException(
|
||||
"Invalid link $this: rel attribute is mandatory")
|
||||
}
|
||||
if (!href) {
|
||||
throw new InvalidInitializrMetadataException(
|
||||
"Invalid link $this: href attribute is mandatory")
|
||||
}
|
||||
def matcher = (href =~ VARIABLE_REGEX)
|
||||
while (matcher.find()) {
|
||||
def variable = matcher.group(1)
|
||||
this.templateVariables << variable
|
||||
}
|
||||
this.templated = this.templateVariables
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the link using the specified parameters.
|
||||
* @param parameters the parameters value
|
||||
* @return an URI where all variables have been expanded
|
||||
*/
|
||||
URI expand(Map<String, String> parameters) {
|
||||
String result = href
|
||||
templateVariables.forEach { var ->
|
||||
Object value = parameters[var]
|
||||
if (!value) {
|
||||
throw new IllegalArgumentException(
|
||||
"Could not explan $href, missing value for '$var'")
|
||||
}
|
||||
result = result.replace("{$var}", value.toString())
|
||||
}
|
||||
new URI(result)
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2016 the original author or authors.
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -123,6 +123,15 @@ class DependencyTests {
|
||||
dependency.resolve()
|
||||
}
|
||||
|
||||
@Test
|
||||
void invalidLink() {
|
||||
def dependency = new Dependency(id : 'foo')
|
||||
dependency.links << new Link(href: 'https://example.com')
|
||||
|
||||
thrown.expect(InvalidInitializrMetadataException)
|
||||
dependency.resolve()
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateIdWithNoGroupId() {
|
||||
def dependency = new Dependency()
|
||||
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or 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
|
||||
*
|
||||
* http://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 io.spring.initializr.metadata
|
||||
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.rules.ExpectedException
|
||||
|
||||
/**
|
||||
* Tests for {@link Link}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class LinkTests {
|
||||
|
||||
@Rule
|
||||
public final ExpectedException thrown = ExpectedException.none()
|
||||
|
||||
@Test
|
||||
void resolveInvalidLinkNoRel() {
|
||||
def link = new Link(href: 'https://example.com')
|
||||
thrown.expect(InvalidInitializrMetadataException)
|
||||
link.resolve()
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveInvalidLinkNoHref() {
|
||||
def link = new Link(rel: 'reference', description: 'foo doc')
|
||||
|
||||
thrown.expect(InvalidInitializrMetadataException)
|
||||
link.resolve()
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveLinkNoVariables() {
|
||||
def link = new Link(rel: 'reference', href: 'https://example.com/2')
|
||||
link.resolve()
|
||||
assert !link.templated
|
||||
assert link.templateVariables.size() == 0
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveLinkWithVariables() {
|
||||
def link = new Link(rel: 'reference', href: 'https://example.com/{a}/2/{b}')
|
||||
link.resolve()
|
||||
assert link.templated
|
||||
assert link.templateVariables.size() == 2
|
||||
assert link.templateVariables.contains('a')
|
||||
assert link.templateVariables.contains('b')
|
||||
}
|
||||
|
||||
@Test
|
||||
void expandLink() {
|
||||
def link = new Link(rel: 'reference', href: 'https://example.com/{a}/2/{b}')
|
||||
link.resolve()
|
||||
assert link.expand(['a': 'test', 'b': 'another']) ==
|
||||
new URI('https://example.com/test/2/another')
|
||||
}
|
||||
|
||||
@Test
|
||||
void expandLinkWithSameAttributeAtTwoPlaces() {
|
||||
def link = new Link(rel: 'reference', href: 'https://example.com/{a}/2/{a}')
|
||||
link.resolve()
|
||||
assert link.expand(['a': 'test', 'b': 'another']) ==
|
||||
new URI('https://example.com/test/2/test')
|
||||
}
|
||||
|
||||
@Test
|
||||
void expandLinkMissingVariable() {
|
||||
def link = new Link(rel: 'reference', href: 'https://example.com/{a}/2/{b}')
|
||||
link.resolve()
|
||||
|
||||
thrown.expect(IllegalArgumentException)
|
||||
thrown.expectMessage("missing value for 'b'")
|
||||
link.expand(['a': 'test'])
|
||||
}
|
||||
|
||||
}
|
@ -39,6 +39,12 @@ initializr:
|
||||
description: Web dependency description
|
||||
facets:
|
||||
- web
|
||||
links:
|
||||
- rel: guide
|
||||
href: https://example.com/guide
|
||||
description: Building a RESTful Web Service
|
||||
- rel: reference
|
||||
href: https://example.com/doc
|
||||
- name: Security
|
||||
id: security
|
||||
- name: Data JPA
|
||||
@ -55,6 +61,14 @@ initializr:
|
||||
keywords:
|
||||
- thefoo
|
||||
- dafoo
|
||||
links:
|
||||
- rel: guide
|
||||
href: https://example.com/guide1
|
||||
- rel: reference
|
||||
href: https://example.com/{bootVersion}/doc
|
||||
- rel: guide
|
||||
href: https://example.com/guide2
|
||||
description: Some guide for foo
|
||||
- name: Bar
|
||||
id: org.acme:bar
|
||||
version: 2.1.0
|
||||
|
@ -52,6 +52,9 @@ class InitializrMetadataV21JsonMapper extends InitializrMetadataV2JsonMapper {
|
||||
if (dependency.versionRange) {
|
||||
content['versionRange'] = dependency.versionRange
|
||||
}
|
||||
if (dependency.links) {
|
||||
content._links = LinkMapper.mapLinks(dependency.links)
|
||||
}
|
||||
content
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or 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
|
||||
*
|
||||
* http://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 io.spring.initializr.web.mapper
|
||||
|
||||
import io.spring.initializr.metadata.Link
|
||||
|
||||
/**
|
||||
* Generate a json representation for {@link Link}
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class LinkMapper {
|
||||
|
||||
/**
|
||||
* Map the specified links to a json model. If several links share
|
||||
* the same relation, they are grouped together.
|
||||
* @param links the links to map
|
||||
* @return a model for the specified links
|
||||
*/
|
||||
static mapLinks(List<Link> links) {
|
||||
def result = [:]
|
||||
Map<String, List<Link>> byRel = new LinkedHashMap<>()
|
||||
links.each {
|
||||
def relLinks = byRel[it.rel]
|
||||
if (!relLinks) {
|
||||
relLinks = []
|
||||
byRel[it.rel] = relLinks
|
||||
}
|
||||
relLinks.add(it)
|
||||
}
|
||||
byRel.forEach { rel, l ->
|
||||
if (l.size() == 1) {
|
||||
def root = [:]
|
||||
mapLink(l[0], root)
|
||||
result[rel] = root
|
||||
} else {
|
||||
def root = []
|
||||
l.each {
|
||||
def model = [:]
|
||||
mapLink(it, model)
|
||||
root << model
|
||||
}
|
||||
result[rel] = root
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
private static mapLink(Link link, def model) {
|
||||
model.href = link.href
|
||||
if (link.templated) {
|
||||
model.templated = true
|
||||
}
|
||||
if (link.description) {
|
||||
model.title = link.description
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2015 the original author or authors.
|
||||
* Copyright 2012-2017 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -17,7 +17,9 @@
|
||||
package io.spring.initializr.web.mapper
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import io.spring.initializr.metadata.Dependency
|
||||
import io.spring.initializr.metadata.InitializrMetadata
|
||||
import io.spring.initializr.metadata.Link
|
||||
import io.spring.initializr.test.metadata.InitializrMetadataTestBuilder
|
||||
import org.junit.Test
|
||||
|
||||
@ -52,4 +54,17 @@ class InitializrMetadataJsonMapperTests {
|
||||
result._links.foo.href
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepLinksOrdering() {
|
||||
def dependency = new Dependency(id: 'foo')
|
||||
dependency.links << new Link(rel: 'guide', href: 'https://example.com/how-to')
|
||||
dependency.links << new Link(rel: 'reference', href: 'https://example.com/doc')
|
||||
InitializrMetadata metadata = InitializrMetadataTestBuilder.withDefaults()
|
||||
.addDependencyGroup('test', dependency).build()
|
||||
def json = jsonMapper.write(metadata, null)
|
||||
def first = json.indexOf('https://example.com/how-to')
|
||||
def second = json.indexOf('https://example.com/doc')
|
||||
assert first < second
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2012-2017 the original author or 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
|
||||
*
|
||||
* http://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 io.spring.initializr.web.mapper
|
||||
|
||||
import io.spring.initializr.metadata.Link
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Tests for {@link LinkMapper}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class LinkMapperTests {
|
||||
|
||||
@Test
|
||||
void mapSimpleRel() {
|
||||
def links = new ArrayList()
|
||||
links << new Link(rel: 'a', 'href': 'https://example.com',
|
||||
description: 'some description')
|
||||
def model = LinkMapper.mapLinks(links)
|
||||
assert model.size() == 1
|
||||
assert model.containsKey('a')
|
||||
def linkModel = model['a']
|
||||
assert linkModel.size() == 2
|
||||
assert linkModel['href'] == 'https://example.com'
|
||||
assert linkModel['title'] == 'some description'
|
||||
}
|
||||
|
||||
@Test
|
||||
void mapTemplatedRel() {
|
||||
def links = new ArrayList()
|
||||
links << new Link(rel: 'a', 'href': 'https://example.com/{bootVersion}/a',
|
||||
templated: true)
|
||||
def model = LinkMapper.mapLinks(links)
|
||||
assert model.size() == 1
|
||||
assert model.containsKey('a')
|
||||
def linkModel = model['a']
|
||||
assert linkModel.size() == 2
|
||||
assert linkModel['href'] == 'https://example.com/{bootVersion}/a'
|
||||
assert linkModel['templated'] == true
|
||||
}
|
||||
|
||||
@Test
|
||||
void mergeSeveralLinksInArray() {
|
||||
def links = new ArrayList()
|
||||
links << new Link(rel: 'a', 'href': 'https://example.com',
|
||||
description: 'some description')
|
||||
links << new Link(rel: 'a', 'href': 'https://example.com/2')
|
||||
def model = LinkMapper.mapLinks(links)
|
||||
assert model.size() == 1
|
||||
assert model.containsKey('a')
|
||||
def linksModel = model['a']
|
||||
assert linksModel.size() == 2
|
||||
assert linksModel[0]['href'] == 'https://example.com'
|
||||
assert linksModel[1]['href'] == 'https://example.com/2'
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepOrdering() {
|
||||
def links = new ArrayList()
|
||||
links << new Link(rel: 'a', 'href': 'https://example.com')
|
||||
links << new Link(rel: 'b', 'href': 'https://example.com')
|
||||
def model = LinkMapper.mapLinks(links)
|
||||
def iterator = model.keySet().iterator()
|
||||
assert ++iterator == 'a'
|
||||
assert ++iterator == 'b'
|
||||
}
|
||||
|
||||
@Test
|
||||
void keepOrderingWithMultipleUrlForSameRel() {
|
||||
def links = new ArrayList()
|
||||
links << new Link(rel: 'a', 'href': 'https://example.com')
|
||||
links << new Link(rel: 'b', 'href': 'https://example.com')
|
||||
links << new Link(rel: 'a', 'href': 'https://example.com')
|
||||
def model = LinkMapper.mapLinks(links)
|
||||
def iterator = model.keySet().iterator()
|
||||
assert ++iterator == 'a'
|
||||
assert ++iterator == 'b'
|
||||
}
|
||||
|
||||
}
|
@ -131,7 +131,18 @@
|
||||
"groupId": "org.springframework.boot",
|
||||
"id": "web",
|
||||
"name": "Web",
|
||||
"scope": "compile"
|
||||
"scope": "compile",
|
||||
"links": [
|
||||
{
|
||||
"rel": "guide",
|
||||
"description": "Building a RESTful Web Service",
|
||||
"href": "https://example.com/guide"
|
||||
},
|
||||
{
|
||||
"rel": "reference",
|
||||
"href": "https://example.com/doc"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"starter": true,
|
||||
@ -164,7 +175,23 @@
|
||||
"starter": true,
|
||||
"keywords": ["thefoo", "dafoo"],
|
||||
"scope": "compile",
|
||||
"version": "1.3.5"
|
||||
"version": "1.3.5",
|
||||
"links": [
|
||||
{
|
||||
"rel": "guide",
|
||||
"href": "https://example.com/guide1"
|
||||
},
|
||||
{
|
||||
"rel": "reference",
|
||||
"href": "https://example.com/{bootVersion}/doc",
|
||||
"templated": true
|
||||
},
|
||||
{
|
||||
"rel": "guide",
|
||||
"description": "Some guide for foo",
|
||||
"href": "https://example.com/guide2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"starter": true,
|
||||
|
@ -30,7 +30,16 @@
|
||||
{
|
||||
"id": "web",
|
||||
"name": "Web",
|
||||
"description": "Web dependency description"
|
||||
"description": "Web dependency description",
|
||||
"_links": {
|
||||
"guide": {
|
||||
"href": "https://example.com/guide",
|
||||
"title": "Building a RESTful Web Service"
|
||||
},
|
||||
"reference": {
|
||||
"href": "https://example.com/doc"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "security",
|
||||
@ -47,7 +56,22 @@
|
||||
"values": [
|
||||
{
|
||||
"id": "org.acme:foo",
|
||||
"name": "Foo"
|
||||
"name": "Foo",
|
||||
"_links": {
|
||||
"guide": [
|
||||
{
|
||||
"href": "https://example.com/guide1"
|
||||
},
|
||||
{
|
||||
"href": "https://example.com/guide2",
|
||||
"title": "Some guide for foo"
|
||||
}
|
||||
],
|
||||
"reference": {
|
||||
"href": "https://example.com/{bootVersion}/doc",
|
||||
"templated": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "org.acme:bar",
|
||||
|
Loading…
Reference in New Issue
Block a user