diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index fe33874..5c9590e 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -9,56 +9,56 @@ on: jobs: build-and-test: runs-on: ubuntu-latest - + steps: - uses: actions/checkout@v4 - + - name: Setup Java uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: '17' - + - name: Install Leiningen uses: DeLaGuardo/setup-clojure@12.1 with: lein: 2.10.0 - + - name: Install dependencies run: lein deps - + - name: Run tests run: lein test - + - name: Run architectural fitness checks run: ./bin/check-architecture.sh - + - name: Build uberjar run: lein uberjar - + - name: Upload uberjar artifact uses: actions/upload-artifact@v4 with: name: walue-uberjar path: target/uberjar/walue-*-standalone.jar - + docker-build: needs: build-and-test runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/main' - + steps: - uses: actions/checkout@v4 - + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - + - name: Login to DockerHub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - + - name: Build and push uses: docker/build-push-action@v5 with: diff --git a/README.md b/README.md index 4182d38..cf03964 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Walue - Portfolio Evaluation Service -[![CI/CD Pipeline](https://github.com/yourusername/walue/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/yourusername/walue/actions/workflows/ci-cd.yml) -[![Architectural Fitness](https://img.shields.io/badge/Architectural%20Fitness-Checked-brightgreen.svg)](https://github.com/yourusername/walue/blob/main/src/walue/infra/fitness.clj) +[![CI/CD Pipeline](https://github.com/wagnerdevocelot/walue/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/wagnerdevocelot/walue/actions/workflows/ci-cd.yml) +[![Architectural Fitness](https://img.shields.io/badge/Architectural%20Fitness-Checked-brightgreen.svg)](https://github.com/wagnerdevocelot/walue/blob/main/src/walue/infra/fitness.clj) A Clojure web service that implements a portfolio evaluation system using Domain-Driven Design (DDD) and Hexagonal Architecture. diff --git a/src/walue/adapter/http_adapter.clj b/src/walue/adapter/http_adapter.clj index c3ed11b..182ddea 100644 --- a/src/walue/adapter/http_adapter.clj +++ b/src/walue/adapter/http_adapter.clj @@ -33,10 +33,10 @@ (cond (and (= uri "/api/evaluate") (= method :post)) (handle-evaluate-portfolio request evaluation-service logging-service) - + (and (= uri "/health") (= method :get)) (handle-health-check request) - + :else {:status 404 :body {:error "Not found"}})))) @@ -46,7 +46,7 @@ (let [start (System/currentTimeMillis) response (handler request) duration (- (System/currentTimeMillis) start)] - (logging-port/log-info logging-service + (logging-port/log-info logging-service (str "Request completed in " duration " ms with status " (:status response))) response))) diff --git a/src/walue/core.clj b/src/walue/core.clj index ad434d3..4011a16 100644 --- a/src/walue/core.clj +++ b/src/walue/core.clj @@ -37,7 +37,7 @@ (Integer/parseInt port-str) (catch NumberFormatException _ (let [logging-service (logging-port/->LoggingService)] - (logging-port/log-warn logging-service + (logging-port/log-warn logging-service (str "Invalid PORT environment variable value: " port-str " - using default 8080"))) 8080)))) diff --git a/src/walue/domain/evaluation.clj b/src/walue/domain/evaluation.clj index 020af2c..77b0b18 100644 --- a/src/walue/domain/evaluation.clj +++ b/src/walue/domain/evaluation.clj @@ -47,7 +47,7 @@ (defn calculate-asset-score "Calculate score for a single asset based on all criteria" [asset criteria] - (reduce + (reduce (fn [score criterion] (if (evaluate-criterion asset criterion) (+ score (or (:peso criterion) (get criterion "peso"))) @@ -58,7 +58,7 @@ (defn evaluate-portfolio "Main domain function that evaluates a portfolio based on criteria" [portfolio criteria] - (let [evaluated-assets (map + (let [evaluated-assets (map (fn [asset] {:ticker (or (:ticker asset) (get asset "ticker")) :score (calculate-asset-score asset criteria)}) diff --git a/src/walue/infra/fitness.clj b/src/walue/infra/fitness.clj index f747891..6d35840 100644 --- a/src/walue/infra/fitness.clj +++ b/src/walue/infra/fitness.clj @@ -50,7 +50,7 @@ files (get-clj-files src-dir) ns-deps (keep extract-namespace-and-deps files) violations (atom [])] - + (doseq [[ns-name deps] ns-deps] (let [src-layer (ns-to-layer ns-name)] (when src-layer @@ -59,12 +59,12 @@ (when (and dep-layer (not (contains? (get allowed-deps src-layer) dep-layer)) (not= src-layer dep-layer)) - (swap! violations conj + (swap! violations conj {:source ns-name :source-layer src-layer :dependency dep :dependency-layer dep-layer}))))))) - + {:valid? (empty? @violations) :violations @violations})) @@ -75,16 +75,16 @@ files (get-clj-files src-dir) ns-deps (keep extract-namespace-and-deps files) violations (atom [])] - + (doseq [[ns-name deps] ns-deps] (doseq [dep deps] (let [dep-str (str dep)] (when-not (or (str/starts-with? dep-str "clojure.") (str/starts-with? dep-str "walue.domain")) - (swap! violations conj + (swap! violations conj {:namespace ns-name :external-dependency dep}))))) - + {:valid? (empty? @violations) :violations @violations})) @@ -97,12 +97,12 @@ visited (atom #{}) path (atom []) cycles (atom [])] - + (letfn [(dfs [ns] (when-not (contains? @visited ns) (swap! visited conj ns) (swap! path conj ns) - + (doseq [dep (get ns-deps ns)] (if (some #{dep} @path) ;; Encontrou um ciclo @@ -111,13 +111,13 @@ (swap! cycles conj cycle)) ;; Continue DFS (dfs dep))) - + (swap! path pop)))] - + (doseq [ns (keys ns-deps)] (reset! path []) (dfs ns))) - + {:valid? (empty? @cycles) :cycles @cycles})) @@ -127,15 +127,15 @@ (let [src-dir "src/walue/port" files (get-clj-files src-dir) violations (atom [])] - + (doseq [file files] (let [content (slurp file) file-name (.getName file)] (when-not (re-find #"defprotocol" content) - (swap! violations conj + (swap! violations conj {:file file-name :reason "Port file should define at least one protocol"})))) - + {:valid? (empty? @violations) :violations @violations})) @@ -145,18 +145,18 @@ (let [src-dir "src/walue/adapter" files (get-clj-files src-dir) violations (atom [])] - + (doseq [file files] (let [content (slurp file) file-name (.getName file) - has-port-dependency (or + has-port-dependency (or (re-find #"require.*\[walue\.port" content) (re-find #"walue\.port\.[a-z-]+\s+:as" content))] (when-not has-port-dependency - (swap! violations conj + (swap! violations conj {:file file-name :reason "Adapter should depend on at least one port"})))) - + {:valid? (empty? @violations) :violations @violations})) @@ -173,7 +173,7 @@ (:valid? circular-deps-result) (:valid? interface-result) (:valid? adapter-result))] - + {:all-valid? all-valid? :layer-dependencies layer-deps-result :domain-purity domain-purity-result @@ -186,9 +186,9 @@ [& args] (let [results (run-fitness-checks) all-valid? (:all-valid? results)] - + (println "\n=== Architectural Fitness Check Results ===\n") - + ;; Verifica dependências entre camadas (let [{:keys [valid? violations]} (:layer-dependencies results)] (println "Layer Dependencies Check:" (if valid? "PASSED ✓" "FAILED ✗")) @@ -197,7 +197,7 @@ (doseq [v violations] (println (str " - " (:source v) " (" (name (:source-layer v)) ") depends on " (:dependency v) " (" (name (:dependency-layer v)) ")"))))) - + ;; Verifica pureza do domínio (let [{:keys [valid? violations]} (:domain-purity results)] (println "\nDomain Purity Check:" (if valid? "PASSED ✓" "FAILED ✗")) @@ -205,7 +205,7 @@ (println " Violations:") (doseq [v violations] (println (str " - " (:namespace v) " depends on external " (:external-dependency v)))))) - + ;; Verifica dependências circulares (let [{:keys [valid? cycles]} (:circular-dependencies results)] (println "\nCircular Dependencies Check:" (if valid? "PASSED ✓" "FAILED ✗")) @@ -213,7 +213,7 @@ (println " Cycles detected:") (doseq [cycle cycles] (println (str " - " (str/join " -> " cycle)))))) - + ;; Verifica isolamento de interfaces (let [{:keys [valid? violations]} (:interface-isolation results)] (println "\nInterface Isolation Check:" (if valid? "PASSED ✓" "FAILED ✗")) @@ -221,7 +221,7 @@ (println " Violations:") (doseq [v violations] (println (str " - " (:file v) ": " (:reason v)))))) - + ;; Verifica implementação de adaptadores (let [{:keys [valid? violations]} (:adapter-implementation results)] (println "\nAdapter Implementation Check:" (if valid? "PASSED ✓" "FAILED ✗")) @@ -229,7 +229,7 @@ (println " Violations:") (doseq [v violations] (println (str " - " (:file v) ": " (:reason v)))))) - + (println "\nOverall Result:" (if all-valid? "PASSED ✓" "FAILED ✗")) - + (System/exit (if all-valid? 0 1)))) \ No newline at end of file diff --git a/src/walue/infra/metrics.clj b/src/walue/infra/metrics.clj index 3ea03e1..3226a35 100644 --- a/src/walue/infra/metrics.clj +++ b/src/walue/infra/metrics.clj @@ -27,7 +27,7 @@ value-fn) (defn get-all-metrics [] - (reduce-kv + (reduce-kv (fn [acc k v] (let [value (cond (= (:type v) :counter) (get-counter-value (:value v)) diff --git a/src/walue/port/logging_port.clj b/src/walue/port/logging_port.clj index 9df3bfe..4b06a8e 100644 --- a/src/walue/port/logging_port.clj +++ b/src/walue/port/logging_port.clj @@ -9,7 +9,7 @@ (defrecord LoggingService [] LoggingPort - (log-info [_ message] + (log-info [_ message] (println (str "{\"level\":\"info\",\"message\":\"" message "\"}"))) (log-warn [_ message] (println (str "{\"level\":\"warn\",\"message\":\"" message "\"}"))) diff --git a/test/walue/adapter/http_adapter_test.clj b/test/walue/adapter/http_adapter_test.clj index 1a931f3..0dfe4de 100644 --- a/test/walue/adapter/http_adapter_test.clj +++ b/test/walue/adapter/http_adapter_test.clj @@ -44,7 +44,7 @@ (mock/json-body request-body)) response (app request) body (parse-json-body response)] - + (is (= 200 (:status response))) (is (vector? (:resultado body))) (is (= 2 (count (:resultado body)))) @@ -60,7 +60,7 @@ request (mock/request :get "/health") response (app request) body (parse-json-body response)] - + (is (= 200 (:status response))) (is (= "UP" (:status body))))) @@ -72,6 +72,6 @@ (mock/json-body {:invalid "request"})) response (app request) body (parse-json-body response)] - + (is (= 400 (:status response))) (is (contains? body :error)))))) \ No newline at end of file diff --git a/test/walue/infra/fitness_test.clj b/test/walue/infra/fitness_test.clj index 8d2ad1c..4f1dc3f 100644 --- a/test/walue/infra/fitness_test.clj +++ b/test/walue/infra/fitness_test.clj @@ -5,30 +5,30 @@ (deftest test-architectural-fitness (testing "Layer dependencies compliance" (let [result (fitness/check-layer-dependencies)] - (is (:valid? result) - (str "Layer dependencies violated: " + (is (:valid? result) + (str "Layer dependencies violated: " (pr-str (:violations result)))))) - + (testing "Domain purity" (let [result (fitness/check-domain-purity)] - (is (:valid? result) - (str "Domain purity violated: " + (is (:valid? result) + (str "Domain purity violated: " (pr-str (:violations result)))))) - + (testing "No circular dependencies" (let [result (fitness/check-circular-dependencies)] - (is (:valid? result) - (str "Circular dependencies detected: " + (is (:valid? result) + (str "Circular dependencies detected: " (pr-str (:cycles result)))))) - + (testing "Interface isolation" (let [result (fitness/check-interface-isolation)] - (is (:valid? result) - (str "Interface isolation violated: " + (is (:valid? result) + (str "Interface isolation violated: " (pr-str (:violations result)))))) - + (testing "Adapter implementation" (let [result (fitness/check-adapter-implementation)] - (is (:valid? result) - (str "Adapter implementation violated: " + (is (:valid? result) + (str "Adapter implementation violated: " (pr-str (:violations result))))))) \ No newline at end of file