implemented data sources with prisma in go

This commit is contained in:
Jean Jacques Avril 2025-01-02 13:19:55 +00:00
parent cfb0bdf9cf
commit 615e749a12
No known key found for this signature in database
55 changed files with 1399 additions and 788 deletions

View File

@ -1,3 +0,0 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/

View File

@ -1,3 +0,0 @@
## 1.0.0
- Initial version.

View File

@ -1,2 +0,0 @@
A sample command-line application with an entrypoint in `bin/`, library code
in `lib/`, and example unit test in `test/`.

View File

@ -1,30 +0,0 @@
# This file configures the static analysis results for your project (errors,
# warnings, and lints).
#
# This enables the 'recommended' set of lints from `package:lints`.
# This set helps identify many issues that may lead to problems when running
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
# style and format.
#
# If you want a smaller set of lints you can change this to specify
# 'package:lints/core.yaml'. These are just the most critical lints
# (the recommended set includes the core lints).
# The core lints are also what is used by pub.dev for scoring packages.
include: package:lints/recommended.yaml
# Uncomment the following section to specify additional rules.
# linter:
# rules:
# - camel_case_types
# analyzer:
# exclude:
# - path/to/excluded/files/**
# For more information about the core and recommended set of lints, see
# https://dart.dev/go/core-lints
# For additional information about configuring this file, see
# https://dart.dev/guides/language/analysis-options

View File

@ -1,5 +0,0 @@
module db_client
go 1.23.2
require github.com/steebchen/prisma-client-go v0.43.0 // indirect

View File

@ -1,2 +0,0 @@
github.com/steebchen/prisma-client-go v0.43.0 h1:B4ImvlGo2YXlkSbxjSyin/9Uy3vkhneuKir4K7vXd/A=
github.com/steebchen/prisma-client-go v0.43.0/go.mod h1:wp2xU9HO5WIefc65vcl1HOiFUzaHKyOhHw5atrzs8hc=

View File

@ -1 +0,0 @@

View File

@ -1,5 +0,0 @@
{
"dependencies": {
"prisma": "^5.22.0"
}
}

View File

@ -1,72 +0,0 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
dependencies:
prisma:
specifier: ^5.22.0
version: 5.22.0
packages:
'@prisma/debug@5.22.0':
resolution: {integrity: sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==}
'@prisma/engines-version@5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2':
resolution: {integrity: sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==}
'@prisma/engines@5.22.0':
resolution: {integrity: sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==}
'@prisma/fetch-engine@5.22.0':
resolution: {integrity: sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==}
'@prisma/get-platform@5.22.0':
resolution: {integrity: sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==}
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
prisma@5.22.0:
resolution: {integrity: sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==}
engines: {node: '>=16.13'}
hasBin: true
snapshots:
'@prisma/debug@5.22.0': {}
'@prisma/engines-version@5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2': {}
'@prisma/engines@5.22.0':
dependencies:
'@prisma/debug': 5.22.0
'@prisma/engines-version': 5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2
'@prisma/fetch-engine': 5.22.0
'@prisma/get-platform': 5.22.0
'@prisma/fetch-engine@5.22.0':
dependencies:
'@prisma/debug': 5.22.0
'@prisma/engines-version': 5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2
'@prisma/get-platform': 5.22.0
'@prisma/get-platform@5.22.0':
dependencies:
'@prisma/debug': 5.22.0
fsevents@2.3.3:
optional: true
prisma@5.22.0:
dependencies:
'@prisma/engines': 5.22.0
optionalDependencies:
fsevents: 2.3.3

View File

@ -1,522 +0,0 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
url: "https://pub.dev"
source: hosted
version: "76.0.0"
_macros:
dependency: transitive
description: dart
source: sdk
version: "0.3.3"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
url: "https://pub.dev"
source: hosted
version: "6.11.0"
args:
dependency: transitive
description:
name: args
sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6
url: "https://pub.dev"
source: hosted
version: "2.6.0"
async:
dependency: transitive
description:
name: async
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
url: "https://pub.dev"
source: hosted
version: "2.12.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: "28a712df2576b63c6c005c465989a348604960c0958d28be5303ba9baa841ac2"
url: "https://pub.dev"
source: hosted
version: "8.9.3"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
source: hosted
version: "1.1.2"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
url: "https://pub.dev"
source: hosted
version: "4.10.1"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
source: hosted
version: "1.19.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
coverage:
dependency: transitive
description:
name: coverage
sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43
url: "https://pub.dev"
source: hosted
version: "1.11.1"
crypto:
dependency: transitive
description:
name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
url: "https://pub.dev"
source: hosted
version: "2.3.7"
decimal:
dependency: transitive
description:
name: decimal
sha256: da8f65df568345f2738cc8b0de74971c86d2d93ce5fc8c4ec094f6b7c5d48eb5
url: "https://pub.dev"
source: hosted
version: "3.1.0"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.dev"
source: hosted
version: "1.1.1"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
http:
dependency: transitive
description:
name: http
sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
url: "https://pub.dev"
source: hosted
version: "1.2.2"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
url: "https://pub.dev"
source: hosted
version: "3.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "76d306a1c3afb33fe82e2bbacad62a61f409b5634c915fceb0d799de1a913360"
url: "https://pub.dev"
source: hosted
version: "4.1.1"
intl:
dependency: transitive
description:
name: intl
sha256: "00f33b908655e606b86d2ade4710a231b802eec6f11e87e4ea3783fd72077a50"
url: "https://pub.dev"
source: hosted
version: "0.20.1"
io:
dependency: transitive
description:
name: io
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
url: "https://pub.dev"
source: hosted
version: "1.0.5"
js:
dependency: transitive
description:
name: js
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
url: "https://pub.dev"
source: hosted
version: "0.7.1"
json_rpc_2:
dependency: transitive
description:
name: json_rpc_2
sha256: "246b321532f0e8e2ba474b4d757eaa558ae4fdd0688fdbc1e1ca9705f9b8ca0e"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
lints:
dependency: "direct dev"
description:
name: lints
sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
macros:
dependency: transitive
description:
name: macros
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
url: "https://pub.dev"
source: hosted
version: "0.1.3-main.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev"
source: hosted
version: "0.12.17"
meta:
dependency: transitive
description:
name: meta
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
url: "https://pub.dev"
source: hosted
version: "1.16.0"
mime:
dependency: transitive
description:
name: mime
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
url: "https://pub.dev"
source: hosted
version: "1.0.6"
node_preamble:
dependency: transitive
description:
name: node_preamble
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
orm:
dependency: "direct main"
description:
name: orm
sha256: "6b5fe849c96fc3f521353d834ed029b13621e306ba8b9946c1118ffbf7c6a911"
url: "https://pub.dev"
source: hosted
version: "5.2.1"
package_config:
dependency: transitive
description:
name: package_config
sha256: "92d4488434b520a62570293fbd33bb556c7d49230791c1b4bbd973baf6d2dc67"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
pool:
dependency: transitive
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "7b3cfbf654f3edd0c6298ecd5be782ce997ddf0e00531b9464b55245185bbbbd"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
rational:
dependency: transitive
description:
name: rational
sha256: cb808fb6f1a839e6fc5f7d8cb3b0a10e1db48b3be102de73938c627f0b636336
url: "https://pub.dev"
source: hosted
version: "2.2.3"
rc:
dependency: transitive
description:
name: rc
sha256: "0c2d562c5925b68687ae86b387d847cfc175a5d1d2fce2956a9acc461bd2f33e"
url: "https://pub.dev"
source: hosted
version: "0.3.3"
recase:
dependency: transitive
description:
name: recase
sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213
url: "https://pub.dev"
source: hosted
version: "4.1.0"
shelf:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.2"
shelf_packages_handler:
dependency: transitive
description:
name: shelf_packages_handler
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
shelf_static:
dependency: transitive
description:
name: shelf_static
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
url: "https://pub.dev"
source: hosted
version: "1.1.3"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67
url: "https://pub.dev"
source: hosted
version: "2.0.1"
source_map_stack_trace:
dependency: transitive
description:
name: source_map_stack_trace
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
url: "https://pub.dev"
source: hosted
version: "2.1.2"
source_maps:
dependency: transitive
description:
name: source_maps
sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
url: "https://pub.dev"
source: hosted
version: "0.10.13"
source_span:
dependency: transitive
description:
name: source_span
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
url: "https://pub.dev"
source: hosted
version: "1.10.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
source: hosted
version: "1.12.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "4ac0537115a24d772c408a2520ecd0abb99bca2ea9c4e634ccbdbfae64fe17ec"
url: "https://pub.dev"
source: hosted
version: "2.1.3"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev"
source: hosted
version: "1.4.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
test:
dependency: "direct dev"
description:
name: test
sha256: "8391fbe68d520daf2314121764d38e37f934c02fd7301ad18307bd93bd6b725d"
url: "https://pub.dev"
source: hosted
version: "1.25.14"
test_api:
dependency: transitive
description:
name: test_api
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
url: "https://pub.dev"
source: hosted
version: "0.7.4"
test_core:
dependency: transitive
description:
name: test_core
sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa"
url: "https://pub.dev"
source: hosted
version: "0.6.8"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
url: "https://pub.dev"
source: hosted
version: "15.0.0"
watcher:
dependency: transitive
description:
name: watcher
sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
web:
dependency: transitive
description:
name: web
sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
url: "https://pub.dev"
source: hosted
version: "1.1.0"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83"
url: "https://pub.dev"
source: hosted
version: "0.1.6"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
webfetch:
dependency: transitive
description:
name: webfetch
sha256: "2724933db80e1622baec57d5096b9ae5b1f0a396e047679a91ffeb60bcd5c0f2"
url: "https://pub.dev"
source: hosted
version: "0.1.0"
webkit_inspection_protocol:
dependency: transitive
description:
name: webkit_inspection_protocol
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
url: "https://pub.dev"
source: hosted
version: "1.2.1"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.5.4 <4.0.0"

View File

@ -1,16 +0,0 @@
name: db_client
description: Library for interacting with a database.
version: 1.0.0
# repository: https://github.com/my_org/my_repo
environment:
sdk: ^3.5.4
# Add regular dependencies here.
dependencies:
orm: ^5.2.1 # requires dart>=3.4
# path: ^1.8.0
dev_dependencies:
lints: ^4.0.0
test: ^1.24.0

View File

@ -1,7 +1,7 @@
extension Let<T> on T? {
R? let<R>(R Function(T it) action) {
if (this != null) {
return action(this!);
return action(this as T);
}
return null;
}

View File

@ -4,4 +4,4 @@
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
DATABASE_URL="postgresql://user:secret@localhost:5432/time_tracking_db?schema=public"
DATABASE_URL="postgresql://user:secret@host.docker.internal:5432/time_tracking_db?schema=public"

View File

@ -1,7 +1,7 @@
package main
import (
"actatempus_backend/internal/infrastructure/persistence/config"
"actatempus_backend/internal/infrastructure/config"
"actatempus_backend/internal/interfaces/http"
"fmt"
"log"

View File

@ -2,26 +2,32 @@ module actatempus_backend
go 1.23.2
require github.com/spf13/viper v1.19.0
require (
github.com/joho/godotenv v1.5.1
github.com/shopspring/decimal v1.4.0
github.com/spf13/viper v1.19.0
github.com/steebchen/prisma-client-go v0.45.0
)
require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/IBM/fp-go v1.0.151
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/magiconair/properties v1.8.9 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/text v0.14.0 // indirect
go.mongodb.org/mongo-driver/v2 v2.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -1,71 +1,68 @@
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/IBM/fp-go v1.0.151 h1:PXje3KRwrfIq4lEP/1KUDj4/4xSQxj9M0fWZYWv0LYA=
github.com/IBM/fp-go v1.0.151/go.mod h1:QOwgpyL4RsTBcwRHOk/QfrMExEGxkMPraXkunCyrUeU=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/steebchen/prisma-client-go v0.45.0 h1:sXqCi96/1HIh3CScfd/27OlcR/YDXDtgbrpkDSk+3TY=
github.com/steebchen/prisma-client-go v0.45.0/go.mod h1:4sNeijtPnYSX04/CIXRptUrczEsmPDpWSYR7ahY+H+c=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
go.mongodb.org/mongo-driver/v2 v2.0.0 h1:Jfd7XpdZa9yk3eY774bO7SWVb30noLSirL9nKTpavhI=
go.mongodb.org/mongo-driver/v2 v2.0.0/go.mod h1:nSjmNq4JUstE8IRZKTktLgMHM4F1fccL6HGX1yh+8RA=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo=
golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -0,0 +1,28 @@
package dto
// ProjectDTO represents the data structure for transferring project data.
type ProjectDTO struct {
ID string `json:"id"`
Name string `json:"name"`
Description *string `json:"description"`
ClientID *string `json:"clientId"`
UserID string `json:"userId"`
CreatedAt string `json:"createdAt"` // Use ISO8601 format
UpdatedAt string `json:"updatedAt"` // Use ISO8601 format
}
// ProjectCreateDTO is used for creating a new project.
type ProjectCreateDTO struct {
Name string `json:"name"`
Description *string `json:"description"`
ClientID *string `json:"clientId"`
UserID string `json:"userId"`
}
// ProjectUpdateDTO is used for updating an existing project.
type ProjectUpdateDTO struct {
Name *string `json:"name"`
Description *string `json:"description"`
ClientID *string `json:"clientId"`
UserID *string `json:"userId"`
}

View File

@ -0,0 +1,25 @@
package dto
// ProjectTaskDTO represents the data structure for transferring project task data.
type ProjectTaskDTO struct {
ID string `json:"id"`
Name string `json:"name"`
Description *string `json:"description"`
ProjectID string `json:"projectId"`
CreatedAt string `json:"createdAt"` // Use ISO8601 format
UpdatedAt string `json:"updatedAt"` // Use ISO8601 format
}
// ProjectTaskCreateDTO is used for creating a new project task.
type ProjectTaskCreateDTO struct {
Name string `json:"name"`
Description *string `json:"description"`
ProjectID string `json:"projectId"`
}
// ProjectTaskUpdateDTO is used for updating an existing project task.
type ProjectTaskUpdateDTO struct {
Name *string `json:"name"`
Description *string `json:"description"`
ProjectID *string `json:"projectId"`
}

View File

@ -0,0 +1,31 @@
package dto
// TimeEntryDTO represents the data structure for transferring time entry data.
type TimeEntryDTO struct {
ID string `json:"id"`
StartTime string `json:"startTime"` // Use ISO8601 format
EndTime *string `json:"endTime"` // Use ISO8601 format
Description *string `json:"description"`
UserID string `json:"userId"`
ProjectID string `json:"projectId"`
CreatedAt string `json:"createdAt"` // Use ISO8601 format
UpdatedAt string `json:"updatedAt"` // Use ISO8601 format
}
// TimeEntryCreateDTO is used for creating a new time entry.
type TimeEntryCreateDTO struct {
StartTime string `json:"startTime"` // Use ISO8601 format
EndTime *string `json:"endTime"` // Use ISO8601 format
Description *string `json:"description"`
UserID string `json:"userId"`
ProjectID string `json:"projectId"`
}
// TimeEntryUpdateDTO is used for updating an existing time entry.
type TimeEntryUpdateDTO struct {
StartTime *string `json:"startTime"` // Use ISO8601 format
EndTime *string `json:"endTime"` // Use ISO8601 format
Description *string `json:"description"`
UserID *string `json:"userId"`
ProjectID *string `json:"projectId"`
}

View File

@ -0,0 +1,25 @@
package dto
// UserDTO represents the data structure for transferring user data.
type UserDTO struct {
ID string `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
Password *string `json:"password,omitempty"` // Optional for cases where password shouldn't be exposed
CreatedAt string `json:"createdAt"` // Use ISO8601 format
UpdatedAt string `json:"updatedAt"` // Use ISO8601 format
}
// UserCreateDTO is used for creating a new user.
type UserCreateDTO struct {
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"password"`
}
// UserUpdateDTO is used for updating an existing user.
type UserUpdateDTO struct {
Name *string `json:"name"`
Email *string `json:"email"`
Password *string `json:"password"`
}

View File

@ -0,0 +1,14 @@
package mappers
import (
"time"
)
// Parses ISO8601 timestamps
func parseISOTime(value string) time.Time {
parsed, err := time.Parse(time.RFC3339, value)
if err != nil {
panic("Invalid ISO8601 format") // Or handle error gracefully
}
return parsed
}

View File

@ -0,0 +1,41 @@
package mappers
import (
"actatempus_backend/internal/application/services/dto"
"actatempus_backend/internal/domain/entities"
"time"
)
// MapProjectToDTO converts a Project domain object to a ProjectDTO.
func MapProjectToDTO(project entities.Project) dto.ProjectDTO {
return dto.ProjectDTO{
ID: project.ID,
Name: project.Name,
Description: project.Description,
ClientID: project.ClientID,
UserID: project.UserID,
CreatedAt: project.CreatedAt.Format(time.RFC3339),
UpdatedAt: project.UpdatedAt.Format(time.RFC3339),
}
}
// MapCreateDTOToProject converts a ProjectCreateDTO to a Project domain object.
func MapCreateDTOToProject(dto dto.ProjectCreateDTO) entities.ProjectCreate {
return entities.ProjectCreate{
Name: dto.Name,
Description: dto.Description,
ClientID: dto.ClientID,
UserID: dto.UserID,
}
}
// MapUpdateDTOToProject converts a ProjectUpdateDTO to a partial Project domain object.
func MapUpdateDTOToProject(dto dto.ProjectUpdateDTO, ID string) entities.ProjectUpdate {
return entities.ProjectUpdate{
ID: ID,
Name: dto.Name,
Description: dto.Description,
ClientID: dto.ClientID,
UserID: dto.UserID,
}
}

View File

@ -0,0 +1,38 @@
package mappers
import (
"actatempus_backend/internal/application/services/dto"
"actatempus_backend/internal/domain/entities"
"time"
)
// MapProjectTaskToDTO converts a ProjectTask domain object to a ProjectTaskDTO.
func MapProjectTaskToDTO(task entities.ProjectTask) dto.ProjectTaskDTO {
return dto.ProjectTaskDTO{
ID: task.ID,
Name: task.Name,
Description: task.Description,
ProjectID: task.ProjectID,
CreatedAt: task.CreatedAt.Format(time.RFC3339),
UpdatedAt: task.UpdatedAt.Format(time.RFC3339),
}
}
// MapCreateDTOToProjectTask converts a ProjectTaskCreateDTO to a ProjectTask domain object.
func MapCreateDTOToProjectTask(dto dto.ProjectTaskCreateDTO) entities.ProjectTaskCreate {
return entities.ProjectTaskCreate{
Name: dto.Name,
Description: dto.Description,
ProjectID: dto.ProjectID,
}
}
// MapUpdateDTOToProjectTask converts a ProjectTaskUpdateDTO to a partial ProjectTask domain object.
func MapUpdateDTOToProjectTask(dto dto.ProjectTaskUpdateDTO, ID string) entities.ProjectTaskUpdate {
return entities.ProjectTaskUpdate{
ID: ID,
Name: dto.Name,
Description: dto.Description,
ProjectID: dto.ProjectID,
}
}

View File

@ -0,0 +1,46 @@
package mappers
import (
"actatempus_backend/internal/application/services/dto"
"actatempus_backend/internal/domain/entities"
"actatempus_backend/internal/utils"
"time"
)
// MapTimeEntryToDTO converts a TimeEntry domain object to a TimeEntryDTO.
func MapTimeEntryToDTO(entry entities.TimeEntry) dto.TimeEntryDTO {
return dto.TimeEntryDTO{
ID: entry.ID,
StartTime: entry.StartTime.Format(time.RFC3339),
EndTime: utils.Let(entry.EndTime, func (t time.Time) string { return t.Format(time.RFC3339);}),
Description: entry.Description,
UserID: entry.UserID,
ProjectID: entry.ProjectID,
CreatedAt: entry.CreatedAt.Format(time.RFC3339),
UpdatedAt: entry.UpdatedAt.Format(time.RFC3339),
}
}
// MapCreateDTOToTimeEntry converts a TimeEntryCreateDTO to a TimeEntry domain object.
func MapCreateDTOToTimeEntry(dto dto.TimeEntryCreateDTO) entities.TimeEntryCreate {
return entities.TimeEntryCreate{
StartTime: parseISOTime(dto.StartTime),
EndTime: utils.Let(dto.EndTime,parseISOTime),
Description: dto.Description,
UserID: dto.UserID,
ProjectID: dto.ProjectID,
}
}
// MapUpdateDTOToTimeEntry converts a TimeEntryUpdateDTO to a partial TimeEntry domain object.
func MapUpdateDTOToTimeEntry(dto dto.TimeEntryUpdateDTO, ID string) entities.TimeEntryUpdate {
return entities.TimeEntryUpdate{
ID: ID,
StartTime: utils.Let(dto.StartTime,parseISOTime),
EndTime: utils.Let(dto.EndTime,parseISOTime),
Description: dto.Description,
UserID: dto.UserID,
ProjectID: dto.ProjectID,
}
}

View File

@ -0,0 +1,37 @@
package mappers
import (
"actatempus_backend/internal/application/services/dto"
"actatempus_backend/internal/domain/entities"
"time"
)
// MapUserToDTO converts a User domain object to a UserDTO.
func MapUserToDTO(user entities.User) dto.UserDTO {
return dto.UserDTO{
ID: user.ID,
Name: user.Name,
Email: user.Email,
CreatedAt: user.CreatedAt.Format(time.RFC3339),
UpdatedAt: user.UpdatedAt.Format(time.RFC3339),
}
}
// MapCreateDTOToUser converts a UserCreateDTO to a User domain object.
func MapCreateDTOToUser(dto dto.UserCreateDTO) entities.UserCreate {
return entities.UserCreate{
Name: dto.Name,
Email: dto.Email,
Password: dto.Password,
}
}
// MapUpdateDTOToUser converts a UserUpdateDTO to a partial User domain object.
func MapUpdateDTOToUser(dto dto.UserUpdateDTO, ID string) entities.UserUpdate {
return entities.UserUpdate{
ID: ID,
Name: dto.Name,
Email: dto.Email,
Password: dto.Password,
}
}

View File

@ -1,23 +0,0 @@
package usecases
import (
"actatempus_backend/internal/domain/entities"
"actatempus_backend/internal/domain/repositories"
)
type RegisterUserUseCase struct {
userRepository repositories.UserRepository
}
func NewRegisterUserUseCase(userRepo repositories.UserRepository) *RegisterUserUseCase {
return &RegisterUserUseCase{userRepository: userRepo}
}
func (uc *RegisterUserUseCase) Execute(name, email, password string) error {
user := &entities.User{
Name: name,
Email: email,
Password: password, // In der Realität: Passwörter hashen
}
return uc.userRepository.Create(user)
}

View File

@ -0,0 +1,40 @@
package app_error
import (
"fmt"
)
// AppError is the standard error type used throughout the application.
type AppError struct {
Code ErrorCode `json:"code"`
Message string `json:"message"`
Status int `json:"status"` // HTTP status code
Err error `json:"-"` // Original error, if any
}
// Error satisfies the error interface.
func (e *AppError) Error() string {
if e.Err != nil {
return fmt.Sprintf("Code: %s, Message: %s, Original Error: %s", e.Code, e.Message, e.Err.Error())
}
return fmt.Sprintf("Code: %s, Message: %s", e.Code, e.Message)
}
// Wrap wraps an existing error into an AppError.
func Wrap(err error, code ErrorCode, message string, status int) *AppError {
return &AppError{
Code: code,
Message: message,
Status: status,
Err: err,
}
}
// New creates a new AppError.
func New(code ErrorCode, message string, status int) *AppError {
return &AppError{
Code: code,
Message: message,
Status: status,
}
}

View File

@ -0,0 +1,20 @@
package app_error
// ErrorCode is a custom type for application error codes.
type ErrorCode string
const (
// General errors
InternalError ErrorCode = "internal_error"
ValidationError ErrorCode = "validation_error"
NotFoundError ErrorCode = "not_found"
UnauthorizedError ErrorCode = "unauthorized"
ForbiddenError ErrorCode = "forbidden"
ConflictError ErrorCode = "conflict"
DatabaseError ErrorCode = "database_error"
ExternalServiceError ErrorCode = "external_service_error"
// Specific errors
UserNotFoundError ErrorCode = "user_not_found"
ProjectNotFoundError ErrorCode = "project_not_found"
)

View File

@ -0,0 +1,25 @@
package app_error
import "net/http"
// Helpers for creating common errors
// NewValidationError creates a validation error.
func NewValidationError(message string) *AppError {
return New(ValidationError, message, http.StatusBadRequest)
}
// NewNotFoundError creates a not found error.
func NewNotFoundError(message string) *AppError {
return New(NotFoundError, message, http.StatusNotFound)
}
// NewUnauthorizedError creates an unauthorized error.
func NewUnauthorizedError(message string) *AppError {
return New(UnauthorizedError, message, http.StatusUnauthorized)
}
// NewInternalError creates an internal server error.
func NewInternalError(err error) *AppError {
return Wrap(err, InternalError, "An internal server error occurred", http.StatusInternalServerError)
}

View File

@ -0,0 +1,18 @@
package data
import (
"actatempus_backend/internal/domain/entities"
"context"
E "github.com/IBM/fp-go/either"
)
// ProjectDataSource defines the operations for interacting with project data.
type ProjectDataSource interface {
Create(ctx context.Context, project entities.ProjectCreate) E.Either[error,entities.Project]
FindByID(ctx context.Context, id string) E.Either[error,entities.Project]
FindByUserID(ctx context.Context, userID string) E.Either[error,[]entities.Project]
Update(ctx context.Context, project entities.ProjectUpdate) E.Either[error,entities.Project]
Delete(ctx context.Context, id string) E.Either[error,entities.Project]
FindAll(ctx context.Context) E.Either[error,[]entities.Project]
}

View File

@ -0,0 +1,18 @@
package data
import (
"actatempus_backend/internal/domain/entities"
"context"
E "github.com/IBM/fp-go/either"
)
// ProjectTaskDataSource defines the operations for interacting with project task data.
type ProjectTaskDataSource interface {
Create(ctx context.Context, task entities.ProjectTaskCreate) E.Either[error,entities.ProjectTask]
FindByID(ctx context.Context, id string) E.Either[error,entities.ProjectTask]
FindByProjectID(ctx context.Context, projectID string) E.Either[error,[]entities.ProjectTask]
Update(ctx context.Context, task entities.ProjectTaskUpdate) E.Either[error,entities.ProjectTask]
Delete(ctx context.Context, id string) E.Either[error,entities.ProjectTask]
FindAll(ctx context.Context) E.Either[error,[]entities.ProjectTask]
}

View File

@ -0,0 +1,19 @@
package data
import (
"actatempus_backend/internal/domain/entities"
"context"
E "github.com/IBM/fp-go/either"
)
// TimeEntryDataSource defines the operations for interacting with time entry data.
type TimeEntryDataSource interface {
Create(ctx context.Context, entry entities.TimeEntryCreate) E.Either[error,entities.TimeEntry]
FindByID(ctx context.Context, id string) E.Either[error,entities.TimeEntry]
FindByUserID(ctx context.Context, userID string) E.Either[error,[]entities.TimeEntry]
FindByProjectID(ctx context.Context, projectID string) E.Either[error,[]entities.TimeEntry]
Update(ctx context.Context, entry entities.TimeEntryUpdate) E.Either[error,entities.TimeEntry]
Delete(ctx context.Context, id string) E.Either[error,entities.TimeEntry]
FindAll(ctx context.Context) E.Either[error,[]entities.TimeEntry]
}

View File

@ -0,0 +1,17 @@
package data
import (
"actatempus_backend/internal/domain/entities"
"context"
E "github.com/IBM/fp-go/either"
)
// UserDataSource defines the operations for interacting with user data.
type UserDataSource interface {
Create(ctx context.Context, user entities.UserCreate) E.Either[error,entities.User]
FindByID(ctx context.Context, id string) E.Either[error,entities.User]
FindByEmail(ctx context.Context, email string) E.Either[error,entities.User]
Update(ctx context.Context, user entities.UserUpdate) E.Either[error,entities.User]
Delete(ctx context.Context, id string) E.Either[error,entities.User]
FindAll(ctx context.Context) E.Either[error,[]entities.User]
}

View File

@ -0,0 +1,31 @@
package entities
import "time"
// Project Domain
type Project struct {
ID string
Name string
Description *string
ClientID *string
UserID string
CreatedAt time.Time
UpdatedAt time.Time
}
// ProjectCreate
type ProjectCreate struct {
Name string
Description *string
ClientID *string
UserID string
}
// ProjectUpdate
type ProjectUpdate struct {
ID string
Name *string
Description *string
ClientID *string
UserID *string
}

View File

@ -0,0 +1,28 @@
package entities
import "time"
// ProjectTask Domain
type ProjectTask struct {
ID string
Name string
Description *string
ProjectID string
CreatedAt time.Time
UpdatedAt time.Time
}
// ProjectTaskCreate
type ProjectTaskCreate struct {
Name string
Description *string
ProjectID string
}
// ProjectTaskUpdate
type ProjectTaskUpdate struct {
ID string
Name *string
Description *string
ProjectID *string
}

View File

@ -0,0 +1,34 @@
package entities
import "time"
// TimeEntry Domain
type TimeEntry struct {
ID string
StartTime time.Time
EndTime *time.Time
Description *string
UserID string
ProjectID string
CreatedAt time.Time
UpdatedAt time.Time
}
// TimeEntryCreate
type TimeEntryCreate struct {
StartTime time.Time
EndTime *time.Time
Description *string
UserID string
ProjectID string
}
// TimeEntryUpdate
type TimeEntryUpdate struct {
ID string
StartTime *time.Time
EndTime *time.Time
Description *string
UserID *string
ProjectID *string
}

View File

@ -1,8 +1,30 @@
package entities
import "time"
// In user.go
// User Domain
type User struct {
ID string
ID string
Name string
Email string
Password string
CreatedAt time.Time
UpdatedAt time.Time
}
// UserCreate DTO
type UserCreate struct {
Name string
Email string
Password string
}
// UserUpdate DTO
type UserUpdate struct {
ID string
Name *string
Email *string
Password *string
}

View File

@ -1,9 +0,0 @@
package repositories
import "actatempus_backend/internal/domain/entities"
type UserRepository interface {
Create(user *entities.User) error
FindByEmail(email string) (*entities.User, error)
FindByID(id string) (*entities.User, error)
}

View File

@ -0,0 +1,16 @@
package repository
import (
"actatempus_backend/internal/domain/entities"
"context"
)
// ProjectRepository defines the operations for interacting with project data.
type ProjectRepository interface {
Create(ctx context.Context, project entities.Project) (entities.Project, error)
FindByID(ctx context.Context, id string) (entities.Project, error)
FindByUserID(ctx context.Context, userID string) ([]entities.Project, error)
Update(ctx context.Context, project entities.Project) (entities.Project, error)
Delete(ctx context.Context, id string) error
FindAll(ctx context.Context) ([]entities.Project, error)
}

View File

@ -0,0 +1,16 @@
package repository
import (
"actatempus_backend/internal/domain/entities"
"context"
)
// ProjectTaskRepository defines the operations for interacting with project task data.
type ProjectTaskRepository interface {
Create(ctx context.Context, task entities.ProjectTask) (entities.ProjectTask, error)
FindByID(ctx context.Context, id string) (entities.ProjectTask, error)
FindByProjectID(ctx context.Context, projectID string) ([]entities.ProjectTask, error)
Update(ctx context.Context, task entities.ProjectTask) (entities.ProjectTask, error)
Delete(ctx context.Context, id string) error
FindAll(ctx context.Context) ([]entities.ProjectTask, error)
}

View File

@ -0,0 +1,17 @@
package repository
import (
"actatempus_backend/internal/domain/entities"
"context"
)
// TimeEntryRepository defines the operations for interacting with time entry data.
type TimeEntryRepository interface {
Create(ctx context.Context, entry entities.TimeEntry) (entities.TimeEntry, error)
FindByID(ctx context.Context, id string) (entities.TimeEntry, error)
FindByUserID(ctx context.Context, userID string) ([]entities.TimeEntry, error)
FindByProjectID(ctx context.Context, projectID string) ([]entities.TimeEntry, error)
Update(ctx context.Context, entry entities.TimeEntry) (entities.TimeEntry, error)
Delete(ctx context.Context, id string) error
FindAll(ctx context.Context) ([]entities.TimeEntry, error)
}

View File

@ -0,0 +1,16 @@
package repository
import (
"actatempus_backend/internal/domain/entities"
"context"
)
// UserRepository defines the operations for interacting with user data.
type UserRepository interface {
Create(ctx context.Context, user entities.UserCreate) (entities.User, error)
FindByID(ctx context.Context, id string) (entities.User, error)
FindByEmail(ctx context.Context, email string) (entities.User, error)
Update(ctx context.Context, user entities.UserUpdate) (entities.User, error)
Delete(ctx context.Context, id string) error
FindAll(ctx context.Context) ([]entities.User, error)
}

View File

@ -0,0 +1,22 @@
package data
import (
"actatempus_backend/internal/domain/app_error"
"actatempus_backend/internal/infrastructure/data/db"
"errors"
)
func handleDBError(err error, notFoundMessage string) error {
if errors.Is(err, db.ErrNotFound) {
return app_error.NewNotFoundError(notFoundMessage)
}
return app_error.NewInternalError(err)
}
func NullableField[T any](getter func() (T, bool)) *T {
if value, ok := getter(); ok {
return &value
}
return nil
}

View File

@ -0,0 +1,88 @@
package data
import (
"actatempus_backend/internal/domain/entities"
"actatempus_backend/internal/infrastructure/data/db"
)
// Maps a Prisma User model to the domain User model.
func mapPrismaUserToDomain(user db.UserDboModel) entities.User {
return entities.User{
ID: user.ID,
Name: user.Name,
Email: user.Email,
Password: user.Password,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
}
}
// Maps a slice of Prisma User models to domain User models.
func mapPrismaUsersToDomain(users []db.UserDboModel) []entities.User {
domainUsers := make([]entities.User, len(users))
for i, user := range users {
domainUsers[i] = mapPrismaUserToDomain(user)
}
return domainUsers
}
func mapPrismaProjectToDomain(project db.ProjectDboModel) entities.Project {
return entities.Project{
ID: project.ID,
Name: project.Name,
UserID: project.UserID,
Description: NullableField(project.Description),
ClientID: NullableField(project.ClientID),
CreatedAt: project.CreatedAt,
UpdatedAt: project.UpdatedAt,
}
}
func mapPrismaProjectsToDomain(projects []db.ProjectDboModel) []entities.Project {
domainProjects := make([]entities.Project, len(projects))
for i, project := range projects {
domainProjects[i] = mapPrismaProjectToDomain(project)
}
return domainProjects
}
func mapPrismaTimeEntryToDomain(timeEntry db.TimeEntryDboModel) entities.TimeEntry {
return entities.TimeEntry{
ID: timeEntry.ID,
ProjectID: timeEntry.ProjectID,
UserID: timeEntry.UserID,
StartTime: timeEntry.StartTime,
EndTime: NullableField(timeEntry.EndTime),
Description: NullableField(timeEntry.Description),
CreatedAt: timeEntry.CreatedAt,
UpdatedAt: timeEntry.UpdatedAt,
}
}
func mapPrismaTimeEntriesToDomain(timeEntries []db.TimeEntryDboModel) []entities.TimeEntry {
domainTimeEntries := make([]entities.TimeEntry, len(timeEntries))
for i, timeEntry := range timeEntries {
domainTimeEntries[i] = mapPrismaTimeEntryToDomain(timeEntry)
}
return domainTimeEntries
}
func mapPrismaProjectTaskToDomain(projectTask db.ProjectTaskDboModel) entities.ProjectTask {
return entities.ProjectTask{
ID: projectTask.ID,
ProjectID: projectTask.ProjectID,
Name: projectTask.Name,
Description: NullableField(projectTask.Description),
CreatedAt: projectTask.CreatedAt,
UpdatedAt: projectTask.UpdatedAt,
}
}
func mapPrismaProjectTasksToDomain(projectTasks []db.ProjectTaskDboModel) []entities.ProjectTask {
domainProjectTasks := make([]entities.ProjectTask, len(projectTasks))
for i, projectTask := range projectTasks {
domainProjectTasks[i] = mapPrismaProjectTaskToDomain(projectTask)
}
return domainProjectTasks
}

View File

@ -0,0 +1,66 @@
package data
import (
"actatempus_backend/internal/domain/data"
"actatempus_backend/internal/infrastructure/data/db"
"context"
"log"
)
// PrismaDatabase provides access to various data sources and manages the Prisma client.
type PrismaDatabase struct {
client *db.PrismaClient
users data.UserDataSource
timeEntries data.TimeEntryDataSource
projectTasks data.ProjectTaskDataSource
projects data.ProjectDataSource
}
// NewPrismaDatabase initializes the Prisma client and the data sources.
func NewPrismaDatabase() (*PrismaDatabase, error) {
// Create a new Prisma client
client := db.NewClient()
// Test the connection to ensure the client is working
if err := client.Connect(); err != nil {
return nil, err
}
// Initialize the database and data sources
db := &PrismaDatabase{
client: client,
users: NewPrismaUserDataSource(client),
timeEntries: NewPrismaTimeEntryDataSource(client),
projectTasks: NewPrismaProjectTaskDataSource(client),
projects: NewPrismaProjectDataSource(client),
}
log.Println("Database initialized")
return db, nil
}
// Users returns the User data source.
func (db *PrismaDatabase) Users() data.UserDataSource {
return db.users
}
// TimeEntries returns the TimeEntry data source.
func (db *PrismaDatabase) TimeEntries() data.TimeEntryDataSource {
return db.timeEntries
}
// ProjectTasks returns the ProjectTask data source.
func (db *PrismaDatabase) ProjectTasks() data.ProjectTaskDataSource {
return db.projectTasks
}
// Projects returns the Project data source.
func (db *PrismaDatabase) Projects() data.ProjectDataSource {
return db.projects
}
// Close releases the Prisma client connection.
func (db *PrismaDatabase) Close(ctx context.Context) error {
log.Println("Closing database connection")
return db.client.Disconnect();
}

View File

@ -0,0 +1,123 @@
package data
import (
"actatempus_backend/internal/domain/app_error"
"actatempus_backend/internal/domain/entities"
"actatempus_backend/internal/infrastructure/data/db"
"context"
"fmt"
E "github.com/IBM/fp-go/either"
)
type PrismaProjectDataSource struct {
client *db.PrismaClient
}
func NewPrismaProjectDataSource(client *db.PrismaClient) *PrismaProjectDataSource {
return &PrismaProjectDataSource{client: client}
}
// Create a new project
func (ds *PrismaProjectDataSource) Create(ctx context.Context, project entities.ProjectCreate) E.Either[error, entities.Project] {
createdProject, err := ds.client.ProjectDbo.CreateOne(
db.ProjectDbo.Name.Set(project.Name),
db.ProjectDbo.User.Link(
db.UserDbo.ID.Equals(project.UserID),
),
db.ProjectDbo.UserID.Set(project.UserID),
db.ProjectDbo.Description.SetIfPresent(project.Description),
db.ProjectDbo.ClientID.SetIfPresent(project.ClientID),
).Exec(ctx)
if err != nil {
return E.Left[entities.Project,error](app_error.NewInternalError(err))
}
if createdProject == nil {
return E.Left[entities.Project,error](app_error.NewInternalError(fmt.Errorf("Could not create project")))
}
return E.Right[error](mapPrismaProjectToDomain(*createdProject))
}
// FindByID retrieves a project by ID
func (ds *PrismaProjectDataSource) FindByID(ctx context.Context, id string) E.Either[error, entities.Project] {
project, err := ds.client.ProjectDbo.FindUnique(
db.ProjectDbo.ID.Equals(id),
).Exec(ctx)
if err != nil {
return E.Left[entities.Project](handleDBError(err, fmt.Sprintf("Project with ID %s not found", id)))
}
if project == nil {
return E.Left[entities.Project,error](app_error.NewNotFoundError(fmt.Sprintf("Project with ID %s not found", id)))
}
return E.Right[error](mapPrismaProjectToDomain(*project))
}
// Update updates a project
func (ds *PrismaProjectDataSource) Update(ctx context.Context, project entities.ProjectUpdate) E.Either[error, entities.Project] {
updatedProject, err := ds.client.ProjectDbo.FindUnique(
db.ProjectDbo.ID.Equals(project.ID),
).Update(
db.ProjectDbo.Name.SetIfPresent(project.Name),
db.ProjectDbo.Description.SetIfPresent(project.Description),
db.ProjectDbo.ClientID.SetIfPresent(project.ClientID),
db.ProjectDbo.User.Link(
db.UserDbo.ID.EqualsIfPresent(project.UserID),
),
).Exec(ctx)
if err != nil {
return E.Left[entities.Project](handleDBError(err, fmt.Sprintf("Could not update project with ID %s", project.ID)))
}
if updatedProject == nil {
return E.Left[entities.Project,error](app_error.NewNotFoundError(fmt.Sprintf("Project with ID %s not found", project.ID)))
}
return E.Right[error](mapPrismaProjectToDomain(*updatedProject))
}
// Delete removes a project
func (ds *PrismaProjectDataSource) Delete(ctx context.Context, id string) E.Either[error, entities.Project] {
deleted, err := ds.client.ProjectDbo.FindUnique(
db.ProjectDbo.ID.Equals(id),
).Delete().Exec(ctx)
if err != nil {
return E.Left[entities.Project](handleDBError(err, fmt.Sprintf("Could not delete project with ID %s", id)))
}
if deleted == nil {
return E.Left[entities.Project,error](app_error.NewNotFoundError(fmt.Sprintf("Project with ID %s not found", id)))
}
return E.Right[error](mapPrismaProjectToDomain(*deleted))
}
// FindAll retrieves all projects
func (ds *PrismaProjectDataSource) FindAll(ctx context.Context) E.Either[error, []entities.Project] {
projects, err := ds.client.ProjectDbo.FindMany().Exec(ctx)
if err != nil {
return E.Left[[]entities.Project](handleDBError(err, "Could not retrieve projects"))
}
return E.Right[error](mapPrismaProjectsToDomain(projects))
}
// FindByUserID retrieves all projects for a user
func (ds *PrismaProjectDataSource) FindByUserID(ctx context.Context, userID string) E.Either[error, []entities.Project] {
projects, err := ds.client.ProjectDbo.FindMany(
db.ProjectDbo.UserID.Equals(userID),
).Exec(ctx)
if err != nil {
return E.Left[[]entities.Project](handleDBError(err, fmt.Sprintf("Could not retrieve projects for user with ID %s", userID)))
}
return E.Right[error](mapPrismaProjectsToDomain(projects))
}

View File

@ -0,0 +1,119 @@
package data
import (
"actatempus_backend/internal/domain/app_error"
"actatempus_backend/internal/domain/entities"
"actatempus_backend/internal/infrastructure/data/db"
"context"
"fmt"
E "github.com/IBM/fp-go/either"
)
type PrismaProjectTaskDataSource struct {
client *db.PrismaClient
}
func NewPrismaProjectTaskDataSource(client *db.PrismaClient) *PrismaProjectTaskDataSource {
return &PrismaProjectTaskDataSource{client: client}
}
// Create a new ProjectTask
func (ds *PrismaProjectTaskDataSource) Create(ctx context.Context, task entities.ProjectTaskCreate) E.Either[error, entities.ProjectTask] {
createdTask, err := ds.client.ProjectTaskDbo.CreateOne(
db.ProjectTaskDbo.Name.Set(task.Name),
db.ProjectTaskDbo.Project.Link(
db.ProjectDbo.ID.Equals(task.ProjectID),
),
db.ProjectTaskDbo.Description.SetIfPresent(task.Description),
).Exec(ctx)
if err != nil {
return E.Left[entities.ProjectTask,error](handleDBError(err, fmt.Sprintf("Could not create project task")))
}
if createdTask == nil {
return E.Left[entities.ProjectTask,error](app_error.NewInternalError(fmt.Errorf("Could not create project task")))
}
return E.Right[error](mapPrismaProjectTaskToDomain(*createdTask))
}
// Find ProjectTask by ID
func (ds *PrismaProjectTaskDataSource) FindByID(ctx context.Context, id string) E.Either[error, entities.ProjectTask] {
task, err := ds.client.ProjectTaskDbo.FindUnique(
db.ProjectTaskDbo.ID.Equals(id),
).Exec(ctx)
if err != nil {
return E.Left[entities.ProjectTask](handleDBError(err, fmt.Sprintf("ProjectTask with ID %s not found", id)))
}
if task == nil {
return E.Left[entities.ProjectTask,error](app_error.NewNotFoundError(fmt.Sprintf("ProjectTask with ID %s not found", id)))
}
return E.Right[error](mapPrismaProjectTaskToDomain(*task))
}
// Update an existing ProjectTask
func (ds *PrismaProjectTaskDataSource) Update(ctx context.Context, task entities.ProjectTaskUpdate) E.Either[error, entities.ProjectTask] {
updatedTask, err := ds.client.ProjectTaskDbo.FindUnique(
db.ProjectTaskDbo.ID.Equals(task.ID),
).Update(
db.ProjectTaskDbo.Name.SetIfPresent(task.Name),
db.ProjectTaskDbo.Description.SetIfPresent(task.Description),
db.ProjectTaskDbo.Project.Link(
db.ProjectDbo.ID.EqualsIfPresent(task.ProjectID),
),
).Exec(ctx)
if err != nil {
return E.Left[entities.ProjectTask](handleDBError(err, fmt.Sprintf("Could not update project task with ID %s", task.ID)))
}
if updatedTask == nil {
return E.Left[entities.ProjectTask,error](app_error.NewNotFoundError(fmt.Sprintf("ProjectTask with ID %s not found", task.ID)))
}
return E.Right[error](mapPrismaProjectTaskToDomain(*updatedTask))
}
// Delete a ProjectTask
func (ds *PrismaProjectTaskDataSource) Delete(ctx context.Context, id string) E.Either[error, entities.ProjectTask] {
deletedTask, err := ds.client.ProjectTaskDbo.FindUnique(
db.ProjectTaskDbo.ID.Equals(id),
).Delete().Exec(ctx)
if err != nil {
return E.Left[entities.ProjectTask](handleDBError(err, fmt.Sprintf("Could not delete project task with ID %s", id)))
}
if deletedTask == nil {
return E.Left[entities.ProjectTask,error](app_error.NewNotFoundError(fmt.Sprintf("ProjectTask with ID %s not found", id)))
}
return E.Right[error](mapPrismaProjectTaskToDomain(*deletedTask))
}
// FindAll retrieves all ProjectTasks
func (ds *PrismaProjectTaskDataSource) FindAll(ctx context.Context) E.Either[error, []entities.ProjectTask] {
tasks, err := ds.client.ProjectTaskDbo.FindMany().Exec(ctx)
if err != nil {
return E.Left[[]entities.ProjectTask](handleDBError(err, "Could not retrieve project tasks"))
}
return E.Right[error](mapPrismaProjectTasksToDomain(tasks))
}
// FindByProjectID retrieves all ProjectTasks for a given Project
func (ds *PrismaProjectTaskDataSource) FindByProjectID(ctx context.Context, projectID string) E.Either[error, []entities.ProjectTask] {
tasks, err := ds.client.ProjectTaskDbo.FindMany(
db.ProjectTaskDbo.ProjectID.Equals(projectID),
).Exec(ctx)
if err != nil {
return E.Left[[]entities.ProjectTask](handleDBError(err, fmt.Sprintf("Could not retrieve project tasks for project with ID %s", projectID)))
}
return E.Right[error](mapPrismaProjectTasksToDomain(tasks))
}

View File

@ -0,0 +1,140 @@
package data
import (
"actatempus_backend/internal/domain/app_error"
"actatempus_backend/internal/domain/entities"
"actatempus_backend/internal/infrastructure/data/db"
"context"
"fmt"
E "github.com/IBM/fp-go/either"
)
type PrismaTimeEntryDataSource struct {
client *db.PrismaClient
}
func NewPrismaTimeEntryDataSource(client *db.PrismaClient) *PrismaTimeEntryDataSource {
return &PrismaTimeEntryDataSource{client: client}
}
// Create a new TimeEntry
func (ds *PrismaTimeEntryDataSource) Create(ctx context.Context, entry entities.TimeEntryCreate) E.Either[error, entities.TimeEntry] {
createdEntry, err := ds.client.TimeEntryDbo.CreateOne(
db.TimeEntryDbo.StartTime.Set(entry.StartTime),
db.TimeEntryDbo.User.Link(
db.UserDbo.ID.Equals(entry.UserID),
), db.TimeEntryDbo.Project.Link(
db.ProjectDbo.ID.Equals(entry.ProjectID),
),
db.TimeEntryDbo.EndTime.SetIfPresent(entry.EndTime),
db.TimeEntryDbo.Description.SetIfPresent(entry.Description),
).Exec(ctx)
if err != nil {
return E.Left[entities.TimeEntry,error](app_error.NewInternalError(err))
}
if createdEntry == nil {
return E.Left[entities.TimeEntry,error](app_error.NewInternalError(fmt.Errorf("Could not create time entry")))
}
return E.Right[error](mapPrismaTimeEntryToDomain(*createdEntry))
}
// Find TimeEntry by ID
func (ds *PrismaTimeEntryDataSource) FindByID(ctx context.Context, id string) E.Either[error, entities.TimeEntry] {
entry, err := ds.client.TimeEntryDbo.FindUnique(
db.TimeEntryDbo.ID.Equals(id),
).Exec(ctx)
if err != nil {
return E.Left[entities.TimeEntry](handleDBError(err, fmt.Sprintf("TimeEntry with ID %s not found", id)))
}
if entry == nil {
return E.Left[entities.TimeEntry,error](app_error.NewNotFoundError(fmt.Sprintf("TimeEntry with ID %s not found", id)))
}
return E.Right[error](mapPrismaTimeEntryToDomain(*entry))
}
// Update an existing TimeEntry
func (ds *PrismaTimeEntryDataSource) Update(ctx context.Context, entry entities.TimeEntryUpdate) E.Either[error, entities.TimeEntry] {
updatedEntry, err := ds.client.TimeEntryDbo.FindUnique(
db.TimeEntryDbo.ID.Equals(entry.ID),
).Update(
db.TimeEntryDbo.StartTime.SetIfPresent(entry.StartTime),
db.TimeEntryDbo.EndTime.SetIfPresent(entry.EndTime),
db.TimeEntryDbo.Description.SetIfPresent(entry.Description),
db.TimeEntryDbo.User.Link(
db.UserDbo.ID.EqualsIfPresent(entry.UserID),
),
db.TimeEntryDbo.Project.Link(
db.ProjectDbo.ID.EqualsIfPresent(entry.ProjectID),
),
).Exec(ctx)
if err != nil {
return E.Left[entities.TimeEntry,error](handleDBError(err, fmt.Sprintf("Could not update time entry with ID %s", entry.ID)))
}
if updatedEntry == nil {
return E.Left[entities.TimeEntry,error](app_error.NewNotFoundError(fmt.Sprintf("TimeEntry with ID %s not found", entry.ID)))
}
return E.Right[error](mapPrismaTimeEntryToDomain(*updatedEntry))
}
// Delete a TimeEntry
func (ds *PrismaTimeEntryDataSource) Delete(ctx context.Context, id string) E.Either[error, entities.TimeEntry] {
deletedEntry, err := ds.client.TimeEntryDbo.FindUnique(
db.TimeEntryDbo.ID.Equals(id),
).Delete().Exec(ctx)
if err != nil {
return E.Left[entities.TimeEntry](handleDBError(err, fmt.Sprintf("Could not delete time entry with ID %s", id)))
}
if deletedEntry == nil {
return E.Left[entities.TimeEntry,error](app_error.NewNotFoundError(fmt.Sprintf("TimeEntry with ID %s not found", id)))
}
return E.Right[error](mapPrismaTimeEntryToDomain(*deletedEntry))
}
// FindAll retrieves all TimeEntries
func (ds *PrismaTimeEntryDataSource) FindAll(ctx context.Context) E.Either[error, []entities.TimeEntry] {
entries, err := ds.client.TimeEntryDbo.FindMany().Exec(ctx)
if err != nil {
return E.Left[[]entities.TimeEntry](handleDBError(err, "Could not retrieve time entries"))
}
return E.Right[error](mapPrismaTimeEntriesToDomain(entries))
}
// FindByUserID retrieves all TimeEntries by UserID
func (ds *PrismaTimeEntryDataSource) FindByUserID(ctx context.Context, userID string) E.Either[error, []entities.TimeEntry] {
entries, err := ds.client.TimeEntryDbo.FindMany(
db.TimeEntryDbo.UserID.Equals(userID),
).Exec(ctx)
if err != nil {
return E.Left[[]entities.TimeEntry](handleDBError(err, fmt.Sprintf("Could not retrieve time entries for user with ID %s", userID)))
}
return E.Right[error](mapPrismaTimeEntriesToDomain(entries))
}
// FindByProjectID retrieves all TimeEntries by ProjectID
func (ds *PrismaTimeEntryDataSource) FindByProjectID(ctx context.Context, projectID string) E.Either[error, []entities.TimeEntry] {
entries, err := ds.client.TimeEntryDbo.FindMany(
db.TimeEntryDbo.ProjectID.Equals(projectID),
).Exec(ctx)
if err != nil {
return E.Left[[]entities.TimeEntry](handleDBError(err, fmt.Sprintf("Could not retrieve time entries for project with ID %s", projectID)))
}
return E.Right[error](mapPrismaTimeEntriesToDomain(entries))
}

View File

@ -0,0 +1,115 @@
package data
import (
"actatempus_backend/internal/domain/app_error"
"actatempus_backend/internal/domain/entities"
"actatempus_backend/internal/infrastructure/data/db"
"context"
"fmt"
E "github.com/IBM/fp-go/either"
)
type PrismaUserDataSource struct {
client *db.PrismaClient
}
func NewPrismaUserDataSource(client *db.PrismaClient) *PrismaUserDataSource {
return &PrismaUserDataSource{client: client}
}
func (ds *PrismaUserDataSource) Create(ctx context.Context, user entities.UserCreate) E.Either[error,entities.User] {
createdUser, err := ds.client.UserDbo.CreateOne(
db.UserDbo.Name.Set(user.Name),
db.UserDbo.Email.Set(user.Email),
db.UserDbo.Password.Set(user.Password),
).Exec(ctx)
if err != nil {
return E.Left[entities.User,error](app_error.NewInternalError(err))
}
if createdUser == nil {
return E.Left[entities.User,error](app_error.NewInternalError(fmt.Errorf("Could not create user")))
}
return E.Right[error](mapPrismaUserToDomain(*createdUser))
}
func (ds *PrismaUserDataSource) FindByID(ctx context.Context, id string) E.Either[error,entities.User] {
user, err := ds.client.UserDbo.FindUnique(
db.UserDbo.ID.Equals(id),
).Exec(ctx)
if err != nil {
return E.Left[entities.User,error](handleDBError(err, fmt.Sprintf("Query for user with ID %s failed", id)))
}
if user == nil {
return E.Left[entities.User,error](app_error.NewNotFoundError(fmt.Sprintf("User with ID %s not found", id)))
}
return E.Right[error](mapPrismaUserToDomain(*user))
}
func (ds *PrismaUserDataSource) FindByEmail(ctx context.Context, email string) E.Either[error,entities.User]{
user, err := ds.client.UserDbo.FindUnique(
db.UserDbo.Email.Equals(email),
).Exec(ctx)
if err != nil {
return E.Left[entities.User,error](handleDBError(err, fmt.Sprintf("Query for user with email %s failed", email)))
}
if user == nil {
return E.Left[entities.User,error](app_error.NewNotFoundError(fmt.Sprintf("User with email %s not found", email)))
}
return E.Right[error](mapPrismaUserToDomain(*user))
}
func (ds *PrismaUserDataSource) Update(ctx context.Context, user entities.UserUpdate) E.Either[error,entities.User] {
updatedUser, err := ds.client.UserDbo.FindUnique(
db.UserDbo.ID.Equals(user.ID),
).Update(
db.UserDbo.Name.SetIfPresent(user.Name),
db.UserDbo.Email.SetIfPresent(user.Email),
db.UserDbo.Password.SetIfPresent(user.Password),
).Exec(ctx)
if err != nil {
return E.Left[entities.User,error](handleDBError(err, fmt.Sprintf("Could not update user with ID %s", user.ID)))
}
if updatedUser == nil {
return E.Left[entities.User,error](app_error.NewNotFoundError(fmt.Sprintf("User with ID %s not found", user.ID)))
}
return E.Right[error](mapPrismaUserToDomain(*updatedUser))
}
func (ds *PrismaUserDataSource) Delete(ctx context.Context, id string) E.Either[error,entities.User] {
deleted, err := ds.client.UserDbo.FindUnique(
db.UserDbo.ID.Equals(id),
).Delete().Exec(ctx)
if err != nil {
return E.Left[entities.User,error](handleDBError(err, fmt.Sprintf("Could not delete user with ID %s", id)))
}
if deleted == nil {
return E.Left[entities.User,error](app_error.NewNotFoundError(fmt.Sprintf("User with ID %s not found", id)))
}
return E.Right[error](mapPrismaUserToDomain(*deleted))
}
func (ds *PrismaUserDataSource) FindAll(ctx context.Context) E.Either[error,[]entities.User] {
users, err := ds.client.UserDbo.FindMany().Exec(ctx)
if err != nil {
return E.Left[[]entities.User,error](handleDBError(err, "Could not retrieve users"))
}
return E.Right[error](mapPrismaUsersToDomain(users))
}

View File

@ -1,7 +1,7 @@
package http
import (
"actatempus_backend/internal/infrastructure/persistence/config"
"actatempus_backend/internal/infrastructure/config"
"fmt"
"net/http"
)

View File

@ -0,0 +1,11 @@
package utils
// Let applies a function `f` to a pointer value `ptr` if it's not nil.
// Returns nil if the pointer is nil.
func Let[T any, R any](ptr *T, f func(T) R) *R {
if ptr == nil {
return nil
}
result := f(*ptr)
return &result
}

View File

@ -1,11 +1,6 @@
generator dartClient {
provider = "dart run orm"
output = "../../../backend_dart/lib/infrastructure/persistence/db"
}
generator goClient {
provider = "go run github.com/steebchen/prisma-client-go"
output = "../../../backend-go/internal/infrastructure/persistence/db"
output = "../internal/infrastructure/data/db"
}
datasource db {
@ -14,51 +9,51 @@ datasource db {
}
// User Model
model User {
model UserDbo {
id String @id @default(uuid())
name String
email String @unique
password String
projects Project[] // Beziehung zu Projekten
timeEntries TimeEntry[] // Beziehung zu Zeiteinträgen
projects ProjectDbo[] // Beziehung zu Projekten
timeEntries TimeEntryDbo[] // Beziehung zu Zeiteinträgen
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// Project Model
model Project {
model ProjectDbo {
id String @id @default(uuid())
name String
description String?
clientId String?
tasks Task[] // Beziehung zu Aufgaben
timeEntries TimeEntry[] // Beziehung zu Zeiteinträgen
user User @relation(fields: [userId], references: [id])
tasks ProjectTaskDbo[] // Beziehung zu Aufgaben
timeEntries TimeEntryDbo[] // Beziehung zu Zeiteinträgen
user UserDbo @relation(fields: [userId], references: [id])
userId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// TimeEntry Model
model TimeEntry {
model TimeEntryDbo {
id String @id @default(uuid())
startTime DateTime
endTime DateTime
endTime DateTime?
description String?
user User @relation(fields: [userId], references: [id])
user UserDbo @relation(fields: [userId], references: [id])
userId String
project Project @relation(fields: [projectId], references: [id])
project ProjectDbo @relation(fields: [projectId], references: [id])
projectId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
// Task Model (optional)
model Task {
model ProjectTaskDbo {
id String @id @default(uuid())
name String
description String?
project Project @relation(fields: [projectId], references: [id])
project ProjectDbo @relation(fields: [projectId], references: [id])
projectId String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt