initial commit

This commit is contained in:
Johannes Loher 2018-12-18 22:18:06 +01:00
commit f460c46f4b
13 changed files with 369 additions and 0 deletions

33
.editorconfig Normal file
View 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
View 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
View 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
View 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"
}
}

View 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();
}

View 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;
}

View 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);
}

View 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);
}
}

View file

@ -0,0 +1,7 @@
module d_webservice_example.data.todo_update_do;
struct TodoUpdateDO
{
string title;
string content;
}

View file

@ -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);
}
}

View 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);
}

View 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;
}

View 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;
}