diff --git a/_common/db_client/.gitignore b/_common/db_client/.gitignore deleted file mode 100755 index 3a85790..0000000 --- a/_common/db_client/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# https://dart.dev/guides/libraries/private-files -# Created by `dart pub` -.dart_tool/ diff --git a/_common/db_client/CHANGELOG.md b/_common/db_client/CHANGELOG.md deleted file mode 100755 index effe43c..0000000 --- a/_common/db_client/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 1.0.0 - -- Initial version. diff --git a/_common/db_client/README.md b/_common/db_client/README.md deleted file mode 100755 index 3816eca..0000000 --- a/_common/db_client/README.md +++ /dev/null @@ -1,2 +0,0 @@ -A sample command-line application with an entrypoint in `bin/`, library code -in `lib/`, and example unit test in `test/`. diff --git a/_common/db_client/analysis_options.yaml b/_common/db_client/analysis_options.yaml deleted file mode 100755 index dee8927..0000000 --- a/_common/db_client/analysis_options.yaml +++ /dev/null @@ -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 diff --git a/_common/db_client/go.mod b/_common/db_client/go.mod deleted file mode 100755 index bce2e32..0000000 --- a/_common/db_client/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module db_client - -go 1.23.2 - -require github.com/steebchen/prisma-client-go v0.43.0 // indirect diff --git a/_common/db_client/go.sum b/_common/db_client/go.sum deleted file mode 100755 index a4a9287..0000000 --- a/_common/db_client/go.sum +++ /dev/null @@ -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= diff --git a/_common/db_client/lib/db_client.dart b/_common/db_client/lib/db_client.dart deleted file mode 100755 index 8b13789..0000000 --- a/_common/db_client/lib/db_client.dart +++ /dev/null @@ -1 +0,0 @@ - diff --git a/_common/db_client/package.json b/_common/db_client/package.json deleted file mode 100755 index 0a9a569..0000000 --- a/_common/db_client/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "prisma": "^5.22.0" - } -} diff --git a/_common/db_client/pnpm-lock.yaml b/_common/db_client/pnpm-lock.yaml deleted file mode 100755 index d984a4f..0000000 --- a/_common/db_client/pnpm-lock.yaml +++ /dev/null @@ -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 diff --git a/_common/db_client/pubspec.lock b/_common/db_client/pubspec.lock deleted file mode 100755 index 4b66dbd..0000000 --- a/_common/db_client/pubspec.lock +++ /dev/null @@ -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" diff --git a/_common/db_client/pubspec.yaml b/_common/db_client/pubspec.yaml deleted file mode 100755 index 5933d55..0000000 --- a/_common/db_client/pubspec.yaml +++ /dev/null @@ -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 diff --git a/backend-dart/lib/common/extensions.dart b/backend-dart/lib/common/extensions.dart index 51fdd3a..cab7dee 100644 --- a/backend-dart/lib/common/extensions.dart +++ b/backend-dart/lib/common/extensions.dart @@ -1,7 +1,7 @@ extension Let on T? { R? let(R Function(T it) action) { if (this != null) { - return action(this!); + return action(this as T); } return null; } diff --git a/_common/db_client/.env b/backend-go/.env similarity index 81% rename from _common/db_client/.env rename to backend-go/.env index 2d08b29..889b884 100755 --- a/_common/db_client/.env +++ b/backend-go/.env @@ -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" \ No newline at end of file +DATABASE_URL="postgresql://user:secret@host.docker.internal:5432/time_tracking_db?schema=public" \ No newline at end of file diff --git a/backend-go/cmd/actatempus/main.go b/backend-go/cmd/actatempus/main.go index 77d640d..4ec72e4 100755 --- a/backend-go/cmd/actatempus/main.go +++ b/backend-go/cmd/actatempus/main.go @@ -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" diff --git a/backend-go/go.mod b/backend-go/go.mod index f38e863..aad98f7 100755 --- a/backend-go/go.mod +++ b/backend-go/go.mod @@ -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 ) diff --git a/backend-go/go.sum b/backend-go/go.sum index ec3cc1e..6979c62 100755 --- a/backend-go/go.sum +++ b/backend-go/go.sum @@ -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= diff --git a/backend-go/internal/application/services/dto/project_dto.go b/backend-go/internal/application/services/dto/project_dto.go new file mode 100644 index 0000000..6d1fcda --- /dev/null +++ b/backend-go/internal/application/services/dto/project_dto.go @@ -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"` +} diff --git a/backend-go/internal/application/services/dto/project_task_dto.go b/backend-go/internal/application/services/dto/project_task_dto.go new file mode 100644 index 0000000..37acabc --- /dev/null +++ b/backend-go/internal/application/services/dto/project_task_dto.go @@ -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"` +} diff --git a/backend-go/internal/application/services/dto/time_entry_dto.go b/backend-go/internal/application/services/dto/time_entry_dto.go new file mode 100644 index 0000000..6427936 --- /dev/null +++ b/backend-go/internal/application/services/dto/time_entry_dto.go @@ -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"` +} diff --git a/backend-go/internal/application/services/dto/user_dto.go b/backend-go/internal/application/services/dto/user_dto.go new file mode 100644 index 0000000..98ba738 --- /dev/null +++ b/backend-go/internal/application/services/dto/user_dto.go @@ -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"` +} diff --git a/backend-go/internal/application/services/mapper/helper.go b/backend-go/internal/application/services/mapper/helper.go new file mode 100644 index 0000000..f4c44ec --- /dev/null +++ b/backend-go/internal/application/services/mapper/helper.go @@ -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 +} \ No newline at end of file diff --git a/backend-go/internal/application/services/mapper/project_dto_mapper.go b/backend-go/internal/application/services/mapper/project_dto_mapper.go new file mode 100644 index 0000000..678f69d --- /dev/null +++ b/backend-go/internal/application/services/mapper/project_dto_mapper.go @@ -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, + } +} diff --git a/backend-go/internal/application/services/mapper/project_task_mapper.go b/backend-go/internal/application/services/mapper/project_task_mapper.go new file mode 100644 index 0000000..eb0da99 --- /dev/null +++ b/backend-go/internal/application/services/mapper/project_task_mapper.go @@ -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, + } +} diff --git a/backend-go/internal/application/services/mapper/time_entry_mapper.go b/backend-go/internal/application/services/mapper/time_entry_mapper.go new file mode 100644 index 0000000..976c71c --- /dev/null +++ b/backend-go/internal/application/services/mapper/time_entry_mapper.go @@ -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, + } +} diff --git a/backend-go/internal/application/services/mapper/user_dto_mapper.go b/backend-go/internal/application/services/mapper/user_dto_mapper.go new file mode 100644 index 0000000..1d6e761 --- /dev/null +++ b/backend-go/internal/application/services/mapper/user_dto_mapper.go @@ -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, + } +} diff --git a/backend-go/internal/application/usecases/register_user.go b/backend-go/internal/application/usecases/register_user.go deleted file mode 100755 index 3609968..0000000 --- a/backend-go/internal/application/usecases/register_user.go +++ /dev/null @@ -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) -} diff --git a/backend-go/internal/domain/app_error/app_error.go b/backend-go/internal/domain/app_error/app_error.go new file mode 100644 index 0000000..6c36bcd --- /dev/null +++ b/backend-go/internal/domain/app_error/app_error.go @@ -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, + } +} diff --git a/backend-go/internal/domain/app_error/error_codes.go b/backend-go/internal/domain/app_error/error_codes.go new file mode 100644 index 0000000..2b0b66b --- /dev/null +++ b/backend-go/internal/domain/app_error/error_codes.go @@ -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" +) diff --git a/backend-go/internal/domain/app_error/helpers.go b/backend-go/internal/domain/app_error/helpers.go new file mode 100644 index 0000000..8095e23 --- /dev/null +++ b/backend-go/internal/domain/app_error/helpers.go @@ -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) +} diff --git a/backend-go/internal/domain/data/project_data_source.go b/backend-go/internal/domain/data/project_data_source.go new file mode 100644 index 0000000..086471b --- /dev/null +++ b/backend-go/internal/domain/data/project_data_source.go @@ -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] +} diff --git a/backend-go/internal/domain/data/project_task_data_source.go b/backend-go/internal/domain/data/project_task_data_source.go new file mode 100644 index 0000000..173506a --- /dev/null +++ b/backend-go/internal/domain/data/project_task_data_source.go @@ -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] +} diff --git a/backend-go/internal/domain/data/time_entry_data_source.go b/backend-go/internal/domain/data/time_entry_data_source.go new file mode 100644 index 0000000..eb13130 --- /dev/null +++ b/backend-go/internal/domain/data/time_entry_data_source.go @@ -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] +} diff --git a/backend-go/internal/domain/data/user_data_source.go b/backend-go/internal/domain/data/user_data_source.go new file mode 100644 index 0000000..3e4ddeb --- /dev/null +++ b/backend-go/internal/domain/data/user_data_source.go @@ -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] +} diff --git a/backend-go/internal/domain/entities/project.go b/backend-go/internal/domain/entities/project.go new file mode 100644 index 0000000..424fb7c --- /dev/null +++ b/backend-go/internal/domain/entities/project.go @@ -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 +} diff --git a/backend-go/internal/domain/entities/project_task.go b/backend-go/internal/domain/entities/project_task.go new file mode 100644 index 0000000..96e4ec1 --- /dev/null +++ b/backend-go/internal/domain/entities/project_task.go @@ -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 +} diff --git a/backend-go/internal/domain/entities/time_entry.go b/backend-go/internal/domain/entities/time_entry.go new file mode 100644 index 0000000..b4b236a --- /dev/null +++ b/backend-go/internal/domain/entities/time_entry.go @@ -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 +} diff --git a/backend-go/internal/domain/entities/user.go b/backend-go/internal/domain/entities/user.go index 2895da0..b295169 100755 --- a/backend-go/internal/domain/entities/user.go +++ b/backend-go/internal/domain/entities/user.go @@ -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 +} diff --git a/backend-go/internal/domain/repositories/user_repository.go b/backend-go/internal/domain/repositories/user_repository.go deleted file mode 100755 index a3a2c91..0000000 --- a/backend-go/internal/domain/repositories/user_repository.go +++ /dev/null @@ -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) -} diff --git a/backend-go/internal/domain/repository/project_repository.go b/backend-go/internal/domain/repository/project_repository.go new file mode 100644 index 0000000..b8c3880 --- /dev/null +++ b/backend-go/internal/domain/repository/project_repository.go @@ -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) +} diff --git a/backend-go/internal/domain/repository/project_task_repository.go b/backend-go/internal/domain/repository/project_task_repository.go new file mode 100644 index 0000000..9e86542 --- /dev/null +++ b/backend-go/internal/domain/repository/project_task_repository.go @@ -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) +} diff --git a/backend-go/internal/domain/repository/time_entry_repository.go b/backend-go/internal/domain/repository/time_entry_repository.go new file mode 100644 index 0000000..fc26971 --- /dev/null +++ b/backend-go/internal/domain/repository/time_entry_repository.go @@ -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) +} diff --git a/backend-go/internal/domain/repository/user_repository.go b/backend-go/internal/domain/repository/user_repository.go new file mode 100755 index 0000000..400e222 --- /dev/null +++ b/backend-go/internal/domain/repository/user_repository.go @@ -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) +} diff --git a/backend-go/internal/infrastructure/persistence/config/config.go b/backend-go/internal/infrastructure/config/config.go similarity index 94% rename from backend-go/internal/infrastructure/persistence/config/config.go rename to backend-go/internal/infrastructure/config/config.go index 324102b..c376836 100755 --- a/backend-go/internal/infrastructure/persistence/config/config.go +++ b/backend-go/internal/infrastructure/config/config.go @@ -1,25 +1,25 @@ -package config - -import ( - "github.com/spf13/viper" -) - -type Config struct { - DatabaseURL string - Port string -} - -func LoadConfig(path string) (*Config, error) { - viper.AddConfigPath(path) - viper.SetConfigName("config") - viper.SetConfigType("yaml") - - if err := viper.ReadInConfig(); err != nil { - return nil, err - } - - return &Config{ - DatabaseURL: viper.GetString("database.url"), - Port: viper.GetString("server.port"), - }, nil -} +package config + +import ( + "github.com/spf13/viper" +) + +type Config struct { + DatabaseURL string + Port string +} + +func LoadConfig(path string) (*Config, error) { + viper.AddConfigPath(path) + viper.SetConfigName("config") + viper.SetConfigType("yaml") + + if err := viper.ReadInConfig(); err != nil { + return nil, err + } + + return &Config{ + DatabaseURL: viper.GetString("database.url"), + Port: viper.GetString("server.port"), + }, nil +} diff --git a/backend-go/internal/infrastructure/persistence/db/.gitignore b/backend-go/internal/infrastructure/data/db/.gitignore old mode 100755 new mode 100644 similarity index 100% rename from backend-go/internal/infrastructure/persistence/db/.gitignore rename to backend-go/internal/infrastructure/data/db/.gitignore diff --git a/backend-go/internal/infrastructure/data/helper.go b/backend-go/internal/infrastructure/data/helper.go new file mode 100644 index 0000000..8d8e510 --- /dev/null +++ b/backend-go/internal/infrastructure/data/helper.go @@ -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 +} \ No newline at end of file diff --git a/backend-go/internal/infrastructure/data/mapper.go b/backend-go/internal/infrastructure/data/mapper.go new file mode 100644 index 0000000..d1d910d --- /dev/null +++ b/backend-go/internal/infrastructure/data/mapper.go @@ -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 +} \ No newline at end of file diff --git a/backend-go/internal/infrastructure/data/primsa_database.go b/backend-go/internal/infrastructure/data/primsa_database.go new file mode 100644 index 0000000..e265189 --- /dev/null +++ b/backend-go/internal/infrastructure/data/primsa_database.go @@ -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(); +} diff --git a/backend-go/internal/infrastructure/data/prisma_project_data_source.go b/backend-go/internal/infrastructure/data/prisma_project_data_source.go new file mode 100644 index 0000000..81b6789 --- /dev/null +++ b/backend-go/internal/infrastructure/data/prisma_project_data_source.go @@ -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)) +} \ No newline at end of file diff --git a/backend-go/internal/infrastructure/data/prisma_project_task_data_source.go b/backend-go/internal/infrastructure/data/prisma_project_task_data_source.go new file mode 100644 index 0000000..5d7dcd9 --- /dev/null +++ b/backend-go/internal/infrastructure/data/prisma_project_task_data_source.go @@ -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)) +} \ No newline at end of file diff --git a/backend-go/internal/infrastructure/data/prisma_time_entries_data_source.go b/backend-go/internal/infrastructure/data/prisma_time_entries_data_source.go new file mode 100644 index 0000000..876902b --- /dev/null +++ b/backend-go/internal/infrastructure/data/prisma_time_entries_data_source.go @@ -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)) +} \ No newline at end of file diff --git a/backend-go/internal/infrastructure/data/prisma_user_data_source.go b/backend-go/internal/infrastructure/data/prisma_user_data_source.go new file mode 100644 index 0000000..49e1dfe --- /dev/null +++ b/backend-go/internal/infrastructure/data/prisma_user_data_source.go @@ -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)) +} diff --git a/backend-go/internal/interfaces/http/server.go b/backend-go/internal/interfaces/http/server.go index 9708a21..ab419f0 100755 --- a/backend-go/internal/interfaces/http/server.go +++ b/backend-go/internal/interfaces/http/server.go @@ -1,7 +1,7 @@ package http import ( - "actatempus_backend/internal/infrastructure/persistence/config" + "actatempus_backend/internal/infrastructure/config" "fmt" "net/http" ) diff --git a/backend-go/internal/utils/utils.go b/backend-go/internal/utils/utils.go new file mode 100644 index 0000000..7305d13 --- /dev/null +++ b/backend-go/internal/utils/utils.go @@ -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 +} diff --git a/_common/db_client/prisma/prisma-query-engine b/backend-go/prisma/prisma-query-engine similarity index 100% rename from _common/db_client/prisma/prisma-query-engine rename to backend-go/prisma/prisma-query-engine diff --git a/_common/db_client/prisma/schema.prisma b/backend-go/prisma/schema.prisma similarity index 57% rename from _common/db_client/prisma/schema.prisma rename to backend-go/prisma/schema.prisma index ca9622a..0f7edff 100755 --- a/_common/db_client/prisma/schema.prisma +++ b/backend-go/prisma/schema.prisma @@ -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