diff --git a/examples/pom.xml b/examples/pom.xml
index fa89b284..9acf019c 100644
--- a/examples/pom.xml
+++ b/examples/pom.xml
@@ -50,7 +50,6 @@
openapiopenapi-toolssecurity
- todo-apptranslator-appwebserverintegrations
diff --git a/examples/todo-app/README.md b/examples/todo-app/README.md
deleted file mode 100644
index bdaae1f0..00000000
--- a/examples/todo-app/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# TODO Demo Application
-
-This application implements todomvc[http://todomvc.com] with two microservices
-implemented with Helidon MP and Helidon SE.
-
-## Build
-
-```shell
-mvn clean package
-docker build -t helidon-examples-todo-cassandra cassandra
-```
-
-## Run
-
-```shell
-docker run -d -p 9042:9042 --name helidon-examples-todo-cassandra helidon-examples-todo-cassandra
-docker run --name zipkin -d -p 9411:9411 openzipkin/zipkin
-java -jar backend/target/helidon-examples-todo-backend.jar &
-java -jar frontend/target/helidon-examples-todo-frontend.jar &
-```
-
-- Open http://localhost:8080 in your browser
-- Login with a Google account
-- Add some TODO entries
-- Check-out the traces at http://localhost:9411
-
-### HTTP proxy
-
-If you want to run behind an HTTP proxy:
-
-```shell
-export security_providers_0_google_dash_login_proxy_dash_host=proxy.acme.com
-export security_providers_0_google_dash_login_proxy_dash_port=80
-```
-
-## Stop
-
-```shell
-kill %1 %2
-docker rm -f zipkin helidon-examples-todo-cassandra
-```
diff --git a/examples/todo-app/backend/pom.xml b/examples/todo-app/backend/pom.xml
deleted file mode 100644
index 47990769..00000000
--- a/examples/todo-app/backend/pom.xml
+++ /dev/null
@@ -1,161 +0,0 @@
-
-
-
-
- 4.0.0
-
- io.helidon.applications
- helidon-mp
- 2.6.8-SNAPSHOT
-
-
- io.helidon.examples.todos
- helidon-examples-todo-backend
- 1.0.0-SNAPSHOT
- Helidon Examples TODO Demo Backend
-
-
- Back-end part of the application uses Helidon MP
-
-
-
- io.helidon.demo.todos.backend.Main
- 3.10.2
- 4.3.1.0
- 4.9.0
- 4.9.0
- 3.0.2
- 1.32
-
-
-
-
-
- com.datastax.cassandra
- cassandra-driver-core
- ${version.lib.cassandra}
-
-
- io.dropwizard.metrics
- metrics-core
-
-
-
-
- org.yaml
- snakeyaml
- ${version.lib.snakeyaml.override}
-
-
-
-
-
-
- io.helidon.microprofile.bundles
- helidon-microprofile-core
-
-
- io.helidon.microprofile
- helidon-microprofile-security
-
-
- io.helidon.security.providers
- helidon-security-providers-google-login
-
-
- io.helidon.security.providers
- helidon-security-providers-http-sign
-
-
- io.helidon.security.providers
- helidon-security-providers-abac
-
-
- io.helidon.microprofile.tracing
- helidon-microprofile-tracing
-
-
- io.helidon.tracing
- helidon-tracing-zipkin
-
-
- com.datastax.cassandra
- cassandra-driver-core
-
-
- org.junit.jupiter
- junit-jupiter-api
- test
-
-
- io.helidon.microprofile.tests
- helidon-microprofile-tests-junit5
- test
-
-
- org.cassandraunit
- cassandra-unit
- ${version.cassandra.unit}
- test
-
-
- com.datastax.oss
- java-driver-core
- ${version.datastax.driver.core}
- test
-
-
- com.datastax.oss
- java-driver-query-builder
- ${version.datastax.driver.query.builder}
- test
-
-
- com.codahale.metrics
- metrics-core
- ${version.codahale.metrics.core}
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- copy-libs
-
-
-
-
- org.jboss.jandex
- jandex-maven-plugin
-
-
- make-index
-
-
-
-
-
-
diff --git a/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/DbService.java b/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/DbService.java
deleted file mode 100644
index 5dc28c83..00000000
--- a/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/DbService.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (c) 2017, 2024 Oracle and/or its affiliates.
- *
- * 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.helidon.demo.todos.backend;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Supplier;
-
-import javax.enterprise.context.ApplicationScoped;
-import javax.inject.Inject;
-
-import io.helidon.config.Config;
-import io.helidon.security.SecurityException;
-
-import com.datastax.driver.core.BoundStatement;
-import com.datastax.driver.core.Cluster;
-import com.datastax.driver.core.PreparedStatement;
-import com.datastax.driver.core.ResultSet;
-import com.datastax.driver.core.Row;
-import com.datastax.driver.core.Session;
-import io.opentracing.Span;
-import io.opentracing.SpanContext;
-import io.opentracing.tag.Tags;
-import io.opentracing.util.GlobalTracer;
-
-/**
- * A service showing to access a no-SQL database.
- */
-@ApplicationScoped
-public class DbService {
-
- private static final String LIST_QUERY = "select * from backend where user = ? ALLOW FILTERING";
- private static final String GET_QUERY = "select * from backend where id = ?";
- private static final String INSERT_QUERY = "insert into backend (id, user, message, completed, created)"
- + " values (?, ?, ?, ?, ?)";
- private static final String UPDATE_QUERY = "update backend set message = ?, completed = ? where id = ? if user = ?";
- private static final String DELETE_QUERY = "delete from backend where id = ?";
-
- private final Session session;
- private final PreparedStatement listStatement;
- private final PreparedStatement getStatement;
- private final PreparedStatement insertStatement;
- private final PreparedStatement updateStatement;
- private final PreparedStatement deleteStatement;
-
- /**
- * Create a new {@code DbService} instance.
- * @param config the configuration root
- */
- @Inject
- public DbService(Config config) {
- Cluster.Builder clusterBuilder = Cluster.builder()
- .withoutMetrics();
-
- Config cConfig = config.get("cassandra");
- cConfig.get("servers").asList(Config.class).stream()
- .flatMap(Collection::stream)
- .map(server -> server.get("host").asString().get())
- .forEach(clusterBuilder::addContactPoints);
- cConfig.get("port").asInt().ifPresent(clusterBuilder::withPort);
-
- Cluster cluster = clusterBuilder.build();
- session = cluster.connect("backend");
-
- listStatement = session.prepare(LIST_QUERY);
- getStatement = session.prepare(GET_QUERY);
- insertStatement = session.prepare(INSERT_QUERY);
- updateStatement = session.prepare(UPDATE_QUERY);
- deleteStatement = session.prepare(DELETE_QUERY);
- }
-
- /**
- * Invoke the given supplier and wrap it around with a tracing
- * {@code Span}.
- * @param the supplier return type
- * @param tracingSpan the parent span to use
- * @param operation the name of the operation
- * @param supplier the supplier to invoke
- * @return the object returned by the supplier
- */
- private static T execute(SpanContext tracingSpan, String operation, Supplier supplier) {
- Span span = startSpan(tracingSpan, operation);
-
- try {
- return supplier.get();
- } catch (Exception e) {
- Tags.ERROR.set(span, true);
- span.log(Map.of("event", "error", "error.object", e));
- throw e;
- } finally {
- span.finish();
- }
- }
-
- /**
- * Utility method to create and start a child span of the given span.
- * @param span the parent span
- * @param operation the name for the new span
- * @return the created span
- */
- private static Span startSpan(SpanContext span, String operation) {
- return GlobalTracer.get().buildSpan(operation).asChildOf(span).start();
- }
-
- /**
- * Retrieve the TODOs entries from the database.
- * @param tracingSpan the tracing span to use
- * @param userId the database user id
- * @return retrieved entries as {@code Iterable}
- */
- Iterable list(SpanContext tracingSpan, String userId) {
- return execute(tracingSpan, "cassandra::list", () -> {
- BoundStatement bs = listStatement.bind(userId);
- ResultSet rs = session.execute(bs);
-
- List result = new ArrayList<>();
- for (Row r : rs) {
- result.add(Todo.fromDb(r));
- }
-
- return result;
- });
- }
-
- /**
- * Get the entry identified by the given ID from the database.
- * @param tracingSpan the tracing span to use
- * @param id the ID identifying the entry to retrieve
- * @param userId the database user id
- * @return retrieved entry as {@code Optional}
- */
- Optional get(SpanContext tracingSpan, String id, String userId) {
- return execute(tracingSpan, "cassandra::get", () -> getNoContext(id, userId));
- }
-
- /**
- * Get the entry identified by the given ID from the database, fails if the
- * entry is not associated with the given {@code userId}.
- * @param id the ID identifying the entry to retrieve
- * @param userId the database user id
- * @return retrieved entry as {@code Optional}
- */
- private Optional getNoContext(String id, String userId) {
- BoundStatement bs = getStatement.bind(id);
- ResultSet rs = session.execute(bs);
- Row one = rs.one();
- if (null == one) {
- return Optional.empty();
- }
- Todo result = Todo.fromDb(one);
- if (userId.equals(result.getUserId())) {
- return Optional.of(result);
- }
- throw new SecurityException(String.format(
- "User %s attempted to read record %s of another user",
- userId, id));
- }
-
- /**
- * Update the given entry in the database.
- * @param tracingSpan the tracing span to use
- * @param entry the entry to update
- * @return {@code Optional} of updated entry if the update was successful,
- * otherwise an empty {@code Optional}
- */
- Optional update(SpanContext tracingSpan, Todo entry) {
- return execute(tracingSpan, "cassandra::update", () -> {
- //update backend set message = ?
- // , completed = ? where id = ? if user = ?
- BoundStatement bs = updateStatement.bind(
- entry.getTitle(),
- entry.getCompleted(),
- entry.getId(),
- entry.getUserId());
- ResultSet execute = session.execute(bs);
-
- if (execute.wasApplied()) {
- return Optional.of(entry);
- } else {
- return Optional.empty();
- }
- });
- }
-
- /**
- * Delete the entry identified by the given ID in from the database.
- * @param tracingSpan the tracing span to use
- * @param id the ID identifying the entry to delete
- * @param userId the database user id
- * @return the deleted entry as {@code Optional}
- */
- Optional delete(SpanContext tracingSpan, String id, String userId) {
- return execute(tracingSpan, "cassandra::delete",
- () -> getNoContext(id, userId)
- .map(todo -> {
- BoundStatement bs = deleteStatement.bind(id);
- ResultSet rs = session.execute(bs);
- if (!rs.wasApplied()) {
- throw new RuntimeException("Failed to delete todo: "
- + todo);
- }
- return todo;
- }));
- }
-
- /**
- * Insert a new entry in the database.
- * @param tracingSpan the tracing span to use
- * @param entry the entry to insert
- */
- void insert(SpanContext tracingSpan, Todo entry) {
- execute(tracingSpan, "cassandra::insert", () -> {
- BoundStatement bs = insertStatement
- .bind(entry.getId(),
- entry.getUserId(),
- entry.getTitle(),
- entry.getCompleted(),
- new Date(entry.getCreated()));
-
- ResultSet execute = session.execute(bs);
- if (!execute.wasApplied()) {
- throw new RuntimeException("Failed to insert todo: " + entry);
- }
- return null;
- });
- }
-}
diff --git a/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/JaxRsBackendResource.java b/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/JaxRsBackendResource.java
deleted file mode 100644
index d0238b9d..00000000
--- a/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/JaxRsBackendResource.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (c) 2017, 2024 Oracle and/or its affiliates.
- *
- * 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.helidon.demo.todos.backend;
-
-import java.util.Collections;
-import java.util.UUID;
-
-import javax.enterprise.context.ApplicationScoped;
-import javax.inject.Inject;
-import javax.json.Json;
-import javax.json.JsonArrayBuilder;
-import javax.json.JsonBuilderFactory;
-import javax.json.JsonObject;
-import javax.ws.rs.Consumes;
-import javax.ws.rs.DELETE;
-import javax.ws.rs.GET;
-import javax.ws.rs.POST;
-import javax.ws.rs.PUT;
-import javax.ws.rs.Path;
-import javax.ws.rs.PathParam;
-import javax.ws.rs.Produces;
-import javax.ws.rs.core.Context;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-
-import io.helidon.security.Principal;
-import io.helidon.security.SecurityContext;
-import io.helidon.security.Subject;
-import io.helidon.security.annotations.Authenticated;
-import io.helidon.security.annotations.Authorized;
-
-import io.opentracing.Tracer;
-import org.eclipse.microprofile.opentracing.Traced;
-
-/**
- * The TODO backend REST service.
- */
-@Path("/api/backend")
-@Authenticated
-@Authorized
-@ApplicationScoped
-public class JaxRsBackendResource {
-
- private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());
-
- private final DbService backendService;
- private final Tracer tracer;
-
- /**
- * Create new {@code JaxRsBackendResource} instance.
- * @param dbs the database service facade to use
- * @param tracer tracer to use
- */
- @Inject
- public JaxRsBackendResource(DbService dbs, Tracer tracer) {
- this.backendService = dbs;
- this.tracer = tracer;
- }
-
- /**
- * Retrieve all TODO entries.
- *
- * @param context security context to map the user
- * @return the response with the retrieved entries as entity
- */
- @GET
- @Produces(MediaType.APPLICATION_JSON)
- @Traced(operationName = "jaxrs:list")
- public Response list(@Context SecurityContext context) {
- JsonArrayBuilder builder = JSON.createArrayBuilder();
- backendService.list(tracer.activeSpan().context(), getUserId(context))
- .forEach(data -> builder.add(data.forRest()));
- return Response.ok(builder.build()).build();
- }
-
- /**
- * Get the TODO entry identified by the given ID.
- * @param id the ID of the entry to retrieve
- * @param context security context to map the user
- * @return the response with the retrieved entry as entity
- */
- @GET
- @Path("/{id}")
- @Produces(MediaType.APPLICATION_JSON)
- public Response get(@PathParam("id") String id, @Context SecurityContext context) {
-
- return backendService
- .get(tracer.activeSpan().context(), id, getUserId(context))
- .map(Todo::forRest)
- .map(Response::ok)
- .orElse(Response.status(Response.Status.NOT_FOUND))
- .build();
- }
-
- /**
- * Delete the TODO entry identified by the given ID.
- * @param id the id of the entry to delete
- * @param context security context to map the user
- * @return the response with the deleted entry as entity
- */
- @DELETE
- @Path("/{id}")
- @Produces(MediaType.APPLICATION_JSON)
- public Response delete(@PathParam("id") String id, @Context SecurityContext context) {
-
- return backendService
- .delete(tracer.activeSpan().context(), id, getUserId(context))
- .map(Todo::forRest)
- .map(Response::ok)
- .orElse(Response.status(Response.Status.NOT_FOUND))
- .build();
- }
-
- /**
- * Create a new TODO entry.
- * @param jsonObject the value of the new entry
- * @param context security context to map the user
- * @return the response ({@code 200} status if successful
- */
- @POST
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- public Response createIt(JsonObject jsonObject, @Context SecurityContext context) {
-
- String newId = UUID.randomUUID().toString();
- String userId = getUserId(context);
- Todo newBackend = Todo.newTodoFromRest(jsonObject, userId, newId);
-
- backendService.insert(tracer.activeSpan().context(), newBackend);
-
- return Response.ok(newBackend.forRest()).build();
- }
-
- /**
- * Update the TODO entry identified by the given ID.
- * @param id the ID of the entry to update
- * @param jsonObject the updated value of the entry
- * @param context security context to map the user
- * @return the response with the updated entry as entity
- */
- @PUT
- @Path("/{id}")
- @Consumes(MediaType.APPLICATION_JSON)
- @Produces(MediaType.APPLICATION_JSON)
- public Response update(@PathParam("id") String id, JsonObject jsonObject, @Context SecurityContext context) {
- return backendService
- .update(tracer.activeSpan().context(), Todo.fromRest(jsonObject, getUserId(context), id))
- .map(Todo::forRest)
- .map(Response::ok)
- .orElse(Response.status(Response.Status.NOT_FOUND))
- .build();
- }
-
- /**
- * Get the user id from the security context.
- * @param context the security context
- * @return user id found in the context or {@code } otherwise
- */
- private String getUserId(SecurityContext context) {
- return context.user()
- .map(Subject::principal)
- .map(Principal::id)
- .orElse("");
- }
-}
diff --git a/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/Main.java b/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/Main.java
deleted file mode 100644
index 584b7d26..00000000
--- a/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/Main.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2017, 2024 Oracle and/or its affiliates.
- *
- * 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.helidon.demo.todos.backend;
-
-import java.util.List;
-
-import io.helidon.common.LogConfig;
-import io.helidon.config.Config;
-import io.helidon.microprofile.server.Server;
-
-import static io.helidon.config.ConfigSources.classpath;
-import static io.helidon.config.ConfigSources.environmentVariables;
-import static io.helidon.config.ConfigSources.file;
-
-/**
- * Main class to start the service.
- */
-public final class Main {
-
- /**
- * Cannot be instantiated.
- */
- private Main() {
- }
-
- /**
- * Application main entry point.
- *
- * @param args command line arguments
- */
- public static void main(final String[] args) {
-
- // load logging configuration
- LogConfig.configureRuntime();
-
- Config config = buildConfig();
-
- // as we need to use custom filter
- // we need to build Server with custom config
- Server server = Server.builder()
- .config(config)
- .build();
-
- server.start();
- }
-
- /**
- * Load the configuration from all sources.
- * @return the configuration root
- */
- static Config buildConfig() {
- return Config.builder()
- .sources(List.of(
- environmentVariables(),
- // expected on development machine
- // to override props for dev
- file("dev.yaml").optional(),
- // expected in k8s runtime
- // to configure testing/production values
- file("prod.yaml").optional(),
- // in jar file
- // (see src/main/resources/application.yaml)
- classpath("application.yaml")))
- // support for passwords in configuration
- //.addFilter(SecureConfigFilter.fromConfig())
- .build();
- }
-}
diff --git a/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/Todo.java b/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/Todo.java
deleted file mode 100644
index d1672195..00000000
--- a/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/Todo.java
+++ /dev/null
@@ -1,261 +0,0 @@
-/*
- * Copyright (c) 2017, 2024 Oracle and/or its affiliates.
- *
- * 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.helidon.demo.todos.backend;
-
-import java.time.Instant;
-import java.time.format.DateTimeFormatter;
-import java.util.Collections;
-import java.util.UUID;
-
-import javax.json.Json;
-import javax.json.JsonBuilderFactory;
-import javax.json.JsonNumber;
-import javax.json.JsonObject;
-import javax.json.JsonObjectBuilder;
-
-import com.datastax.driver.core.Row;
-
-/**
- * Data object for backend.
- */
-public final class Todo {
-
- /**
- * Date formatter to format the dates of the TODO entries.
- */
- private static final DateTimeFormatter DATE_FORMAT =
- DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSVV");
-
- /**
- * Factory for creating JSON builders.
- */
- private static final JsonBuilderFactory JSON = Json.createBuilderFactory(Collections.emptyMap());
-
- /**
- * The TODO ID.
- */
- private String id;
-
- /**
- * The user ID associated with this TODO.
- */
- private String userId;
-
- /**
- * The TODO title.
- */
- private String title;
-
- /**
- * The TODO completed flag.
- */
- private Boolean completed;
-
- /**
- * The TODO creation timestamp.
- */
- private long created;
-
- /**
- * Create a new {@code Todo} instance from a database entry in JSON format.
- * @param jsonObject the database entry
- * @return the created instance
- */
- public static Todo fromDb(final JsonObject jsonObject) {
-
- Todo result = new Todo();
- result.id = jsonObject.getString("id");
- result.userId = jsonObject.getString("user");
- result.title = jsonObject.getString("message");
- result.completed = jsonObject.getBoolean("completed");
- result.created = Instant.from(DATE_FORMAT
- .parse(jsonObject.getString("created"))).toEpochMilli();
- return result;
- }
-
- /**
- * Create a new {@code Todo} instance from a REST entry.
- * The created entry will be new, i.e the {@code completed} flag will be set
- * to {@code false} and the {@code created} timestamp set to the current
- * time.
- * @param jsonObject the REST entry
- * @param userId the user ID associated with this entry
- * @param id the entry ID
- * @return the created instance
- */
- public static Todo newTodoFromRest(final JsonObject jsonObject,
- final String userId,
- final String id) {
-
- Todo result = new Todo();
- result.id = id;
- result.userId = userId;
- result.title = jsonObject.getString("title");
- result.completed = jsonObject.getBoolean("completed", false);
- result.created = System.currentTimeMillis();
- return result;
- }
-
- /**
- * Create a new {@code Todo} instance from a REST entry.
- * @param jsonObject the REST entry
- * @param userId the user ID associated with this entry
- * @param id the entry ID
- * @return the created instance
- */
- public static Todo fromRest(final JsonObject jsonObject,
- final String userId,
- final String id) {
-
- Todo result = new Todo();
- result.id = id;
- result.userId = userId;
- result.title = jsonObject.getString("title", "");
- result.completed = jsonObject.getBoolean("completed");
- JsonNumber created = jsonObject.getJsonNumber("created");
- if (null != created) {
- result.created = created.longValue();
- }
- return result;
- }
-
- /**
- * Create a new {@code Todo} instance from a database entry.
- * @param row the database entry
- * @return the created instance
- */
- public static Todo fromDb(final Row row) {
-
- Todo result = new Todo();
- result.id = row.getString("id");
- result.userId = row.getString("user");
- result.title = row.getString("message");
- result.completed = row.getBool("completed");
- result.created = row.getTimestamp("created").getTime();
- return result;
- }
-
- /**
- * Create a new {@code Todo} instance.
- * The created entry will be new, i.e the {@code completed} flag will be set
- * to {@code false} and the {@code created} timestamp set to the current
- * time.
- * @param userId the user ID associated with the new entry
- * @param title the title for the new entry
- * @return the created instance
- */
- public static Todo create(final String userId, final String title) {
- Todo result = new Todo();
-
- result.id = UUID.randomUUID().toString();
- result.userId = userId;
- result.title = title;
- result.completed = false;
- result.created = System.currentTimeMillis();
-
- return result;
- }
-
- /**
- * Convert this {@code Todo} instance to the JSON database format.
- * @return {@code JsonObject}
- */
- public JsonObject forDb() {
- //to store to DB
- JsonObjectBuilder builder = JSON.createObjectBuilder();
- return builder.add("id", id)
- .add("user", userId)
- .add("message", title)
- .add("completed", completed)
- .add("created", created)
- .build();
- }
-
- /**
- * Convert this {@code Todo} instance to the JSON REST format.
- * @return {@code JsonObject}
- */
- public JsonObject forRest() {
- //to send over to rest
- JsonObjectBuilder builder = JSON.createObjectBuilder();
- return builder.add("id", id)
- .add("user", userId)
- .add("title", title)
- .add("completed", completed)
- .add("created", created)
- .build();
- }
-
- /**
- * Get the TODO ID.
- * @return the {@code String} identifying this entry
- */
- public String getId() {
- return id;
- }
-
- /**
- * Get the user ID associated with this TODO.
- * @return the {@code String} identifying the user
- */
- public String getUserId() {
- return userId;
- }
-
- /**
- * Get the TODO title.
- * @return title
- */
- public String getTitle() {
- return title;
- }
-
- /**
- * Get the completed flag.
- * @return completed flag.
- */
- public Boolean getCompleted() {
- return completed;
- }
-
- /**
- * Set the completed flag.
- * @param iscomplete the completed flag value
- */
- public void setCompleted(final boolean iscomplete) {
- this.completed = iscomplete;
- }
-
- /**
- * Get the creation timestamp.
- * @return timestamp
- */
- public long getCreated() {
- return created;
- }
-
- @Override
- public String toString() {
- return "Todo{"
- + "id='" + id + '\''
- + ", userId='" + userId + '\''
- + ", title='" + title + '\''
- + ", completed=" + completed
- + ", created=" + created
- + '}';
- }
-}
diff --git a/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/package-info.java b/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/package-info.java
deleted file mode 100644
index 111b64f6..00000000
--- a/examples/todo-app/backend/src/main/java/io/helidon/demo/todos/backend/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2017, 2024 Oracle and/or its affiliates.
- *
- * 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.
- */
-
-/**
- * TODOs Demo application backend.
- */
-package io.helidon.demo.todos.backend;
diff --git a/examples/todo-app/backend/src/main/resources/META-INF/beans.xml b/examples/todo-app/backend/src/main/resources/META-INF/beans.xml
deleted file mode 100644
index 7075ac07..00000000
--- a/examples/todo-app/backend/src/main/resources/META-INF/beans.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
diff --git a/examples/todo-app/backend/src/main/resources/application.yaml b/examples/todo-app/backend/src/main/resources/application.yaml
deleted file mode 100644
index 8727766d..00000000
--- a/examples/todo-app/backend/src/main/resources/application.yaml
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# Copyright (c) 2018, 2024 Oracle and/or its affiliates.
-#
-# 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.
-#
-env: docker
-
-server:
- port: 8854
- host: 0.0.0.0
-
-tracing:
- service: "todo:back"
- port: 9411
-
-cassandra:
- port: 9042
- servers:
- - host: "localhost"
-
-security:
- config:
- require-encryption: false
- aes.insecure-passphrase: "changeit"
- provider-policy:
- type: "COMPOSITE"
- authentication:
- - name: "google-login"
- - name: "http-signatures"
- providers:
- - google-login:
- client-id: "1048216952820-6a6ke9vrbjlhngbc0al0dkj9qs9tqbk2.apps.googleusercontent.com"
- - abac:
- - http-signatures:
- inbound.keys:
- - key-id: "frontend"
- principal-name: "Frontend Service"
- hmac.secret: "${CLEAR=changeit}"
diff --git a/examples/todo-app/backend/src/main/resources/logging.properties b/examples/todo-app/backend/src/main/resources/logging.properties
deleted file mode 100644
index 3ed94f67..00000000
--- a/examples/todo-app/backend/src/main/resources/logging.properties
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# Copyright (c) 2017, 2024 Oracle and/or its affiliates.
-#
-# 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.
-#
-
-#All attributes details
-handlers=io.helidon.common.HelidonConsoleHandler
-java.util.logging.SimpleFormatter.format=%1$tY.%1$tm.%1$td %1$tH:%1$tM:%1$tS %4$s %3$s !thread!: %5$s%6$s%n
-
-#All log level details
-.level=WARNING
-io.helidon.webserver.level=INFO
-io.helidon.security.level=INFO
-io.helidon.tracing.level=FINE
-AUDIT.level=FINEST
-
-
diff --git a/examples/todo-app/backend/src/test/java/io/helidon/demo/todos/backend/BackendTests.java b/examples/todo-app/backend/src/test/java/io/helidon/demo/todos/backend/BackendTests.java
deleted file mode 100644
index 52a2d5a0..00000000
--- a/examples/todo-app/backend/src/test/java/io/helidon/demo/todos/backend/BackendTests.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (c) 2021, 2024 Oracle and/or its affiliates.
- *
- * 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.helidon.demo.todos.backend;
-
-import java.io.IOException;
-import java.util.Base64;
-import java.util.Properties;
-
-import javax.inject.Inject;
-import javax.json.Json;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.ws.rs.client.Entity;
-import javax.ws.rs.client.WebTarget;
-import javax.ws.rs.core.MediaType;
-
-import io.helidon.common.http.Http;
-import io.helidon.config.mp.MpConfigSources;
-import io.helidon.config.yaml.mp.YamlMpConfigSource;
-import io.helidon.microprofile.tests.junit5.Configuration;
-import io.helidon.microprofile.tests.junit5.HelidonTest;
-
-import com.datastax.driver.core.Cluster;
-import com.datastax.driver.core.Session;
-import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
-import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-
-@HelidonTest
-@Configuration(useExisting = true)
-class BackendTests {
-
- private final static String CASSANDRA_HOST = "127.0.0.1";
-
- @Inject
- private WebTarget webTarget;
-
- @BeforeAll
- static void init() throws IOException {
- Properties cassandraProperties = initCassandra();
-
- ClassLoader cl = Thread.currentThread().getContextClassLoader();
- ConfigProviderResolver configResolver = ConfigProviderResolver.instance();
-
- org.eclipse.microprofile.config.Config mpConfig = configResolver.getBuilder()
- .withSources(YamlMpConfigSource.create(cl.getResource("test-application.yaml")),
- MpConfigSources.create(cassandraProperties))
- .build();
-
- configResolver.registerConfig(mpConfig, null);
- }
-
- @AfterAll
- static void stopServer() {
- EmbeddedCassandraServerHelper.cleanEmbeddedCassandra();
- }
-
- private static Properties initCassandra() throws IOException {
- EmbeddedCassandraServerHelper.startEmbeddedCassandra(EmbeddedCassandraServerHelper.CASSANDRA_RNDPORT_YML_FILE,
- 20000L);
- Properties prop = new Properties();
- prop.put("cassandra.port", String.valueOf(EmbeddedCassandraServerHelper.getNativeTransportPort()));
- prop.put("cassandra.servers.host.host", CASSANDRA_HOST);
-
- Cluster cluster = Cluster.builder()
- .withoutMetrics()
- .addContactPoint(CASSANDRA_HOST)
- .withPort(EmbeddedCassandraServerHelper.getNativeTransportPort())
- .build();
-
- Session session = cluster.connect();
- session.execute("CREATE KEYSPACE backend WITH REPLICATION = {'class' : 'SimpleStrategy', 'replication_factor' : 1};");
- session.execute(
- "CREATE TABLE backend.backend (id ascii, user ascii, message ascii, completed Boolean, created timestamp, "
- + "PRIMARY KEY (id));");
- session.execute("select * from backend.backend;");
-
- session.close();
- cluster.close();
-
- return prop;
- }
-
- @Test
- void testTodoScenario() {
- String basicAuth = "Basic " + Base64.getEncoder().encodeToString("john:changeit".getBytes());
- JsonObject todo = Json.createObjectBuilder()
- .add("title", "todo title")
- .build();
-
- // Add a new todo
- JsonObject returnedTodo = webTarget
- .path("/api/backend")
- .request(MediaType.APPLICATION_JSON_TYPE)
- .header(Http.Header.AUTHORIZATION, basicAuth)
- .post(Entity.json(todo), JsonObject.class);
-
- assertThat(returnedTodo.getString("user"), is("john"));
- assertThat(returnedTodo.getString("title"), is(todo.getString("title")));
-
- // Get the todo created earlier
- JsonObject fromServer = webTarget.path("/api/backend/" + returnedTodo.getString("id"))
- .request(MediaType.APPLICATION_JSON_TYPE)
- .header(Http.Header.AUTHORIZATION, basicAuth)
- .get(JsonObject.class);
-
- assertThat(fromServer, is(returnedTodo));
-
- // Update the todo created earlier
- JsonObject updatedTodo = Json.createObjectBuilder()
- .add("title", "updated title")
- .add("completed", false)
- .build();
-
- fromServer = webTarget.path("/api/backend/" + returnedTodo.getString("id"))
- .request(MediaType.APPLICATION_JSON_TYPE)
- .header(Http.Header.AUTHORIZATION, basicAuth)
- .put(Entity.json(updatedTodo), JsonObject.class);
-
- assertThat(fromServer.getString("title"), is(updatedTodo.getString("title")));
-
- // Delete the todo created earlier
- fromServer = webTarget.path("/api/backend/" + returnedTodo.getString("id"))
- .request(MediaType.APPLICATION_JSON_TYPE)
- .header(Http.Header.AUTHORIZATION, basicAuth)
- .delete(JsonObject.class);
-
- assertThat(fromServer.getString("id"), is(returnedTodo.getString("id")));
-
- // Get list of todos
- JsonArray jsonValues = webTarget.path("/api/backend")
- .request(MediaType.APPLICATION_JSON_TYPE)
- .header(Http.Header.AUTHORIZATION, basicAuth)
- .get(JsonArray.class);
-
- assertThat("There should be no todos on server", jsonValues.size(), is(0));
- }
-
-}
diff --git a/examples/todo-app/backend/src/test/resources/test-application.yaml b/examples/todo-app/backend/src/test/resources/test-application.yaml
deleted file mode 100644
index 299eed13..00000000
--- a/examples/todo-app/backend/src/test/resources/test-application.yaml
+++ /dev/null
@@ -1,40 +0,0 @@
-#
-# Copyright (c) 2021, 2024 Oracle and/or its affiliates.
-#
-# 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.
-#
-
-# increase importance
-config_ordinal: 500
-
-# we use custom config and Helidon JUnit integration, must allow initializer
-mp:
- initializer:
- allow: true
- no-warn: true
-
-server:
- port: 0
- host: localhost
-
-tracing:
- service: "todo:back"
- enabled: false
-
-security:
- providers:
- - http-basic-auth:
- realm: "helidon"
- users:
- - login: "john"
- password: "changeit"
diff --git a/examples/todo-app/cassandra/Dockerfile b/examples/todo-app/cassandra/Dockerfile
deleted file mode 100644
index b41a1b50..00000000
--- a/examples/todo-app/cassandra/Dockerfile
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# Copyright (c) 2017, 2024 Oracle and/or its affiliates.
-#
-# 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.
-#
-
-FROM cassandra:3.11.16
-
-ADD startup.sh /
-
-RUN chmod -v u=rx,og-rwx /startup.sh
-
-ENTRYPOINT ["/startup.sh"]
-
-EXPOSE 7000 7001 7199 9042 9160
-
-CMD ["cassandra", "-f"]
diff --git a/examples/todo-app/cassandra/startup.sh b/examples/todo-app/cassandra/startup.sh
deleted file mode 100644
index 0cb1765d..00000000
--- a/examples/todo-app/cassandra/startup.sh
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/bin/bash
-#
-# Copyright (c) 2017, 2024 Oracle and/or its affiliates.
-#
-# 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.
-#
-set -e
-
-echo 'Starting Cassandra database'
-/docker-entrypoint.sh "$@" > /var/log/cassandra.log &
-
-echo 'Waiting for database to become available'
-COUNT='1'
-while test $COUNT -lt '120' && ! timeout 1 bash -c 'cat < /dev/null > /dev/tcp/127.0.0.1/9042' > /dev/null 2>&1 ; do
- if [ "$((COUNT%10))" -eq '0' ]; then
- echo " ...$COUNT s"
- fi
- sleep 1
- COUNT=$((COUNT+1))
-done
-
-echo 'Creating todos table'
-cqlsh -e "
- CREATE KEYSPACE backend WITH REPLICATION = {'class' : 'SimpleStrategy', 'replication_factor' : 1};
- CREATE TABLE backend.backend (id ascii, user ascii, message ascii, completed Boolean, created timestamp, PRIMARY KEY (id));
- select * from backend.backend;
-" \
- || true
-
-echo 'Opening database log file'
-echo '-------------------------'
-tail -f /var/log/cassandra.log
diff --git a/examples/todo-app/frontend/pom.xml b/examples/todo-app/frontend/pom.xml
deleted file mode 100644
index 167fee39..00000000
--- a/examples/todo-app/frontend/pom.xml
+++ /dev/null
@@ -1,159 +0,0 @@
-
-
-
-
- 4.0.0
-
- io.helidon.applications
- helidon-se
- 2.6.8-SNAPSHOT
-
-
- io.helidon.examples.todo
- helidon-examples-todo-frontend
- 1.0.0-SNAPSHOT
- Helidon Examples TODO Demo Frontend
-
-
- Front-end part of the application, uses Helidon SE
-
-
-
- io.helidon.demo.todos.frontend.Main
- ${mainClass}
-
-
-
-
- io.helidon.common
- helidon-common
-
-
- io.helidon.webserver
- helidon-webserver
-
-
- io.helidon.webserver
- helidon-webserver-static-content
-
-
- io.helidon.webclient
- helidon-webclient
-
-
- io.helidon.webclient
- helidon-webclient-security
-
-
- io.helidon.webclient
- helidon-webclient-tracing
-
-
- io.helidon.webserver
- helidon-webserver-access-log
-
-
- io.helidon.media
- helidon-media-jsonp
-
-
- io.helidon.tracing
- helidon-tracing
-
-
- io.helidon.tracing
- helidon-tracing-zipkin
-
-
- io.helidon.config
- helidon-config
-
-
- io.helidon.config
- helidon-config-yaml
-
-
- io.helidon.config
- helidon-config-encryption
-
-
- io.helidon.security
- helidon-security
-
-
- io.helidon.security.providers
- helidon-security-providers-google-login
-
-
- io.helidon.security.providers
- helidon-security-providers-abac
-
-
- io.helidon.security.providers
- helidon-security-providers-http-sign
-
-
- io.helidon.security.integration
- helidon-security-integration-webserver
-
-
- io.helidon.metrics
- helidon-metrics-api
-
-
- io.helidon.metrics
- helidon-metrics-service-api
-
-
- io.helidon.metrics
- helidon-metrics
-
-
- io.helidon.security.providers
- helidon-security-providers-http-auth
- test
-
-
- org.junit.jupiter
- junit-jupiter-api
- test
-
-
- org.hamcrest
- hamcrest-all
- test
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-dependency-plugin
-
-
- copy-libs
-
-
-
-
-
-
diff --git a/examples/todo-app/frontend/src/main/java/io/helidon/demo/todos/frontend/BackendServiceClient.java b/examples/todo-app/frontend/src/main/java/io/helidon/demo/todos/frontend/BackendServiceClient.java
deleted file mode 100644
index c39cea7c..00000000
--- a/examples/todo-app/frontend/src/main/java/io/helidon/demo/todos/frontend/BackendServiceClient.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (c) 2017, 2024 Oracle and/or its affiliates.
- *
- * 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.helidon.demo.todos.frontend;
-
-import java.util.function.Function;
-
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-
-import io.helidon.common.http.Http.ResponseStatus.Family;
-import io.helidon.common.reactive.Single;
-import io.helidon.config.Config;
-import io.helidon.media.jsonp.JsonpSupport;
-import io.helidon.webclient.WebClient;
-import io.helidon.webclient.WebClientResponse;
-import io.helidon.webclient.security.WebClientSecurity;
-import io.helidon.webclient.tracing.WebClientTracing;
-import io.helidon.webserver.HttpException;
-
-/**
- * Client to invoke the backend service.
- */
-final class BackendServiceClient {
-
- private final WebClient client;
-
- BackendServiceClient(Config config) {
- String serviceEndpoint = config.get("services.backend.endpoint").asString().get();
- this.client = WebClient.builder()
- .useSystemServiceLoader(false)
- .addService(WebClientTracing.create())
- .addService(WebClientSecurity.create())
- .addMediaSupport(JsonpSupport.create())
- .baseUri(serviceEndpoint + "/api/backend").build();
- }
-
- /**
- * Retrieve all entries from the backend.
- *
- * @return single with all records
- */
- Single list() {
- return client.get()
- .request()
- .flatMapSingle(processResponse(JsonArray.class));
- }
-
- /**
- * Retrieve the entry identified by the given ID.
- *
- * @param id the ID identifying the entry to retrieve
- * @return retrieved entry as a {@code JsonObject}
- */
- Single get(String id) {
- return client.get()
- .path(id)
- .request()
- .flatMapSingle(processResponse(JsonObject.class));
- }
-
- /**
- * Delete the entry identified by the given ID.
- *
- * @param id the ID identifying the entry to delete
- * @return deleted entry as a {@code JsonObject}
- */
- Single deleteSingle(String id) {
- return client.delete()
- .path(id)
- .request()
- .flatMapSingle(processResponse(JsonObject.class));
- }
-
- /**
- * Create a new entry.
- *
- * @param json the new entry value to create as {@code JsonObject}
- * @return created entry as {@code JsonObject}
- */
- Single create(JsonObject json) {
- return client.post()
- .submit(json)
- .flatMapSingle(processResponse(JsonObject.class));
- }
-
- /**
- * Update an entry identified by the given ID.
- *
- * @param id the ID identifying the entry to update
- * @param json the update entry value as {@code JsonObject}
- * @return updated entry as {@code JsonObject}
- */
- Single update(String id, JsonObject json) {
- return client.put()
- .path(id)
- .submit(json)
- .flatMapSingle(processResponse(JsonObject.class));
- }
-
- private Function> processResponse(Class clazz) {
- return response -> {
- if (response.status().family() != Family.SUCCESSFUL) {
- return Single.error(new HttpException("backend error", response.status()));
- }
- return response.content().as(clazz);
- };
- }
-}
diff --git a/examples/todo-app/frontend/src/main/java/io/helidon/demo/todos/frontend/Main.java b/examples/todo-app/frontend/src/main/java/io/helidon/demo/todos/frontend/Main.java
deleted file mode 100644
index 534e5798..00000000
--- a/examples/todo-app/frontend/src/main/java/io/helidon/demo/todos/frontend/Main.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (c) 2017, 2024 Oracle and/or its affiliates.
- *
- * 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.helidon.demo.todos.frontend;
-
-import java.time.Duration;
-import java.util.List;
-
-import io.helidon.common.LogConfig;
-import io.helidon.common.http.Http;
-import io.helidon.config.Config;
-import io.helidon.config.FileSystemWatcher;
-import io.helidon.media.jsonp.JsonpSupport;
-import io.helidon.metrics.serviceapi.MetricsSupport;
-import io.helidon.security.Security;
-import io.helidon.security.integration.webserver.WebSecurity;
-import io.helidon.tracing.TracerBuilder;
-import io.helidon.webserver.Routing;
-import io.helidon.webserver.WebServer;
-import io.helidon.webserver.accesslog.AccessLogSupport;
-import io.helidon.webserver.staticcontent.StaticContentSupport;
-
-import io.opentracing.Tracer;
-
-import static io.helidon.config.ConfigSources.classpath;
-import static io.helidon.config.ConfigSources.environmentVariables;
-import static io.helidon.config.ConfigSources.file;
-import static io.helidon.config.PollingStrategies.regular;
-
-/**
- * Main class to start the service.
- */
-public final class Main {
-
- /**
- * Interval for config polling.
- */
- private static final Long POLLING_INTERVAL = 5L;
-
- /**
- * Cannot be instantiated.
- */
- private Main() {
- }
-
- /**
- * Application main entry point.
- *
- * @param args command line arguments
- */
- public static void main(final String[] args) {
-
- // load logging configuration
- LogConfig.configureRuntime();
-
- Config config = buildConfig();
-
- Security security = Security.create(config.get("security"));
-
- // create a web server
- WebServer server = WebServer.builder()
- .routing(createRouting(security, config))
- .config(config.get("webserver"))
- .addMediaSupport(JsonpSupport.create())
- .tracer(registerTracer(config))
- .build();
-
- // start the web server
- server.start().whenComplete(Main::started);
- }
-
- /**
- * Create a {@code Tracer} instance using the given {@code Config}.
- * @param config the configuration root
- * @return the created {@code Tracer}
- */
- private static Tracer registerTracer(Config config) {
- return TracerBuilder.create(config.get("tracing")).build();
- }
-
- /**
- * Create the web server routing and register all handlers.
- * @param security the security features
- * @param config the configuration root
- * @return the created {@code Routing}
- */
- private static Routing createRouting(Security security, Config config) {
- return Routing.builder()
- .register(AccessLogSupport.create())
- // register metrics features (on "/metrics")
- .register(MetricsSupport.create())
- // register security features
- .register(WebSecurity.create(security, config.get("security")))
- // redirect POST / to GET /
- .post("/", (req, res) -> {
- res.addHeader(Http.Header.LOCATION, "/");
- res.status(Http.Status.SEE_OTHER_303);
- res.send();
- })
- // register static content support (on "/")
- .register(StaticContentSupport.builder("/WEB").welcomeFileName("index.html"))
- // register API handler (on "/api") - this path is secured (see application.yaml)
- .register("/api", new TodoService(new BackendServiceClient(config)))
- .build();
- }
-
- /**
- * Handle web server started event: if successful print server started
- * message in the console with the corresponding URL, otherwise print an
- * error message and exit the application.
- * @param webServer the {@code WebServer} instance
- * @param throwable if non {@code null}, indicate a server startup error
- */
- private static void started(WebServer webServer, Throwable throwable) {
- if (throwable == null) {
- System.out.println("WEB server is up! http://localhost:" + webServer.port());
- } else {
- throwable.printStackTrace(System.out);
- System.exit(1);
- }
- }
-
- /**
- * Load the configuration from all sources.
- * @return the configuration root
- */
- private static Config buildConfig() {
- return Config.builder()
- .sources(List.of(
- environmentVariables(),
- // expected on development machine
- // to override props for dev
- file("dev.yaml")
- .changeWatcher(FileSystemWatcher.create())
- .optional(),
- // expected in k8s runtime
- // to configure testing/production values
- file("prod.yaml")
- .pollingStrategy(regular(Duration.ofSeconds(POLLING_INTERVAL)))
- .optional(),
- // in jar file
- // (see src/main/resources/application.yaml)
- classpath("application.yaml")))
- .build();
- }
-}
diff --git a/examples/todo-app/frontend/src/main/java/io/helidon/demo/todos/frontend/TodoService.java b/examples/todo-app/frontend/src/main/java/io/helidon/demo/todos/frontend/TodoService.java
deleted file mode 100644
index 26542bd4..00000000
--- a/examples/todo-app/frontend/src/main/java/io/helidon/demo/todos/frontend/TodoService.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Copyright (c) 2018, 2024 Oracle and/or its affiliates.
- *
- * 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.helidon.demo.todos.frontend;
-
-import javax.json.JsonObject;
-
-import io.helidon.common.http.Http;
-import io.helidon.metrics.api.RegistryFactory;
-import io.helidon.webserver.Routing;
-import io.helidon.webserver.ServerRequest;
-import io.helidon.webserver.ServerResponse;
-import io.helidon.webserver.Service;
-
-import org.eclipse.microprofile.metrics.Counter;
-import org.eclipse.microprofile.metrics.Metadata;
-import org.eclipse.microprofile.metrics.MetricRegistry;
-import org.eclipse.microprofile.metrics.MetricType;
-import org.eclipse.microprofile.metrics.MetricUnits;
-
-/**
- * TODO service.
- *
- * An entry is structured as follows:
- * { 'title': string, 'completed': boolean, 'id': string }
- *
- * The IDs are server generated on the initial POST operation (so they are not
- * included in that case).
- *
- * Here is a summary of the operations:
- * GET /api/todo: Get all entries
- * GET /api/todo/{id}: Get an entry by ID
- * POST /api/todo: Create a new entry, created entry is returned
- * DELETE /api/todo/{id}: Delete an entry, deleted entry is returned
- * PUT /api/todo/{id}: Update an entry, updated entry is returned
- */
-public final class TodoService implements Service {
-
- private final BackendServiceClient bsc;
- private final Counter createCounter;
- private final Counter updateCounter;
- private final Counter deleteCounter;
-
- /**
- * Create a new {@code TodosHandler} instance.
- *
- * @param bsc the {@code BackendServiceClient} to use
- */
- TodoService(BackendServiceClient bsc) {
- MetricRegistry registry = RegistryFactory.getInstance().getRegistry(MetricRegistry.Type.APPLICATION);
- this.bsc = bsc;
- this.createCounter = registry.counter("created");
- this.updateCounter = registry.counter("updates");
- this.deleteCounter = registry.counter(Metadata.builder()
- .withName("deletes")
- .withDisplayName("deletes")
- .withDescription("Number of deleted todos")
- .withType(MetricType.COUNTER)
- .withUnit(MetricUnits.NONE)
- .build());
- }
-
- @Override
- public void update(Routing.Rules rules) {
- rules.get("/todo/{id}", this::get)
- .delete("/todo/{id}", this::delete)
- .put("/todo/{id}", this::update)
- .get("/todo", this::list)
- .post("/todo", this::create);
- }
-
- /**
- * Handler for {@code POST /todo}.
- *
- * @param req the server request
- * @param res the server response
- */
- private void create(ServerRequest req, ServerResponse res) {
- req.content()
- .as(JsonObject.class)
- .flatMapSingle(bsc::create)
- .peek(ignored -> createCounter.inc())
- .onError(res::send)
- .forSingle(json -> {
- res.status(Http.Status.CREATED_201);
- res.send(json);
- });
- }
-
- /**
- * Handler for {@code GET /todo}.
- *
- * @param req the server request
- * @param res the server response
- */
- private void list(ServerRequest req, ServerResponse res) {
- bsc.list()
- .onError(res::send)
- .forSingle(res::send);
- }
-
- /**
- * Handler for {@code PUT /todo/id}.
- *
- * @param req the server request
- * @param res the server response
- */
- private void update(ServerRequest req, ServerResponse res) {
- req.content()
- .as(JsonObject.class)
- .flatMapSingle(json -> bsc.update(req.path().param("id"), json))
- .peek(ignored -> updateCounter.inc())
- .onError(res::send)
- .forSingle(res::send);
- }
-
- /**
- * Handler for {@code DELETE /todo/id}.
- *
- * @param req the server request
- * @param res the server response
- */
- private void delete(ServerRequest req, ServerResponse res) {
- bsc.deleteSingle(req.path().param("id"))
- .peek(ignored -> deleteCounter.inc())
- .onError(res::send)
- .forSingle(res::send);
- }
-
- /**
- * Handler for {@code GET /todo/id}.
- *
- * @param req the server request
- * @param res the server response
- */
- private void get(ServerRequest req, ServerResponse res) {
- bsc.get(req.path().param("id"))
- .onError(res::send)
- .forSingle(res::send);
- }
-}
diff --git a/examples/todo-app/frontend/src/main/java/io/helidon/demo/todos/frontend/package-info.java b/examples/todo-app/frontend/src/main/java/io/helidon/demo/todos/frontend/package-info.java
deleted file mode 100644
index 94289600..00000000
--- a/examples/todo-app/frontend/src/main/java/io/helidon/demo/todos/frontend/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright (c) 2018, 2024 Oracle and/or its affiliates.
- *
- * 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.
- */
-
-/**
- * TODOs Demo application frontend.
- */
-package io.helidon.demo.todos.frontend;
diff --git a/examples/todo-app/frontend/src/main/resources/WEB/css/styles.css b/examples/todo-app/frontend/src/main/resources/WEB/css/styles.css
deleted file mode 100644
index 7c36bb76..00000000
--- a/examples/todo-app/frontend/src/main/resources/WEB/css/styles.css
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * Copyright (c) 2023, 2024 Oracle and/or its affiliates.
- *
- * 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.
- */
-
-html,
-body {
- margin: 0 auto;
- padding: 0;
- font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
- line-height: 1.4em;
- background: #f5f5f5;
- color: #4d4d4d;
- min-width: 600px;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
- font-weight: 300;
-}
-
-input {
- font-family: inherit;
- font-weight: inherit;
- color: inherit;
- border: 0;
-}
-
-button {
- margin: 0;
- padding: 0;
- border: 0;
- background: none;
- font-size: 100%;
- vertical-align: baseline;
- font-family: inherit;
- font-weight: inherit;
- color: inherit;
- cursor: pointer;
- -webkit-appearance: none;
- appearance: none;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-:focus {
- outline: 0;
-}
-
-.hidden {
- display: none;
-}
-
-nav {
- display: flex;
- align-items: center;
- padding: 0 20px 0 20px;
- height: 80px;
-}
-
-.spacer {
- flex-grow: 1;
-}
-
-.row {
- display: flex;
- flex-direction: row;
- align-items: center;
-}
-
-.column {
- display: flex;
- flex-direction: column;
- align-items: center;
-}
-
-.wrap {
- display: none;
-}
-
-.todoapp {
- width: 550px;
- background: #fff;
- margin: 130px 0 40px 0;
- position: relative;
- box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2),
- 0 25px 50px 0 rgba(0, 0, 0, 0.1);
-}
-
-.todoapp input::-webkit-input-placeholder {
- font-style: italic;
- font-weight: 300;
- color: #e6e6e6;
-}
-
-.todoapp input::-moz-placeholder {
- font-style: italic;
- font-weight: 300;
- color: #e6e6e6;
-}
-
-.todoapp h1 {
- position: absolute;
- top: -155px;
- width: 100%;
- font-size: 100px;
- font-weight: 100;
- text-align: center;
- color: rgba(175, 47, 47, 0.15);
- text-rendering: optimizeLegibility;
-}
-
-.new-todo {
- position: relative;
- margin: 0;
- width: 100%;
- font-size: 24px;
- font-family: inherit;
- font-weight: inherit;
- line-height: 1.4em;
- color: inherit;
- padding: 6px;
- border: 1px solid #999;
- box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
- box-sizing: border-box;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-.new-todo {
- padding: 16px 16px 16px 60px;
- border: none;
- background: rgba(0, 0, 0, 0.003);
- box-shadow: inset 0 -2px 1px rgba(0, 0, 0, 0.03);
-}
-
-.main {
- position: relative;
- z-index: 2;
- border-top: 1px solid #e6e6e6;
-}
-
-.checkbox {
- border: none; /* Mobile Safari */
- appearance: none;
- opacity: 0.25;
- -webkit-appearance: none;
- margin: 10px;
-}
-
-.checkbox:after {
- font-family: 'Material Symbols Outlined';
- font-size: 25px;
- transition: opacity 0.2s ease-out;
- cursor: pointer;
-}
-
-.checkbox:hover {
- opacity: 0.5;
-}
-
-.checkbox:active {
- opacity: 0.75;
-}
-
-.toggle-all:after {
- content: "\e877";
-}
-
-.todo-list {
- margin: 0;
- padding: 0;
- list-style: none;
-}
-
-.todo-list li {
- position: relative;
- font-size: 24px;
- border-bottom: 1px solid #ededed;
-}
-
-.todo-list li:last-child {
- border-bottom: none;
-}
-
-.todo-list .view, .todo-list .edit {
- flex-grow: 1;
- padding: 15px 15px 15px 60px;
- line-height: 1.2;
- font-size: 24px;
-}
-
-.todo-list li.editing {
- padding: 0;
-}
-
-.todo-list li.editing .edit {
- display: block;
-}
-
-.todo-list li.editing .view {
- display: none;
-}
-
-.todo-list li .toggle {
- margin: 10px;
- display: flex;
- align-items: center;
-}
-
-.toggle:after {
- content: "\ef4a";
-}
-
-.toggle:checked:after {
- content: "\e86c";
-}
-
-.todo-list li label {
- word-break: break-all;
- padding: 15px 15px 15px 60px;
- display: block;
- line-height: 1.2;
- transition: color 0.4s;
-}
-
-.todo-list li.completed label {
- color: #d9d9d9;
- text-decoration: line-through;
-}
-
-.todo-list li .actions {
- display: flex;
- align-items: center;
- margin: 10px;
-}
-
-.todo-list li .actions button {
- margin: 5px;
- opacity: 0.25;
- font-size: 0;
- cursor: pointer;
- transition: opacity 0.2s ease-out;
-}
-
-.todo-list li .actions button:hover {
- opacity: 0.5;
-}
-
-.todo-list li .actions button:active {
- opacity: 0.75;
-}
-
-.todo-list li .actions button:before {
- font-family: 'Material Symbols Outlined';
- font-size: 25px;
-}
-
-.todo-list li .actions .update:before {
- content: '\e3c9'
-}
-
-.todo-list li.editing .actions .update:before {
- content: '\e5ca' !important;
-}
-
-.todo-list li .actions .delete:before {
- content: '\e872'
-}
-
-.todo-list li .edit {
- display: none;
-}
-
-.todo-list li.editing:last-child {
- margin-bottom: -1px;
-}
-
-.footer {
- color: #777;
- padding: 10px 15px;
- height: 20px;
- text-align: center;
- border-top: 1px solid #e6e6e6;
-}
-
-.footer:before {
- content: '';
- position: absolute;
- right: 0;
- bottom: 0;
- left: 0;
- height: 50px;
- overflow: hidden;
- box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2),
- 0 8px 0 -3px #f6f6f6,
- 0 9px 1px -3px rgba(0, 0, 0, 0.2),
- 0 16px 0 -6px #f6f6f6,
- 0 17px 2px -6px rgba(0, 0, 0, 0.2);
-}
-
-.todo-count {
- float: left;
- text-align: left;
-}
-
-.todo-count strong {
- font-weight: 300;
-}
-
-.filters {
- margin: 0;
- padding: 0;
- list-style: none;
- position: absolute;
- right: 0;
- left: 0;
-}
-
-.filters li {
- display: inline;
-}
-
-.filters li a {
- color: inherit;
- margin: 3px;
- padding: 3px 7px;
- text-decoration: none;
- border: 1px solid transparent;
- border-radius: 3px;
-}
-
-.filters li a:hover {
- border-color: rgba(175, 47, 47, 0.1);
-}
-
-.filters li a.selected {
- border-color: rgba(175, 47, 47, 0.2);
-}
-
-.clear-completed, html .clear-completed:active {
- float: right;
- position: relative;
- line-height: 20px;
- text-decoration: none;
- cursor: pointer;
-}
-
-.clear-completed:hover {
- text-decoration: underline;
-}
-
-.info {
- margin: 0;
- color: #bfbfbf;
- font-size: 14px;
- text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
- text-align: center;
-}
-
-.info p {
- line-height: 1;
-}
-
-.info a {
- color: inherit;
- text-decoration: none;
- font-weight: 400;
-}
-
-.info a:hover {
- text-decoration: underline;
-}
-
-@media screen and (-webkit-min-device-pixel-ratio: 0) {
- .toggle-all, .todo-list li .toggle {
- background: none;
- }
-
- .todo-list li .toggle {
- height: 40px;
- }
-}
-
-@media (max-width: 430px) {
- .footer {
- height: 50px;
- }
-
- .filters {
- bottom: 10px;
- }
-}
-
-#user-info {
- display:none;
-}
-
-#user-info > div {
- display:flex;
-}
-
-#user-info .sign-out {
- margin-right: 10px;
- background: white;
- padding: 5px;
- border: 1px solid #dadce0;
- border-radius: 4px;
- transition: background-color .218s, border-color .218s;
-}
-
-#user-info .sign-out:hover {
- border-color: #d2e3fc;
- background-color: rgba(66,133,244,.04);
-}
-#user-info .sign-out:active {
- background-color: rgba(66,133,244,.1);
-}
diff --git a/examples/todo-app/frontend/src/main/resources/WEB/index.html b/examples/todo-app/frontend/src/main/resources/WEB/index.html
deleted file mode 100644
index 6e45e004..00000000
--- a/examples/todo-app/frontend/src/main/resources/WEB/index.html
+++ /dev/null
@@ -1,91 +0,0 @@
-
-
-
-
-
- Helidon TodoMVC
-
-
-
-
-
-
-
-
-
-
todos
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/examples/todo-app/frontend/src/main/resources/WEB/js/app.js b/examples/todo-app/frontend/src/main/resources/WEB/js/app.js
deleted file mode 100644
index 560a075b..00000000
--- a/examples/todo-app/frontend/src/main/resources/WEB/js/app.js
+++ /dev/null
@@ -1,465 +0,0 @@
-/*
- * Copyright (c) 2018, 2024 Oracle and/or its affiliates.
- *
- * 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.
- */
-
-/* global jQuery, Router */
-
-/**
- * @typedef {Object} Handlebars
- * @property {function(string, function(*, *, HandlebarsOpts))} registerHelper
- * @property {function(HTMLElement):function(object):string} compile
- */
-
-/**
- * @typedef {Object} HandlebarsOpts
- * @property {function(object)} fn
- * @property {function(object)} inverse
- */
-
-/**
- * @typedef {Object} Google
- * @property {GoogleAccount} accounts
- */
-
-/**
- * @typedef {Object} GoogleAccount
- * @property {GoogleId} id
- */
-
-/**
- * @typedef {Object} GoogleId
- * @property {function({})} initialize
- * @property {function()} prompt
- * @property {function(Element, Object)} renderButton
- * @property {function(string} revoke
- */
-
-/**
- * @typedef {Object} Todo
- * @property {string} title
- * @property {string=} id
- * @property {boolean} completed
- */
-
-class TodoClient {
-
- constructor(access_token) {
- this.access_token = access_token
- }
-
- /**
- * List all entries.
- * @return {Promise}
- */
- list() {
- return this._ajax('GET', '/api/todo');
- }
-
- /**
- * Create a new entry.
- * @property {Todo} data
- * @return {Promise}
- */
- create(item) {
- return this._ajax('POST', '/api/todo', item);
- }
-
- /**
- * Toggle an entry.
- * @param {Todo} item
- * @param {boolean} completed
- */
- toggle(item, completed) {
- const data = {...item, completed};
- return this._ajax('PUT', `/api/todo/${item.id}`, data);
- }
-
- /**
- * Update an entry.
- * @param {Todo} item
- * @return {Promise}
- */
- update(item) {
- return this._ajax('PUT', `/api/todo/${item.id}`, item);
- }
-
- /**
- * Delete an entry.
- * @param {string} id
- * @return {Promise}
- */
- delete(id) {
- return this._ajax('DELETE', `/api/todo/${id}`);
- }
-
- /**
- * Batch requests.
- * @param {Todo[]} items
- * @param {function(Todo):boolean} filter
- * @param {function(Todo, number): Promise} fn
- * @return {Promise[]>}
- */
- batch(items, filter, fn) {
- const promises = [];
- items.forEach((e, i) => {
- if (filter(e)) {
- promises.push(fn(e, i));
- }
- })
- return Promise.all(promises);
- }
-
- /**
- * Toggle all items.
- * @property {Todo[]} items
- * @property {boolean} completed
- * @return {Promise}
- */
- toggleAll(items, completed) {
- const result = [...items];
- return this.batch(items, e => e.completed !== completed, (e, i) => {
- return this.toggle(e, completed).then(data => {
- result[i] = data;
- })
- }).then(() => result);
- }
-
- /**
- * Delete all completed items.
- * @param {Todo[]} items
- * @return {Promise}
- */
- deleteCompleted(items) {
- const indexes = [];
- const result = [...items];
- return this.batch(items, e => e.completed, (data, index) => {
- indexes.push(index);
- return this._ajax('DELETE', `/api/todo/${data.id}`);
- }).then(() => {
- indexes.sort();
- for (let i = indexes.length - 1; i >= 0; i--) {
- result.splice(indexes[i], 1);
- }
- return result;
- })
- }
-
- /**
- * @param {string} type
- * @param {string} path
- * @param {object=} data
- * @return {Promise