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