├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── LICENSE ├── README.md ├── doc └── intro.md ├── docker-compose.yml ├── http └── tweets.http ├── project.clj ├── resources ├── database │ └── tables.sql └── log4j.properties ├── src └── twitter_api │ ├── core.clj │ ├── db │ ├── db.clj │ └── sql │ │ └── queries.sql │ ├── handlers.clj │ └── tweets │ ├── database.clj │ └── validation.clj ├── test └── twitter_api │ ├── core_test.clj │ └── validation_test.clj └── twitter-api.iml /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | jobs: 10 | build-linux: 11 | name: Build 12 | runs-on: ubuntu-20.04 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: DeLaGuardo/setup-graalvm@2.0 16 | with: 17 | graalvm-version: '19.3.1.java11' 18 | - uses: DeLaGuardo/setup-clojure@2.0 19 | with: 20 | tools-deps: '1.10.1.469' 21 | - run: clojure -Sdescribe 22 | - run: lein uberjar 23 | - uses: actions/upload-artifact@master 24 | name: Generating artifact 25 | with: 26 | name: clojure-twitter-api 27 | path: ./target/uberjar/twitter-api-0.1.0-SNAPSHOT-standalone.jar 28 | 29 | tests-linux: 30 | name: Run tests 31 | runs-on: ubuntu-20.04 32 | steps: 33 | - uses: actions/checkout@v1 34 | - uses: DeLaGuardo/setup-graalvm@2.0 35 | with: 36 | graalvm-version: '19.3.1.java11' 37 | - uses: DeLaGuardo/setup-clojure@2.0 38 | with: 39 | tools-deps: '1.10.1.469' 40 | - run: clojure -Sdescribe 41 | - run: lein test 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | profiles.clj 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | /.lein-* 10 | /.nrepl-port 11 | /.prepl-port 12 | .hgignore 13 | .hg/ 14 | .idea -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/). 3 | 4 | ## [Unreleased] 5 | ### Changed 6 | - Add a new arity to `make-widget-async` to provide a different widget shape. 7 | 8 | ## [0.1.1] - 2020-08-14 9 | ### Changed 10 | - Documentation on how to make the widgets. 11 | 12 | ### Removed 13 | - `make-widget-sync` - we're all async, all the time. 14 | 15 | ### Fixed 16 | - Fixed widget maker to keep working when daylight savings switches over. 17 | 18 | ## 0.1.0 - 2020-08-14 19 | ### Added 20 | - Files from the new template. 21 | - Widget maker public API - `make-widget-sync`. 22 | 23 | [Unreleased]: https://github.com/your-name/twitter-api/compare/0.1.1...HEAD 24 | [0.1.1]: https://github.com/your-name/twitter-api/compare/0.1.0...0.1.1 25 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM clojure:openjdk-17-lein 2 | COPY . /usr/src/app 3 | WORKDIR /usr/src/app 4 | RUN ls 5 | RUN lein uberjar 6 | CMD java -jar target/uberjar/clojure-twitter-api -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 2.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE 4 | PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION 5 | OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial content 12 | Distributed under this Agreement, and 13 | 14 | b) in the case of each subsequent Contributor: 15 | i) changes to the Program, and 16 | ii) additions to the Program; 17 | where such changes and/or additions to the Program originate from 18 | and are Distributed by that particular Contributor. A Contribution 19 | "originates" from a Contributor if it was added to the Program by 20 | such Contributor itself or anyone acting on such Contributor's behalf. 21 | Contributions do not include changes or additions to the Program that 22 | are not Modified Works. 23 | 24 | "Contributor" means any person or entity that Distributes the Program. 25 | 26 | "Licensed Patents" mean patent claims licensable by a Contributor which 27 | are necessarily infringed by the use or sale of its Contribution alone 28 | or when combined with the Program. 29 | 30 | "Program" means the Contributions Distributed in accordance with this 31 | Agreement. 32 | 33 | "Recipient" means anyone who receives the Program under this Agreement 34 | or any Secondary License (as applicable), including Contributors. 35 | 36 | "Derivative Works" shall mean any work, whether in Source Code or other 37 | form, that is based on (or derived from) the Program and for which the 38 | editorial revisions, annotations, elaborations, or other modifications 39 | represent, as a whole, an original work of authorship. 40 | 41 | "Modified Works" shall mean any work in Source Code or other form that 42 | results from an addition to, deletion from, or modification of the 43 | contents of the Program, including, for purposes of clarity any new file 44 | in Source Code form that contains any contents of the Program. Modified 45 | Works shall not include works that contain only declarations, 46 | interfaces, types, classes, structures, or files of the Program solely 47 | in each case in order to link to, bind by name, or subclass the Program 48 | or Modified Works thereof. 49 | 50 | "Distribute" means the acts of a) distributing or b) making available 51 | in any manner that enables the transfer of a copy. 52 | 53 | "Source Code" means the form of a Program preferred for making 54 | modifications, including but not limited to software source code, 55 | documentation source, and configuration files. 56 | 57 | "Secondary License" means either the GNU General Public License, 58 | Version 2.0, or any later versions of that license, including any 59 | exceptions or additional permissions as identified by the initial 60 | Contributor. 61 | 62 | 2. GRANT OF RIGHTS 63 | 64 | a) Subject to the terms of this Agreement, each Contributor hereby 65 | grants Recipient a non-exclusive, worldwide, royalty-free copyright 66 | license to reproduce, prepare Derivative Works of, publicly display, 67 | publicly perform, Distribute and sublicense the Contribution of such 68 | Contributor, if any, and such Derivative Works. 69 | 70 | b) Subject to the terms of this Agreement, each Contributor hereby 71 | grants Recipient a non-exclusive, worldwide, royalty-free patent 72 | license under Licensed Patents to make, use, sell, offer to sell, 73 | import and otherwise transfer the Contribution of such Contributor, 74 | if any, in Source Code or other form. This patent license shall 75 | apply to the combination of the Contribution and the Program if, at 76 | the time the Contribution is added by the Contributor, such addition 77 | of the Contribution causes such combination to be covered by the 78 | Licensed Patents. The patent license shall not apply to any other 79 | combinations which include the Contribution. No hardware per se is 80 | licensed hereunder. 81 | 82 | c) Recipient understands that although each Contributor grants the 83 | licenses to its Contributions set forth herein, no assurances are 84 | provided by any Contributor that the Program does not infringe the 85 | patent or other intellectual property rights of any other entity. 86 | Each Contributor disclaims any liability to Recipient for claims 87 | brought by any other entity based on infringement of intellectual 88 | property rights or otherwise. As a condition to exercising the 89 | rights and licenses granted hereunder, each Recipient hereby 90 | assumes sole responsibility to secure any other intellectual 91 | property rights needed, if any. For example, if a third party 92 | patent license is required to allow Recipient to Distribute the 93 | Program, it is Recipient's responsibility to acquire that license 94 | before distributing the Program. 95 | 96 | d) Each Contributor represents that to its knowledge it has 97 | sufficient copyright rights in its Contribution, if any, to grant 98 | the copyright license set forth in this Agreement. 99 | 100 | e) Notwithstanding the terms of any Secondary License, no 101 | Contributor makes additional grants to any Recipient (other than 102 | those set forth in this Agreement) as a result of such Recipient's 103 | receipt of the Program under the terms of a Secondary License 104 | (if permitted under the terms of Section 3). 105 | 106 | 3. REQUIREMENTS 107 | 108 | 3.1 If a Contributor Distributes the Program in any form, then: 109 | 110 | a) the Program must also be made available as Source Code, in 111 | accordance with section 3.2, and the Contributor must accompany 112 | the Program with a statement that the Source Code for the Program 113 | is available under this Agreement, and informs Recipients how to 114 | obtain it in a reasonable manner on or through a medium customarily 115 | used for software exchange; and 116 | 117 | b) the Contributor may Distribute the Program under a license 118 | different than this Agreement, provided that such license: 119 | i) effectively disclaims on behalf of all other Contributors all 120 | warranties and conditions, express and implied, including 121 | warranties or conditions of title and non-infringement, and 122 | implied warranties or conditions of merchantability and fitness 123 | for a particular purpose; 124 | 125 | ii) effectively excludes on behalf of all other Contributors all 126 | liability for damages, including direct, indirect, special, 127 | incidental and consequential damages, such as lost profits; 128 | 129 | iii) does not attempt to limit or alter the recipients' rights 130 | in the Source Code under section 3.2; and 131 | 132 | iv) requires any subsequent distribution of the Program by any 133 | party to be under a license that satisfies the requirements 134 | of this section 3. 135 | 136 | 3.2 When the Program is Distributed as Source Code: 137 | 138 | a) it must be made available under this Agreement, or if the 139 | Program (i) is combined with other material in a separate file or 140 | files made available under a Secondary License, and (ii) the initial 141 | Contributor attached to the Source Code the notice described in 142 | Exhibit A of this Agreement, then the Program may be made available 143 | under the terms of such Secondary Licenses, and 144 | 145 | b) a copy of this Agreement must be included with each copy of 146 | the Program. 147 | 148 | 3.3 Contributors may not remove or alter any copyright, patent, 149 | trademark, attribution notices, disclaimers of warranty, or limitations 150 | of liability ("notices") contained within the Program from any copy of 151 | the Program which they Distribute, provided that Contributors may add 152 | their own appropriate notices. 153 | 154 | 4. COMMERCIAL DISTRIBUTION 155 | 156 | Commercial distributors of software may accept certain responsibilities 157 | with respect to end users, business partners and the like. While this 158 | license is intended to facilitate the commercial use of the Program, 159 | the Contributor who includes the Program in a commercial product 160 | offering should do so in a manner which does not create potential 161 | liability for other Contributors. Therefore, if a Contributor includes 162 | the Program in a commercial product offering, such Contributor 163 | ("Commercial Contributor") hereby agrees to defend and indemnify every 164 | other Contributor ("Indemnified Contributor") against any losses, 165 | damages and costs (collectively "Losses") arising from claims, lawsuits 166 | and other legal actions brought by a third party against the Indemnified 167 | Contributor to the extent caused by the acts or omissions of such 168 | Commercial Contributor in connection with its distribution of the Program 169 | in a commercial product offering. The obligations in this section do not 170 | apply to any claims or Losses relating to any actual or alleged 171 | intellectual property infringement. In order to qualify, an Indemnified 172 | Contributor must: a) promptly notify the Commercial Contributor in 173 | writing of such claim, and b) allow the Commercial Contributor to control, 174 | and cooperate with the Commercial Contributor in, the defense and any 175 | related settlement negotiations. The Indemnified Contributor may 176 | participate in any such claim at its own expense. 177 | 178 | For example, a Contributor might include the Program in a commercial 179 | product offering, Product X. That Contributor is then a Commercial 180 | Contributor. If that Commercial Contributor then makes performance 181 | claims, or offers warranties related to Product X, those performance 182 | claims and warranties are such Commercial Contributor's responsibility 183 | alone. Under this section, the Commercial Contributor would have to 184 | defend claims against the other Contributors related to those performance 185 | claims and warranties, and if a court requires any other Contributor to 186 | pay any damages as a result, the Commercial Contributor must pay 187 | those damages. 188 | 189 | 5. NO WARRANTY 190 | 191 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 192 | PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" 193 | BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 194 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF 195 | TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR 196 | PURPOSE. Each Recipient is solely responsible for determining the 197 | appropriateness of using and distributing the Program and assumes all 198 | risks associated with its exercise of rights under this Agreement, 199 | including but not limited to the risks and costs of program errors, 200 | compliance with applicable laws, damage to or loss of data, programs 201 | or equipment, and unavailability or interruption of operations. 202 | 203 | 6. DISCLAIMER OF LIABILITY 204 | 205 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT 206 | PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS 207 | SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 208 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 209 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 210 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 211 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 212 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE 213 | POSSIBILITY OF SUCH DAMAGES. 214 | 215 | 7. GENERAL 216 | 217 | If any provision of this Agreement is invalid or unenforceable under 218 | applicable law, it shall not affect the validity or enforceability of 219 | the remainder of the terms of this Agreement, and without further 220 | action by the parties hereto, such provision shall be reformed to the 221 | minimum extent necessary to make such provision valid and enforceable. 222 | 223 | If Recipient institutes patent litigation against any entity 224 | (including a cross-claim or counterclaim in a lawsuit) alleging that the 225 | Program itself (excluding combinations of the Program with other software 226 | or hardware) infringes such Recipient's patent(s), then such Recipient's 227 | rights granted under Section 2(b) shall terminate as of the date such 228 | litigation is filed. 229 | 230 | All Recipient's rights under this Agreement shall terminate if it 231 | fails to comply with any of the material terms or conditions of this 232 | Agreement and does not cure such failure in a reasonable period of 233 | time after becoming aware of such noncompliance. If all Recipient's 234 | rights under this Agreement terminate, Recipient agrees to cease use 235 | and distribution of the Program as soon as reasonably practicable. 236 | However, Recipient's obligations under this Agreement and any licenses 237 | granted by Recipient relating to the Program shall continue and survive. 238 | 239 | Everyone is permitted to copy and distribute copies of this Agreement, 240 | but in order to avoid inconsistency the Agreement is copyrighted and 241 | may only be modified in the following manner. The Agreement Steward 242 | reserves the right to publish new versions (including revisions) of 243 | this Agreement from time to time. No one other than the Agreement 244 | Steward has the right to modify this Agreement. The Eclipse Foundation 245 | is the initial Agreement Steward. The Eclipse Foundation may assign the 246 | responsibility to serve as the Agreement Steward to a suitable separate 247 | entity. Each new version of the Agreement will be given a distinguishing 248 | version number. The Program (including Contributions) may always be 249 | Distributed subject to the version of the Agreement under which it was 250 | received. In addition, after a new version of the Agreement is published, 251 | Contributor may elect to Distribute the Program (including its 252 | Contributions) under the new version. 253 | 254 | Except as expressly stated in Sections 2(a) and 2(b) above, Recipient 255 | receives no rights or licenses to the intellectual property of any 256 | Contributor under this Agreement, whether expressly, by implication, 257 | estoppel or otherwise. All rights in the Program not expressly granted 258 | under this Agreement are reserved. Nothing in this Agreement is intended 259 | to be enforceable by any entity that is not a Contributor or Recipient. 260 | No third-party beneficiary rights are created under this Agreement. 261 | 262 | Exhibit A - Form of Secondary Licenses Notice 263 | 264 | "This Source Code may also be made available under the following 265 | Secondary Licenses when the conditions for such availability set forth 266 | in the Eclipse Public License, v. 2.0 are satisfied: GNU General Public 267 | License as published by the Free Software Foundation, either version 2 268 | of the License, or (at your option) any later version, with the GNU 269 | Classpath Exception which is available at 270 | https://www.gnu.org/software/classpath/license.html." 271 | 272 | Simply including a copy of this Agreement, including this Exhibit A 273 | is not sufficient to license the Source Code under Secondary Licenses. 274 | 275 | If it is not possible or desirable to put the notice in a particular 276 | file, then You may include the notice in a location (such as a LICENSE 277 | file in a relevant directory) where a recipient would be likely to 278 | look for such a notice. 279 | 280 | You may add additional accurate notices of copyright ownership. 281 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # twitter-api ![](https://github.com/filhodanuvem/clojure-twitter-api/workflows/CI/badge.svg) 2 | 3 | Well, this is just a project to study clojure. If you have any questions you can ping me via social media or either creating an issue. -------------------------------------------------------------------------------- /doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to twitter-api 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | db: 4 | image: postgres 5 | restart: always 6 | environment: 7 | POSTGRES_PASSWORD: "twitter" 8 | ports: 9 | - 5432:5432 10 | volumes: 11 | - ./resources/database:/docker-entrypoint-initdb.d 12 | app: 13 | build: . 14 | depends_on: 15 | - db 16 | ports: 17 | - 3000:3000 18 | 19 | unit-tests: 20 | image: clojure:openjdk-17-lein 21 | command: lein test 22 | volumes: 23 | - .:/tmp 24 | 25 | 26 | -------------------------------------------------------------------------------- /http/tweets.http: -------------------------------------------------------------------------------- 1 | POST http://localhost:3000/tweets 2 | Content-Type: application/json 3 | 4 | { 5 | "body": "test", 6 | "username" : "@filhodanuvem" 7 | } -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject twitter-api "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0" 5 | :url "https://www.eclipse.org/legal/epl-2.0/"} 6 | :dependencies [[org.clojure/clojure "1.10.1"] 7 | [com.layerware/hugsql "0.5.1"] 8 | [com.layerware/hugsql-adapter-next-jdbc "0.5.1"] 9 | [org.postgresql/postgresql "42.2.2"] 10 | [compojure "1.6.1"] 11 | [http-kit "2.3.0"] 12 | [ring/ring-defaults "0.3.2"] 13 | [ring/ring-json "0.5.0"] 14 | [org.clojure/tools.logging "0.2.3"] 15 | [log4j/log4j "1.2.16"] 16 | [funcool/clojure.jdbc "0.9.0"] 17 | [org.clojure/data.json "0.2.6"]] 18 | :main ^:skip-aot twitter-api.core 19 | :uberjar-name "clojure-twitter-api" 20 | :target-path "target/%s" 21 | :profiles {:uberjar {:aot :all 22 | :jvm-opts ["-Dclojure.compiler.direct-linking=true"]}}) 23 | -------------------------------------------------------------------------------- /resources/database/tables.sql: -------------------------------------------------------------------------------- 1 | -- ------------------------------------------------------------- 2 | -- TablePlus 3.1.2(296) 3 | -- 4 | -- https://tableplus.com/ 5 | -- 6 | -- Database: postgres 7 | -- Generation Time: 2021-04-03 15:38:20.3830 8 | -- ------------------------------------------------------------- 9 | 10 | 11 | -- This script only contains the table creation statements and does not fully represent the table in the database. It's still missing: indices, triggers. Do not use it as a backup. 12 | 13 | -- Table Definition 14 | CREATE TABLE "public"."tweets" ( 15 | "id" uuid NOT NULL, 16 | "body" text NOT NULL, 17 | "created_at" timestamp, 18 | "username" varchar NOT NULL, 19 | PRIMARY KEY ("id") 20 | ); 21 | 22 | -------------------------------------------------------------------------------- /resources/log4j.properties: -------------------------------------------------------------------------------- 1 | # Based on the example properties given at http://logging.apache.org/log4j/1.2/manual.html 2 | # Set root logger level to DEBUG and its only appender to A1. 3 | log4j.rootLogger=DEBUG, A1 4 | 5 | # A1 is set to be a ConsoleAppender. 6 | log4j.appender.A1=org.apache.log4j.ConsoleAppender 7 | 8 | # A1 uses PatternLayout. 9 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout 10 | log4j.appender.A1.layout.ConversionPattern= %-5p %c - %m%n -------------------------------------------------------------------------------- /src/twitter_api/core.clj: -------------------------------------------------------------------------------- 1 | (ns twitter-api.core 2 | (:require [org.httpkit.server :as server] 3 | [compojure.core :refer :all] 4 | [compojure.route :as route] 5 | [ring.middleware.defaults :refer :all] 6 | [ring.middleware.json :as mj] 7 | [clojure.pprint :as pp] 8 | [clojure.string :as str] 9 | [clojure.data.json :as json] 10 | [clojure.tools.logging :as log] 11 | [twitter-api.handlers :refer :all] 12 | [twitter-api.tweets.database :as d]) 13 | (:gen-class)) 14 | 15 | (defroutes app-routes 16 | (POST "/tweets" [] (mj/wrap-json-body post-twitter-handler {:keywords? true :bigdecimals? true})) 17 | (GET "/tweets" [] get-twitter-handler)) 18 | 19 | (defn -main 20 | "I don't do a whole lot ... yet." 21 | [& args] 22 | (let [port 3000] 23 | (server/run-server (wrap-defaults #'app-routes api-defaults) {:port port}) 24 | (println (str "Running service on port " port)))) -------------------------------------------------------------------------------- /src/twitter_api/db/db.clj: -------------------------------------------------------------------------------- 1 | (ns twitter-api.db.db 2 | (:require [hugsql.core :as hugsql] 3 | [hugsql.adapter.next-jdbc :as next-adapter]) 4 | (:gen-class)) 5 | 6 | (def db 7 | {:subname "//db:5432/postgres" 8 | :host "db" 9 | :port "5432" 10 | :dbname "postgres" 11 | :subprotocol "postgres" 12 | :dbtype "postgres" 13 | :user "postgres" 14 | :password "twitter"}) 15 | 16 | (hugsql/def-db-fns 17 | "twitter_api/db/sql/queries.sql" 18 | {:adapter (next-adapter/hugsql-adapter-next-jdbc)}) 19 | 20 | (hugsql/def-sqlvec-fns 21 | "twitter_api/db/sql/queries.sql" 22 | {:adapter (next-adapter/hugsql-adapter-next-jdbc)}) 23 | 24 | -------------------------------------------------------------------------------- /src/twitter_api/db/sql/queries.sql: -------------------------------------------------------------------------------- 1 | -- A :result value of :n below will return affected rows: 2 | -- :name sql-insert-tweet :! :m 3 | -- :doc Persist a tweet on datbase 4 | insert into tweets (id, body, username, created_at) 5 | values ((:id)::uuid , :body, :username, NOW()) 6 | 7 | -- A :result value of :n below will return affected rows: 8 | -- :name sql-search-tweets-by-username :? 9 | -- :doc Find tweets from a specific username 10 | select id, body, username from tweets 11 | where username = :username -------------------------------------------------------------------------------- /src/twitter_api/handlers.clj: -------------------------------------------------------------------------------- 1 | (ns twitter-api.handlers 2 | (:require [org.httpkit.server :as server] 3 | [compojure.core :refer :all] 4 | [compojure.route :as route] 5 | [ring.middleware.defaults :refer :all] 6 | [clojure.pprint :as pp] 7 | [clojure.string :as str] 8 | [clojure.data.json :as json] 9 | [clojure.tools.logging :as log] 10 | [twitter-api.tweets.database :as d]) 11 | (:gen-class)) 12 | 13 | (defn post-twitter-handler 14 | [req] 15 | (let [tweet-json (:body req) 16 | saved (try 17 | (d/post-tweet tweet-json) 18 | (catch Exception e 19 | (do 20 | (log/error e) 21 | false)))] 22 | (log/info tweet-json) 23 | {:status (if saved 201 400) 24 | :headers {"Content-Type" "text/html"} 25 | :body (when (not saved) 26 | "error or saving tweet")})) 27 | 28 | (defn get-twitter-handler 29 | [req] 30 | (log/info req) 31 | (let 32 | [username (-> req 33 | :params 34 | :username) 35 | tweets (d/search-tweets-by-username username)] 36 | 37 | {:status 200 38 | :headers {"Content-type" "application/json"} 39 | :body (json/write-str tweets)})) -------------------------------------------------------------------------------- /src/twitter_api/tweets/database.clj: -------------------------------------------------------------------------------- 1 | (ns twitter-api.tweets.database 2 | (:require [twitter-api.db.db :refer :all] 3 | [cheshire.core :as json] 4 | [twitter-api.tweets.validation :as v]) 5 | (:import java.util.UUID) 6 | (:gen-class)) 7 | 8 | (defn post-tweet 9 | "Post a tweet to the audience" 10 | [tweet] 11 | (let [is-valid (v/validate-tweet tweet)] 12 | (when is-valid 13 | (sql-insert-tweet db (assoc tweet :id (java.util.UUID/randomUUID)))))) 14 | 15 | (defn search-tweets-by-username 16 | "Find tweets from a specific username" 17 | [username] 18 | (let [result (sql-search-tweets-by-username db {:username (str "@" username)})] 19 | (map #(assoc % :id (str (:id %))) result))) -------------------------------------------------------------------------------- /src/twitter_api/tweets/validation.clj: -------------------------------------------------------------------------------- 1 | (ns twitter-api.tweets.validation 2 | (:require [clojure.string :as str] 3 | [compojure.core :refer :all] 4 | [clojure.data.json :as json]) 5 | (:gen-class)) 6 | 7 | (def minimum-body-length 1) 8 | (def maximum-body-length 140) 9 | 10 | (defn validate-tweet 11 | "validate if a tweet has all the required data" 12 | [tweet] 13 | (and 14 | (not (empty? tweet)) 15 | (<= minimum-body-length (count (:body tweet)) maximum-body-length) 16 | (= 0 (str/index-of (:username tweet) "@")) 17 | (> (count (:username tweet)) 2))) 18 | -------------------------------------------------------------------------------- /test/twitter_api/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns twitter-api.core-test 2 | (:require [clojure.test :refer :all] 3 | [twitter-api.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 1 1)))) 8 | -------------------------------------------------------------------------------- /test/twitter_api/validation_test.clj: -------------------------------------------------------------------------------- 1 | (ns twitter-api.validation-test 2 | (:require [clojure.test :refer :all] 3 | [twitter-api.tweets.validation :refer :all])) 4 | 5 | (deftest validation-test 6 | (testing "Should be valid" 7 | (is (true? (validate-tweet {:body "this is my frist tweet" 8 | :username "@filhodanuvem"})))) 9 | 10 | (testing "Should fail when there is no body" 11 | (is (false? (validate-tweet {:body "" 12 | :username "@filhodanuvem"})))) 13 | 14 | (testing "Should fail when there is no username" 15 | (is (false? (validate-tweet {:body "this is my first tweet" 16 | :username ""}))))) -------------------------------------------------------------------------------- /twitter-api.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | --------------------------------------------------------------------------------