Second commit
added few test, first api to add book
This commit is contained in:
76
api_test.go
Normal file
76
api_test.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
|
||||||
|
"git.artlef.fr/PersonalLibraryManager/internal/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testSetup() *gin.Engine {
|
||||||
|
c := config.LoadConfig("config_test/test.toml")
|
||||||
|
return setup(&c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetBooksHandler(t *testing.T) {
|
||||||
|
router := testSetup()
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
req, _ := http.NewRequest("GET", "/books", nil)
|
||||||
|
router.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, 200, w.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPostBookHandler_Ok(t *testing.T) {
|
||||||
|
bookJson :=
|
||||||
|
`{
|
||||||
|
"title": "Le château",
|
||||||
|
"author": "Kafka",
|
||||||
|
"rating": 9
|
||||||
|
}`
|
||||||
|
testPostBookHandler(t, bookJson, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPostBookHandler_OkOnlyTitle(t *testing.T) {
|
||||||
|
bookJson :=
|
||||||
|
`{
|
||||||
|
"title": "Le château"
|
||||||
|
}`
|
||||||
|
testPostBookHandler(t, bookJson, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPostBookHandler_noTitle(t *testing.T) {
|
||||||
|
bookJson :=
|
||||||
|
`{
|
||||||
|
"author": "Kafka",
|
||||||
|
"rating": 9
|
||||||
|
}`
|
||||||
|
testPostBookHandler(t, bookJson, 400)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPostBookHandler_WrongRating(t *testing.T) {
|
||||||
|
bookJson :=
|
||||||
|
`{
|
||||||
|
"title": "Le château",
|
||||||
|
"author": "Kafka",
|
||||||
|
"rating": 15
|
||||||
|
}`
|
||||||
|
testPostBookHandler(t, bookJson, 400)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPostBookHandler(t *testing.T, bookJson string, expectedCode int) {
|
||||||
|
router := testSetup()
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
|
||||||
|
req, _ := http.NewRequest("POST", "/book",
|
||||||
|
strings.NewReader(string(bookJson)))
|
||||||
|
router.ServeHTTP(w, req)
|
||||||
|
|
||||||
|
assert.Equal(t, expectedCode, w.Code)
|
||||||
|
|
||||||
|
}
|
||||||
9
config_test/test.toml
Normal file
9
config_test/test.toml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
# Path to sqlite database file.
|
||||||
|
database_file_path = "file::memory:?cache=shared"
|
||||||
|
|
||||||
|
# The path to the sql file to load for demo data.
|
||||||
|
demo_data_path = ""
|
||||||
|
|
||||||
|
# The port to listen on for the server.
|
||||||
|
port = "8080"
|
||||||
24
front/package-lock.json
generated
24
front/package-lock.json
generated
@@ -8,7 +8,8 @@
|
|||||||
"name": "personal-library-manager",
|
"name": "personal-library-manager",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "^3.5.18"
|
"vue": "^3.5.18",
|
||||||
|
"vue-router": "^4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.31.0",
|
"@eslint/js": "^9.31.0",
|
||||||
@@ -1682,6 +1683,12 @@
|
|||||||
"@vue/shared": "3.5.21"
|
"@vue/shared": "3.5.21"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vue/devtools-api": {
|
||||||
|
"version": "6.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||||
|
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@vue/devtools-core": {
|
"node_modules/@vue/devtools-core": {
|
||||||
"version": "8.0.1",
|
"version": "8.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.0.1.tgz",
|
||||||
@@ -3989,6 +3996,21 @@
|
|||||||
"eslint": "^8.57.0 || ^9.0.0"
|
"eslint": "^8.57.0 || ^9.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-router": {
|
||||||
|
"version": "4.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz",
|
||||||
|
"integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.6.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/posva"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/which": {
|
"node_modules/which": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||||
|
|||||||
@@ -14,7 +14,8 @@
|
|||||||
"format": "prettier --write src/"
|
"format": "prettier --write src/"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"vue": "^3.5.18"
|
"vue": "^3.5.18",
|
||||||
|
"vue-router": "^4.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.31.0",
|
"@eslint/js": "^9.31.0",
|
||||||
|
|||||||
41
front/src/AddBook.vue
Normal file
41
front/src/AddBook.vue
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { postBook } from './api.js'
|
||||||
|
import { useRouter, useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const book = ref({
|
||||||
|
title: "",
|
||||||
|
author: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
function onSubmit(e) {
|
||||||
|
postBook(book);
|
||||||
|
router.push('/');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<form @submit.prevent="onSubmit">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Title</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" v-model="book.title" placeholder="Title">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Author</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" v-model="book.author" placeholder="Author">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<div class="control">
|
||||||
|
<button class="button is-link">Submit</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped></style>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import BooksBrowser from './BooksBrowser.vue'
|
|
||||||
import AppNavBar from './AppNavBar.vue'
|
import AppNavBar from './AppNavBar.vue'
|
||||||
|
import { RouterView } from 'vue-router'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
<AppNavBar/>
|
<AppNavBar/>
|
||||||
</header>
|
</header>
|
||||||
<main class="section">
|
<main class="section">
|
||||||
<BooksBrowser/>
|
<RouterView />
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
<script setup></script>
|
<script setup>
|
||||||
|
import { RouterLink } from 'vue-router'
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<nav class="navbar">
|
<nav class="navbar">
|
||||||
@@ -9,9 +11,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="navbar-menu">
|
<div class="navbar-menu">
|
||||||
<div class="navbar-start">
|
<div class="navbar-start">
|
||||||
<a class="navbar-item">
|
<RouterLink to="/" class="navbar-item" activeClass="is-active">
|
||||||
Home
|
Home
|
||||||
</a>
|
</RouterLink>
|
||||||
|
<RouterLink to="/add" class="navbar-item" activeClass="is-active">
|
||||||
|
Add Book
|
||||||
|
</RouterLink>
|
||||||
<a class="navbar-item">
|
<a class="navbar-item">
|
||||||
Books
|
Books
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const imagePathOrDefault = (props.imagePath == "" || typeof props.imagePath ===
|
|||||||
<div class="media-content">
|
<div class="media-content">
|
||||||
<div class="is-size-4">{{title}}</div>
|
<div class="is-size-4">{{title}}</div>
|
||||||
<div class="is-size-5 is-italic">{{author}}</div>
|
<div class="is-size-5 is-italic">{{author}}</div>
|
||||||
<p>{{rating}}/5</p>
|
<p>{{rating}}/10</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,7 +37,8 @@ img {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.box {
|
.box {
|
||||||
transition:ease-in-out 0.04s
|
transition:ease-in-out 0.04s;
|
||||||
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.box:hover {
|
.box:hover {
|
||||||
|
|||||||
@@ -1,72 +1,38 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import BookCard from './BookCard.vue';
|
import BookCard from './BookCard.vue';
|
||||||
import { useFetch } from './fetch.js'
|
import { getBooks } from './api.js'
|
||||||
|
|
||||||
const { data, error } = useFetch('http://localhost:8080/books');
|
const { data, error } = getBooks();
|
||||||
|
|
||||||
console.log(data.value);
|
|
||||||
|
|
||||||
let books = [];
|
|
||||||
const dumas = {
|
|
||||||
id: 1,
|
|
||||||
title: "Le Comte de Monte-Cristo",
|
|
||||||
author: "Alexandre Dumas",
|
|
||||||
imagePath: "./comtemontecristo.png",
|
|
||||||
rating: 4.5
|
|
||||||
};
|
|
||||||
const celine = {
|
|
||||||
id: 2,
|
|
||||||
title: "Guerre",
|
|
||||||
author: "Céline",
|
|
||||||
imagePath: "",
|
|
||||||
rating: 5
|
|
||||||
}
|
|
||||||
const proust = {
|
|
||||||
id: 3,
|
|
||||||
title: "La Prisonnière",
|
|
||||||
author: "Marcel Proust",
|
|
||||||
imagePath: "",
|
|
||||||
rating: 5
|
|
||||||
}
|
|
||||||
const proust2 = {
|
|
||||||
id: 4,
|
|
||||||
title: "Du côté de chez Swann",
|
|
||||||
author: "Marcel Proust",
|
|
||||||
imagePath: "",
|
|
||||||
rating: 5
|
|
||||||
}
|
|
||||||
const balzac = {
|
|
||||||
id: 5,
|
|
||||||
title: "Splendeurs et misères des courtisanes",
|
|
||||||
author: "Balzac",
|
|
||||||
imagePath: "",
|
|
||||||
rating: 3
|
|
||||||
}
|
|
||||||
const cervantes = {
|
|
||||||
id: 6,
|
|
||||||
title: "L’Ingénieux Hidalgo Don Quichotte de la Manche ou L'Ingénieux Noble Don Quichotte de la Manche",
|
|
||||||
author: "Cervantès",
|
|
||||||
imagePath: "",
|
|
||||||
rating: 3
|
|
||||||
}
|
|
||||||
|
|
||||||
books.push(dumas);
|
|
||||||
books.push(celine);
|
|
||||||
books.push(proust);
|
|
||||||
books.push(proust2);
|
|
||||||
books.push(balzac);
|
|
||||||
books.push(cervantes);
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="grid">
|
<div class="books">
|
||||||
|
|
||||||
<div v-if="error">Error when loading books: {{ error.message }}</div>
|
<div v-if="error">Error when loading books: {{ error.message }}</div>
|
||||||
<div class="cell" v-else-if="data" v-for="book in data" :key="book.id">
|
<div class="book" v-else-if="data" v-for="book in data" :key="book.id">
|
||||||
<BookCard v-bind="book" />
|
<BookCard v-bind="book" />
|
||||||
</div>
|
</div>
|
||||||
<div v-else>Loading...</div>
|
<div v-else>Loading...</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped></style>
|
<style scoped>
|
||||||
|
|
||||||
|
.books {
|
||||||
|
position:relative;
|
||||||
|
float:left;
|
||||||
|
width:100%; height:auto;
|
||||||
|
padding-bottom: 100px;
|
||||||
|
line-height: 2.5;
|
||||||
|
|
||||||
|
column-count: 4;
|
||||||
|
column-gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#book {
|
||||||
|
width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
transition: ease-in-out 0.15s;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
29
front/src/api.js
Normal file
29
front/src/api.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
const baseUrl = "http://localhost:8080"
|
||||||
|
|
||||||
|
function useFetch(url) {
|
||||||
|
const data = ref(null)
|
||||||
|
const error = ref(null)
|
||||||
|
|
||||||
|
fetch(url)
|
||||||
|
.then((res) => res.json())
|
||||||
|
.then((json) => (data.value = json))
|
||||||
|
.catch((err) => (error.value = err))
|
||||||
|
|
||||||
|
return { data, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBooks() {
|
||||||
|
return useFetch(baseUrl + '/books');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postBook(book) {
|
||||||
|
fetch(baseUrl + '/book', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify(book.value)
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import { ref } from 'vue'
|
|
||||||
|
|
||||||
export function useFetch(url) {
|
|
||||||
const data = ref(null)
|
|
||||||
const error = ref(null)
|
|
||||||
|
|
||||||
fetch(url)
|
|
||||||
.then((res) => res.json())
|
|
||||||
.then((json) => (data.value = json))
|
|
||||||
.catch((err) => (error.value = err))
|
|
||||||
|
|
||||||
return { data, error }
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,18 @@
|
|||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import BooksBrowser from './BooksBrowser.vue'
|
||||||
|
import AddBook from './AddBook.vue'
|
||||||
|
|
||||||
createApp(App).mount('#app')
|
|
||||||
|
const routes = [
|
||||||
|
{ path: '/', component: BooksBrowser },
|
||||||
|
{ path: '/add', component: AddBook },
|
||||||
|
]
|
||||||
|
|
||||||
|
export const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes,
|
||||||
|
})
|
||||||
|
|
||||||
|
createApp(App).use(router).mount('#app')
|
||||||
|
|||||||
3
go.mod
3
go.mod
@@ -6,6 +6,7 @@ require (
|
|||||||
github.com/gin-contrib/cors v1.7.6
|
github.com/gin-contrib/cors v1.7.6
|
||||||
github.com/gin-gonic/gin v1.10.1
|
github.com/gin-gonic/gin v1.10.1
|
||||||
github.com/pelletier/go-toml v1.9.5
|
github.com/pelletier/go-toml v1.9.5
|
||||||
|
github.com/stretchr/testify v1.10.0
|
||||||
gorm.io/driver/sqlite v1.6.0
|
gorm.io/driver/sqlite v1.6.0
|
||||||
gorm.io/gorm v1.31.0
|
gorm.io/gorm v1.31.0
|
||||||
)
|
)
|
||||||
@@ -15,6 +16,7 @@ require (
|
|||||||
github.com/bytedance/sonic v1.14.1 // indirect
|
github.com/bytedance/sonic v1.14.1 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
||||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
@@ -32,6 +34,7 @@ require (
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||||
golang.org/x/arch v0.21.0 // indirect
|
golang.org/x/arch v0.21.0 // indirect
|
||||||
|
|||||||
7
internal/api/dto.go
Normal file
7
internal/api/dto.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
type bookPostCreate struct {
|
||||||
|
Title string `json:"title" binding:"required"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
Rating int `json:"rating" binding:"min=0,max=10"`
|
||||||
|
}
|
||||||
9
internal/api/mapper.go
Normal file
9
internal/api/mapper.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import "git.artlef.fr/PersonalLibraryManager/internal/model"
|
||||||
|
|
||||||
|
func (b bookPostCreate) toBook() model.Book {
|
||||||
|
return model.Book{Title: b.Title,
|
||||||
|
Author: b.Author,
|
||||||
|
Rating: b.Rating}
|
||||||
|
}
|
||||||
27
internal/api/routes.go
Normal file
27
internal/api/routes.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"git.artlef.fr/PersonalLibraryManager/internal/model"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetBooksHanderl(c *gin.Context, db *gorm.DB) {
|
||||||
|
var books []model.Book
|
||||||
|
db.Model(&model.Book{}).Find(&books)
|
||||||
|
c.JSON(http.StatusOK, books)
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostBookHandler(c *gin.Context, db *gorm.DB) {
|
||||||
|
var book bookPostCreate
|
||||||
|
err := c.ShouldBindJSON(&book)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bookDb := book.toBook()
|
||||||
|
db.Model(&model.Book{}).Save(&bookDb)
|
||||||
|
c.String(200, "Success")
|
||||||
|
}
|
||||||
@@ -10,15 +10,14 @@ import (
|
|||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Port string `toml:"port" comment:"The port to listen on for the server."`
|
Port string `toml:"port" comment:"The port to listen on for the server."`
|
||||||
|
DatabaseFilePath string `toml:"database_file_path" comment:"Path to sqlite database file."`
|
||||||
DemoDataPath string `toml:"demo_data_path" comment:"The path to the sql file to load for demo data."`
|
DemoDataPath string `toml:"demo_data_path" comment:"The path to the sql file to load for demo data."`
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultConfig() Config {
|
func defaultConfig() Config {
|
||||||
return Config{Port: "8080", DemoDataPath: ""}
|
return Config{Port: "8080", DatabaseFilePath: "plm.db", DemoDataPath: ""}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func LoadConfig(configPath string) Config {
|
func LoadConfig(configPath string) Config {
|
||||||
f, err := os.ReadFile(configPath)
|
f, err := os.ReadFile(configPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package db
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -11,12 +10,10 @@ import (
|
|||||||
"git.artlef.fr/PersonalLibraryManager/internal/model"
|
"git.artlef.fr/PersonalLibraryManager/internal/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Initdb(databaseDir string, demoDataPath string) *gorm.DB {
|
func Initdb(databasePath string, demoDataPath string) *gorm.DB {
|
||||||
createDbFolderIfMissing(databaseDir)
|
|
||||||
db, err := gorm.Open(
|
db, err := gorm.Open(
|
||||||
sqlite.Open(
|
sqlite.Open(
|
||||||
fmt.Sprintf(
|
databasePath), &gorm.Config{})
|
||||||
"%s/plm.db", databaseDir)), &gorm.Config{})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -38,20 +35,3 @@ func migrateSchema(db *gorm.DB, demoDataPath string) {
|
|||||||
}
|
}
|
||||||
db.Exec(string(data))
|
db.Exec(string(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
func createDbFolderIfMissing(databaseDir string) {
|
|
||||||
_, openFileErr := os.Open(databaseDir)
|
|
||||||
if os.IsNotExist(openFileErr) {
|
|
||||||
createNonExistingDbFolder(databaseDir)
|
|
||||||
} else if openFileErr != nil {
|
|
||||||
log.Fatal(openFileErr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createNonExistingDbFolder(databaseDir string) {
|
|
||||||
log.Printf("Creating missing folder %s\n", databaseDir)
|
|
||||||
err := os.MkdirAll(databaseDir, 0700)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
25
main.go
25
main.go
@@ -1,30 +1,29 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/gin-contrib/cors"
|
"github.com/gin-contrib/cors"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"gorm.io/gorm"
|
|
||||||
|
|
||||||
|
"git.artlef.fr/PersonalLibraryManager/internal/api"
|
||||||
"git.artlef.fr/PersonalLibraryManager/internal/config"
|
"git.artlef.fr/PersonalLibraryManager/internal/config"
|
||||||
"git.artlef.fr/PersonalLibraryManager/internal/db"
|
"git.artlef.fr/PersonalLibraryManager/internal/db"
|
||||||
"git.artlef.fr/PersonalLibraryManager/internal/model"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetBookHandler(c *gin.Context, db *gorm.DB) {
|
|
||||||
var books []model.Book
|
|
||||||
db.Model(&model.Book{}).Find(&books)
|
|
||||||
c.JSON(http.StatusOK, books)
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
c := config.LoadConfig("plm.toml")
|
c := config.LoadConfig("plm.toml")
|
||||||
db := db.Initdb(".", c.DemoDataPath)
|
r := setup(&c)
|
||||||
|
r.Run(":" + c.Port)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setup(config *config.Config) *gin.Engine {
|
||||||
|
db := db.Initdb(config.DatabaseFilePath, config.DemoDataPath)
|
||||||
r := gin.Default()
|
r := gin.Default()
|
||||||
r.Use(cors.Default()) // All origins allowed by default
|
r.Use(cors.Default()) // All origins allowed by default
|
||||||
r.GET("/books", func(c *gin.Context) {
|
r.GET("/books", func(c *gin.Context) {
|
||||||
GetBookHandler(c, db)
|
api.GetBooksHanderl(c, db)
|
||||||
})
|
})
|
||||||
r.Run(":" + c.Port)
|
r.POST("/book", func(c *gin.Context) {
|
||||||
|
api.PostBookHandler(c, db)
|
||||||
|
})
|
||||||
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user