initial commit
This commit is contained in:
commit
f460c46f4b
13 changed files with 369 additions and 0 deletions
33
.editorconfig
Normal file
33
.editorconfig
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.d]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
|
max_line_length = 120
|
||||||
|
dfmt_brace_style = allman
|
||||||
|
dfmt_soft_max_line_length = 80
|
||||||
|
dfmt_align_switch_statements = true
|
||||||
|
dfmt_outdent_attributes = true
|
||||||
|
dfmt_split_operator_at_line_end = false
|
||||||
|
dfmt_space_after_cast = true
|
||||||
|
dfmt_space_after_keywords = true
|
||||||
|
dfmt_space_before_function_parameters = false
|
||||||
|
dfmt_selective_import_space = true
|
||||||
|
dfmt_compact_labeled_statements = true
|
||||||
|
dfmt_template_constraint_style = conditional_newline_indent
|
||||||
|
dfmt_single_template_constraint_indent = false
|
||||||
|
|
||||||
|
[*.sdl]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
|
|
||||||
|
[*.json]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
.dub
|
||||||
|
docs.json
|
||||||
|
__dummy.html
|
||||||
|
docs/
|
||||||
|
d-webservice-example
|
||||||
|
d-webservice-example.so
|
||||||
|
d-webservice-example.dylib
|
||||||
|
d-webservice-example.dll
|
||||||
|
d-webservice-example.a
|
||||||
|
d-webservice-example.lib
|
||||||
|
d-webservice-example-test-*
|
||||||
|
*.exe
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.lst
|
13
dub.sdl
Normal file
13
dub.sdl
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
name "d-webservice-example"
|
||||||
|
description "An example webservice application in D."
|
||||||
|
authors "Johannes Loher"
|
||||||
|
copyright "Copyright © 2018, Johannes Loher"
|
||||||
|
license "MIT"
|
||||||
|
dependency "aedi" version="~>1.0.0"
|
||||||
|
dependency "fluent-asserts" version="~>0.12.3"
|
||||||
|
dependency "vibe-d" version="~>0.8.4"
|
||||||
|
|
||||||
|
configuration "executable" {
|
||||||
|
targetType "executable"
|
||||||
|
mainSourceFile "source/d_webservice_example/application.d"
|
||||||
|
}
|
22
dub.selections.json
Normal file
22
dub.selections.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"fileVersion": 1,
|
||||||
|
"versions": {
|
||||||
|
"aedi": "1.0.0",
|
||||||
|
"botan": "1.12.10",
|
||||||
|
"botan-math": "1.0.3",
|
||||||
|
"ddmp": "0.0.1-0.dev.3",
|
||||||
|
"diet-ng": "1.5.0",
|
||||||
|
"eventcore": "0.8.39",
|
||||||
|
"fluent-asserts": "0.12.3",
|
||||||
|
"libasync": "0.8.3",
|
||||||
|
"libdparse": "0.8.8",
|
||||||
|
"libevent": "2.0.2+2.0.16",
|
||||||
|
"memutils": "0.4.13",
|
||||||
|
"mir-linux-kernel": "1.0.1",
|
||||||
|
"openssl": "1.1.6+1.0.1g",
|
||||||
|
"stdx-allocator": "2.77.4",
|
||||||
|
"taggedalgebraic": "0.10.12",
|
||||||
|
"vibe-core": "1.4.5",
|
||||||
|
"vibe-d": "0.8.4"
|
||||||
|
}
|
||||||
|
}
|
26
source/d_webservice_example/application.d
Normal file
26
source/d_webservice_example/application.d
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
module d_webservice_example.application;
|
||||||
|
|
||||||
|
import aermicioi.aedi : locate, singleton;
|
||||||
|
import vibe.vibe;
|
||||||
|
|
||||||
|
void main() @safe
|
||||||
|
{
|
||||||
|
import d_webservice_example.component_registration : registerComponents;
|
||||||
|
import d_webservice_example.controller.todo_controller : TodoController;
|
||||||
|
|
||||||
|
auto container = singleton();
|
||||||
|
scope (exit)
|
||||||
|
container.terminate;
|
||||||
|
|
||||||
|
container.registerComponents();
|
||||||
|
container.instantiate;
|
||||||
|
|
||||||
|
auto router = new URLRouter;
|
||||||
|
router.registerRestInterface(container.locate!TodoController);
|
||||||
|
auto settings = new HTTPServerSettings;
|
||||||
|
settings.port = 8080;
|
||||||
|
settings.bindAddresses = ["::", "0.0.0.0"];
|
||||||
|
listenHTTP(settings, router);
|
||||||
|
|
||||||
|
runApplication();
|
||||||
|
}
|
68
source/d_webservice_example/business/todo_service.d
Normal file
68
source/d_webservice_example/business/todo_service.d
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
module d_webservice_example.business.todo_service;
|
||||||
|
|
||||||
|
import aermicioi.aedi : autowired, component;
|
||||||
|
import d_webservice_example.model.todo : Todo;
|
||||||
|
import std.uuid : UUID, randomUUID;
|
||||||
|
|
||||||
|
@component class TodoService
|
||||||
|
{
|
||||||
|
import d_webservice_example.data.todo_update_do : TodoUpdateDO;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TodoRepository todoRepository;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
@autowired this(TodoRepository todoRepository) @safe
|
||||||
|
{
|
||||||
|
this.todoRepository = todoRepository;
|
||||||
|
}
|
||||||
|
|
||||||
|
Todo createTodo(Todo newTodo) @safe
|
||||||
|
{
|
||||||
|
immutable todo = Todo(newTodo.title, newTodo.content, randomUUID());
|
||||||
|
return todoRepository.save(todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
Todo[] getAllTodos() @safe
|
||||||
|
{
|
||||||
|
return todoRepository.findAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
Todo getTodoByUuid(const UUID uuid) @safe
|
||||||
|
{
|
||||||
|
import std.exception : enforce;
|
||||||
|
import vibe.http.common : HTTPStatus, HTTPStatusException;
|
||||||
|
|
||||||
|
immutable maybeTodo = todoRepository.findByUuid(uuid);
|
||||||
|
enforce(!maybeTodo.isNull, new HTTPStatusException(HTTPStatus.NotFound));
|
||||||
|
return maybeTodo.get;
|
||||||
|
}
|
||||||
|
|
||||||
|
Todo updateTodo(UUID uuid, const TodoUpdateDO todoUpdate) @safe
|
||||||
|
{
|
||||||
|
auto todo = getTodoByUuid(uuid);
|
||||||
|
if (todoUpdate.title != null)
|
||||||
|
todo.title = todoUpdate.title;
|
||||||
|
if (todoUpdate.content != null)
|
||||||
|
todo.content = todoUpdate.content;
|
||||||
|
|
||||||
|
return todoRepository.save(todo);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deleteTodo(UUID uuid) @safe
|
||||||
|
{
|
||||||
|
immutable todo = getTodoByUuid(uuid);
|
||||||
|
todoRepository.remove(todo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TodoRepository
|
||||||
|
{
|
||||||
|
import std.typecons : Nullable;
|
||||||
|
|
||||||
|
Todo save(Todo todo) @safe;
|
||||||
|
bool remove(Todo todo) @safe;
|
||||||
|
Nullable!Todo findByUuid(UUID uuid) @safe;
|
||||||
|
Todo[] findAll() @safe;
|
||||||
|
}
|
10
source/d_webservice_example/component_registration.d
Normal file
10
source/d_webservice_example/component_registration.d
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module d_webservice_example.component_registration;
|
||||||
|
|
||||||
|
import aermicioi.aedi : ConfigurableContainer, scan;
|
||||||
|
|
||||||
|
void registerComponents(ConfigurableContainer container) @safe
|
||||||
|
{
|
||||||
|
container.scan!(d_webservice_example.business.todo_service);
|
||||||
|
container.scan!(d_webservice_example.controller.todo_controller);
|
||||||
|
container.scan!(d_webservice_example.dataaccess.in_memory_todo_repository);
|
||||||
|
}
|
81
source/d_webservice_example/controller/todo_controller.d
Normal file
81
source/d_webservice_example/controller/todo_controller.d
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
module d_webservice_example.controller.todo_controller;
|
||||||
|
|
||||||
|
import aermicioi.aedi : autowired, component;
|
||||||
|
import d_webservice_example.transport.todo_to : TodoTO;
|
||||||
|
import std.typecons : Nullable;
|
||||||
|
import std.uuid : UUID;
|
||||||
|
import vibe.web.rest : path;
|
||||||
|
|
||||||
|
@path("/api/v1/todos")
|
||||||
|
interface TodoApi
|
||||||
|
{
|
||||||
|
@path("/")
|
||||||
|
TodoTO addTodo(string title, string content) @safe;
|
||||||
|
|
||||||
|
@path("/")
|
||||||
|
TodoTO[] getTodos() @safe;
|
||||||
|
|
||||||
|
@path("/:uuid")
|
||||||
|
TodoTO getTodo(UUID _uuid) @safe;
|
||||||
|
|
||||||
|
@path("/:uuid")
|
||||||
|
TodoTO updateTodo(UUID _uuid, Nullable!string title, Nullable!string content) @safe;
|
||||||
|
|
||||||
|
@path("/:uuid")
|
||||||
|
void deleteTodo(UUID _uuid) @safe;
|
||||||
|
}
|
||||||
|
|
||||||
|
@component class TodoController : TodoApi
|
||||||
|
{
|
||||||
|
import d_webservice_example.business.todo_service : TodoService;
|
||||||
|
import d_webservice_example.mapper.todo_mapper : asTodoTO;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TodoService todoService;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
@autowired this(TodoService todoService) @safe
|
||||||
|
{
|
||||||
|
this.todoService = todoService;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: validation
|
||||||
|
override TodoTO addTodo(string title, string content) @safe
|
||||||
|
{
|
||||||
|
import d_webservice_example.model.todo : Todo;
|
||||||
|
|
||||||
|
return todoService.createTodo(Todo(title, content)).asTodoTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
override TodoTO[] getTodos() @safe
|
||||||
|
{
|
||||||
|
import std.algorithm.iteration : map;
|
||||||
|
import std.array : array;
|
||||||
|
|
||||||
|
return todoService.getAllTodos().map!asTodoTO.array;
|
||||||
|
}
|
||||||
|
|
||||||
|
override TodoTO getTodo(UUID _uuid) @safe
|
||||||
|
{
|
||||||
|
return todoService.getTodoByUuid(_uuid).asTodoTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
override TodoTO updateTodo(UUID _uuid, Nullable!string title, Nullable!string content) @safe
|
||||||
|
{
|
||||||
|
import d_webservice_example.data.todo_update_do : TodoUpdateDO;
|
||||||
|
|
||||||
|
TodoUpdateDO update;
|
||||||
|
if (!title.isNull)
|
||||||
|
update.title = title.get;
|
||||||
|
if (!content.isNull)
|
||||||
|
update.content = content.get;
|
||||||
|
|
||||||
|
return todoService.updateTodo(_uuid, update).asTodoTO;
|
||||||
|
}
|
||||||
|
|
||||||
|
override void deleteTodo(UUID _uuid) @safe
|
||||||
|
{
|
||||||
|
todoService.deleteTodo(_uuid);
|
||||||
|
}
|
||||||
|
}
|
7
source/d_webservice_example/data/todo_update_do.d
Normal file
7
source/d_webservice_example/data/todo_update_do.d
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
module d_webservice_example.data.todo_update_do;
|
||||||
|
|
||||||
|
struct TodoUpdateDO
|
||||||
|
{
|
||||||
|
string title;
|
||||||
|
string content;
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
module d_webservice_example.dataaccess.in_memory_todo_repository;
|
||||||
|
|
||||||
|
import aermicioi.aedi : component, qualifier;
|
||||||
|
import d_webservice_example.business.todo_service : TodoRepository;
|
||||||
|
|
||||||
|
@component @qualifier!TodoRepository() class InMemoryTodoRepository : TodoRepository
|
||||||
|
{
|
||||||
|
import d_webservice_example.model.todo : Todo;
|
||||||
|
import std.typecons : Nullable, nullable;
|
||||||
|
import std.uuid : randomUUID, UUID;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Todo[string] todos;
|
||||||
|
|
||||||
|
public:
|
||||||
|
override Todo save(Todo todo)
|
||||||
|
{
|
||||||
|
todo.id = randomUUID.toString;
|
||||||
|
todos[todo.id] = todo;
|
||||||
|
return todo;
|
||||||
|
}
|
||||||
|
|
||||||
|
override Todo[] findAll()
|
||||||
|
{
|
||||||
|
import std.array : array;
|
||||||
|
|
||||||
|
return todos.byValue.array;
|
||||||
|
}
|
||||||
|
|
||||||
|
override Nullable!Todo findByUuid(UUID uuid)
|
||||||
|
{
|
||||||
|
import std.algorithm.searching : find;
|
||||||
|
|
||||||
|
auto foundTodos = todos.byValue.find!(todo => todo.uuid == uuid);
|
||||||
|
if (foundTodos.empty)
|
||||||
|
return Nullable!Todo.init;
|
||||||
|
else
|
||||||
|
return nullable(foundTodos.front);
|
||||||
|
}
|
||||||
|
|
||||||
|
override bool remove(Todo todo)
|
||||||
|
{
|
||||||
|
return todos.remove(todo.id);
|
||||||
|
}
|
||||||
|
}
|
10
source/d_webservice_example/mapper/todo_mapper.d
Normal file
10
source/d_webservice_example/mapper/todo_mapper.d
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module d_webservice_example.mapper.todo_mapper;
|
||||||
|
|
||||||
|
import d_webservice_example.model.todo : Todo;
|
||||||
|
import d_webservice_example.transport.todo_to : TodoTO;
|
||||||
|
|
||||||
|
TodoTO asTodoTO(Todo todo) nothrow pure @safe @nogc
|
||||||
|
{
|
||||||
|
|
||||||
|
return TodoTO(todo.uuid, todo.title, todo.content);
|
||||||
|
}
|
28
source/d_webservice_example/model/todo.d
Normal file
28
source/d_webservice_example/model/todo.d
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
module d_webservice_example.model.todo;
|
||||||
|
|
||||||
|
struct Todo
|
||||||
|
{
|
||||||
|
import std.uuid : UUID;
|
||||||
|
import vibe.data.serialization : name;
|
||||||
|
|
||||||
|
this(string title, string content) nothrow pure @safe @nogc
|
||||||
|
{
|
||||||
|
this.title = title;
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
this(string title, string content, UUID uuid) nothrow pure @safe @nogc
|
||||||
|
{
|
||||||
|
this.title = title;
|
||||||
|
this.content = content;
|
||||||
|
this.uuid = uuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
string title;
|
||||||
|
string content;
|
||||||
|
|
||||||
|
UUID uuid;
|
||||||
|
|
||||||
|
@name("_id")
|
||||||
|
string id;
|
||||||
|
}
|
11
source/d_webservice_example/transport/todo_to.d
Normal file
11
source/d_webservice_example/transport/todo_to.d
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
module d_webservice_example.transport.todo_to;
|
||||||
|
|
||||||
|
struct TodoTO
|
||||||
|
{
|
||||||
|
import std.typecons : Nullable;
|
||||||
|
import std.uuid : UUID;
|
||||||
|
|
||||||
|
UUID uuid;
|
||||||
|
string title;
|
||||||
|
string content;
|
||||||
|
}
|
Loading…
Reference in a new issue