Skip to content
Draft
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
5 changes: 5 additions & 0 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ on:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
# Exercise SiteMesh 3 by default in the test apps. The example/suite builds read this
# flag to choose grails-sitemesh3 over the legacy grails-layout; unset it (or set it to
# anything other than 'true') to fall back to SiteMesh 2.
env:
SITEMESH3_TESTING_ENABLED: 'true'
jobs:
validateDependencies:
name: 'Validate Dependency Versions'
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/groovy-joint-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
# Exercise SiteMesh 3 by default in the test apps. The example/suite builds read this
# flag to choose grails-sitemesh3 over the legacy grails-layout; unset it (or set it to
# anything other than 'true') to fall back to SiteMesh 2.
env:
SITEMESH3_TESTING_ENABLED: 'true'
jobs:
build_groovy:
if: ${{ !contains(github.event.head_commit.message, '[skip tests]') }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ protected Options getOptions(@Nullable FeatureFilter filter, RequestInfo request
filter.getGorm() == null ? GormImpl.DEFAULT_OPTION : filter.getGorm(),
filter.getServlet() == null ? ServletImpl.DEFAULT_OPTION : filter.getServlet(),
filter.getJavaVersion() == null ? JdkVersion.DEFAULT_OPTION : filter.getJavaVersion(),
getOperatingSystem(requestInfo.getUserAgent()));
getOperatingSystem(requestInfo.getUserAgent()))
.withGspLayoutImpl(filter.getGspLayout() == null ? GspLayoutImpl.DEFAULT_OPTION : filter.getGspLayout());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* https://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 org.grails.forge.api;

import io.micronaut.context.MessageSource;
import io.micronaut.core.annotation.Creator;
import io.micronaut.core.annotation.Internal;
import io.micronaut.core.annotation.Introspected;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.naming.Described;
import io.micronaut.core.naming.Named;
import io.swagger.v3.oas.annotations.media.Schema;
import org.grails.forge.options.GspLayoutImpl;

/**
* DTO objects for {@link GspLayoutImpl}.
*
* @since 8.0.0
*/
@Schema(name = "GspLayoutImplInfo")
@Introspected
public class GspLayoutImplDTO extends Linkable implements Named, Described, Selectable<GspLayoutImpl> {

static final String MESSAGE_PREFIX = GrailsForgeConfiguration.PREFIX + ".gspLayoutImpl.";

private final String name;
private final String description;
private final GspLayoutImpl value;

/**
* @param gspLayoutImpl The {@link GspLayoutImpl}
*/
public GspLayoutImplDTO(GspLayoutImpl gspLayoutImpl) {
this.value = gspLayoutImpl;
this.name = gspLayoutImpl.getName();
this.description = gspLayoutImpl.getName();
}

@Creator
@Internal
GspLayoutImplDTO(GspLayoutImpl gspLayoutImpl,
String name,
String description) {
this.value = gspLayoutImpl;
this.name = name;
this.description = description;
}

@Internal
GspLayoutImplDTO(GspLayoutImpl gspLayoutImpl,
MessageSource messageSource,
MessageSource.MessageContext messageContext) {
this.value = gspLayoutImpl;
String name = gspLayoutImpl.getName();
this.name = name;
this.description = messageSource.getMessage(MESSAGE_PREFIX + name + ".description", messageContext, name);
}

@NonNull
@Override
@Schema(description = "A description of the GSP Layout Implementation")
public String getDescription() {
return description;
}

@Override
@Schema(description = "The name of the GSP Layout Implementation")
@NonNull
public String getName() {
return name;
}

@Override
@Schema(description = "The value of the GSP Layout Implementation for select options")
public GspLayoutImpl getValue() {
return value;
}

@Override
@Schema(description = "The label of the GSP Layout Implementation for select options")
public String getLabel() {
return value.getLabel();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public class SelectOptionsDTO {

private ServletImplSelectOptions servlet;

private GspLayoutImplSelectOptions gspLayout;

SelectOptionsDTO() {
}

Expand All @@ -61,13 +63,15 @@ public SelectOptionsDTO(ApplicationTypeSelectOptions type,
LanguageSelectOptions lang,
DevelopmentReloadingSelectOptions reloading,
GormImplSelectOptions gorm,
ServletImplSelectOptions servlet) {
ServletImplSelectOptions servlet,
GspLayoutImplSelectOptions gspLayout) {
this.type = type;
this.jdkVersion = jdkVersion;
this.lang = lang;
this.reloading = reloading;
this.gorm = gorm;
this.servlet = servlet;
this.gspLayout = gspLayout;
}

@Schema(description = "supported options for application type")
Expand Down Expand Up @@ -100,6 +104,11 @@ public ServletImplSelectOptions getServlet() {
return servlet;
}

@Schema(description = "supported options for GSP Layout Implementation")
public GspLayoutImplSelectOptions getGspLayout() {
return gspLayout;
}

/**
* Build the options
*
Expand Down Expand Up @@ -163,8 +172,17 @@ public static SelectOptionsDTO make(MessageSource messageSource, MessageSource.M
new ServletImplDTO(ServletImpl.DEFAULT_OPTION, messageSource, messageContext)
);

List<GspLayoutImplDTO> gspLayoutImpls = Arrays.stream(GspLayoutImpl.values())
.map(it -> new GspLayoutImplDTO(it, messageSource, messageContext))
.collect(Collectors.toList());

GspLayoutImplSelectOptions gspLayoutImplOpts = new GspLayoutImplSelectOptions(
gspLayoutImpls,
new GspLayoutImplDTO(GspLayoutImpl.DEFAULT_OPTION, messageSource, messageContext)
);


return new SelectOptionsDTO(applicationOpts, jdkVersionOpts, languageOpts, developmentReloadingOpts, gormImplOpts, servletImplOpts);
return new SelectOptionsDTO(applicationOpts, jdkVersionOpts, languageOpts, developmentReloadingOpts, gormImplOpts, servletImplOpts, gspLayoutImplOpts);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* https://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 org.grails.forge.api.options;

import io.swagger.v3.oas.annotations.media.Schema;
import org.grails.forge.api.GspLayoutImplDTO;
import org.grails.forge.api.SelectOptionDTO;

import java.util.List;

@Schema(name = "GspLayoutImplSelectOptions")
public class GspLayoutImplSelectOptions extends SelectOptionDTO<GspLayoutImplDTO> {
public GspLayoutImplSelectOptions(List<GspLayoutImplDTO> options, GspLayoutImplDTO defaultOption) {
super(options, defaultOption);
}

@Override
public List<GspLayoutImplDTO> getOptions() {
return super.getOptions();
}

@Override
public GspLayoutImplDTO getDefaultOption() {
return super.getDefaultOption();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* https://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 org.grails.forge.api.options

import io.micronaut.http.HttpRequest
import io.micronaut.http.client.HttpClient
import io.micronaut.http.client.annotation.Client
import io.micronaut.test.extensions.spock.annotation.MicronautTest
import jakarta.inject.Inject
import org.grails.forge.api.SelectOptionsDTO
import org.grails.forge.options.GspLayoutImpl
import spock.lang.Specification

@MicronautTest
class SelectOptionsControllerSpec extends Specification {

@Inject
@Client("/")
HttpClient httpClient

void "select options expose the GSP layout implementations with SiteMesh 3 as the default"() {
when:
SelectOptionsDTO selectOptions = httpClient.toBlocking()
.retrieve(HttpRequest.GET('/select-options'), SelectOptionsDTO)

then:
selectOptions.gspLayout
selectOptions.gspLayout.defaultOption.value == GspLayoutImpl.SITEMESH3
selectOptions.gspLayout.options*.value as Set == [GspLayoutImpl.SITEMESH3, GspLayoutImpl.GRAILS_LAYOUT] as Set
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ public abstract class CreateCommand extends BaseCommand implements Callable<Inte
@CommandLine.Option(names = {"-s", "--servlet"}, paramLabel = "Servlet Implementation", description = "Which Servlet Implementation to configure. Possible values: ${COMPLETION-CANDIDATES}.", completionCandidates = ServletImplCandidates.class, converter = ServletImplConverter.class)
ServletImpl servletImpl;

@ReflectiveAccess
@CommandLine.Option(names = {"--gsp-layout"}, paramLabel = "GSP Layout Implementation", description = "Which GSP layout (SiteMesh) implementation to configure. Possible values: ${COMPLETION-CANDIDATES}.", completionCandidates = GspLayoutImplCandidates.class, converter = GspLayoutImplConverter.class)
GspLayoutImpl gspLayoutImpl;

@ReflectiveAccess
@CommandLine.Option(names = {"-i", "--inplace"}, description = "Create a service using the current directory")
boolean inplace;
Expand Down Expand Up @@ -96,7 +100,7 @@ protected Map<String, Object> getAdditionalOptions() {
public Integer call() throws Exception {
if (listFeatures) {
new ListFeatures(availableFeatures,
new Options(reloading, gormImpl, servletImpl, getJdkVersion(), getOperatingSystem()),
new Options(reloading, gormImpl, servletImpl, getJdkVersion(), getOperatingSystem()).withGspLayoutImpl(getGspLayoutImpl()),
applicationType,
getOperatingSystem(),
contextFactory).output(this);
Expand All @@ -122,11 +126,16 @@ public void generate(OutputHandler outputHandler) throws Exception {
}

public void generate(Project project, OutputHandler outputHandler) throws Exception {
Options options = new Options(reloading, gormImpl, servletImpl, getJdkVersion(), getOperatingSystem(), getAdditionalOptions());
Options options = new Options(reloading, gormImpl, servletImpl, getJdkVersion(), getOperatingSystem(), getAdditionalOptions())
.withGspLayoutImpl(getGspLayoutImpl());

projectGenerator.generate(applicationType, project, options, getOperatingSystem(), getSelectedFeatures(), outputHandler, this);
}

private GspLayoutImpl getGspLayoutImpl() {
return gspLayoutImpl == null ? GspLayoutImpl.DEFAULT_OPTION : gspLayoutImpl;
}

private JdkVersion getJdkVersion() {
if (javaVersion == null) {
return JdkVersion.DEFAULT_OPTION;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* https://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 org.grails.forge.cli.command;

import org.grails.forge.options.GspLayoutImpl;

import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class GspLayoutImplCandidates extends ArrayList<String> {
public GspLayoutImplCandidates() {
super(Stream.of(GspLayoutImpl.values()).map(GspLayoutImpl::getName).collect(Collectors.toList()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* https://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 org.grails.forge.cli.command;

import io.micronaut.core.annotation.Introspected;
import org.grails.forge.options.GspLayoutImpl;
import picocli.CommandLine;

@Introspected
public class GspLayoutImplConverter implements CommandLine.ITypeConverter<GspLayoutImpl> {

@Override
public GspLayoutImpl convert(String value) throws Exception {
if (value == null) {
return GspLayoutImpl.DEFAULT_OPTION;
} else {
for (GspLayoutImpl impl : GspLayoutImpl.values()) {
if (value.equalsIgnoreCase(impl.getName()) || value.equalsIgnoreCase(impl.name())) {
return impl;
}
}
}
throw new CommandLine.TypeConversionException("Invalid GSP layout implementation selection: " + value);
}
}
Loading
Loading