├── .gitignore ├── .nvimrc ├── LICENSE ├── README.md ├── docker-compose.yml ├── project.clj ├── resources └── migrations │ ├── 20171124121048-version1.down.sql │ ├── 20171124121048-version1.up.sql │ ├── 20171128151001-add-groups-table.down.sql │ ├── 20171128151001-add-groups-table.up.sql │ ├── 20171204183639-add-admin-column-to-groups-members.down.sql │ ├── 20171204183639-add-admin-column-to-groups-members.up.sql │ ├── 20171204200333-drop-building-from-venues.down.sql │ ├── 20171204200333-drop-building-from-venues.up.sql │ ├── 20180115065446-version2.down.sql │ ├── 20180115065446-version2.up.sql │ ├── 20180124090419-add-online-venue-id-to-meetups.down.sql │ └── 20180124090419-add-online-venue-id-to-meetups.up.sql ├── swagger-spec-v1.json └── swagger-spec-v2.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/vim,linux,macos,scala,emacs,clojure,windows,haskell,intellij,leiningen,sublimetext 2 | 3 | ### Clojure ### 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /lib/ 9 | /classes/ 10 | /target/ 11 | /checkouts/ 12 | .lein-deps-sum 13 | .lein-repl-history 14 | .lein-plugins/ 15 | .lein-failures 16 | .nrepl-port 17 | 18 | ### Emacs ### 19 | # -*- mode: gitignore; -*- 20 | *~ 21 | \#*\# 22 | /.emacs.desktop 23 | /.emacs.desktop.lock 24 | *.elc 25 | auto-save-list 26 | tramp 27 | .\#* 28 | 29 | # Org-mode 30 | .org-id-locations 31 | *_archive 32 | 33 | # flymake-mode 34 | *_flymake.* 35 | 36 | # eshell files 37 | /eshell/history 38 | /eshell/lastdir 39 | 40 | # elpa packages 41 | /elpa/ 42 | 43 | # reftex files 44 | *.rel 45 | 46 | # AUCTeX auto folder 47 | /auto/ 48 | 49 | # cask packages 50 | .cask/ 51 | dist/ 52 | 53 | # Flycheck 54 | flycheck_*.el 55 | 56 | # server auth directory 57 | /server/ 58 | 59 | # projectiles files 60 | .projectile 61 | projectile-bookmarks.eld 62 | 63 | # directory configuration 64 | .dir-locals.el 65 | 66 | # saveplace 67 | places 68 | 69 | # url cache 70 | url/cache/ 71 | 72 | # cedet 73 | ede-projects.el 74 | 75 | # smex 76 | smex-items 77 | 78 | # company-statistics 79 | company-statistics-cache.el 80 | 81 | # anaconda-mode 82 | anaconda-mode/ 83 | 84 | ### Haskell ### 85 | dist 86 | dist-* 87 | cabal-dev 88 | *.o 89 | *.hi 90 | *.chi 91 | *.chs.h 92 | *.dyn_o 93 | *.dyn_hi 94 | .hpc 95 | .hsenv 96 | .cabal-sandbox/ 97 | cabal.sandbox.config 98 | *.prof 99 | *.aux 100 | *.hp 101 | *.eventlog 102 | .stack-work/ 103 | cabal.project.local 104 | .HTF/ 105 | 106 | ### Intellij ### 107 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 108 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 109 | 110 | # User-specific stuff: 111 | .idea/**/workspace.xml 112 | .idea/**/tasks.xml 113 | .idea/dictionaries 114 | 115 | # Sensitive or high-churn files: 116 | .idea/**/dataSources/ 117 | .idea/**/dataSources.ids 118 | .idea/**/dataSources.xml 119 | .idea/**/dataSources.local.xml 120 | .idea/**/sqlDataSources.xml 121 | .idea/**/dynamic.xml 122 | .idea/**/uiDesigner.xml 123 | 124 | # Gradle: 125 | .idea/**/gradle.xml 126 | .idea/**/libraries 127 | 128 | # CMake 129 | cmake-build-debug/ 130 | 131 | # Mongo Explorer plugin: 132 | .idea/**/mongoSettings.xml 133 | 134 | ## File-based project format: 135 | *.iws 136 | 137 | ## Plugin-specific files: 138 | 139 | # IntelliJ 140 | /out/ 141 | 142 | # mpeltonen/sbt-idea plugin 143 | .idea_modules/ 144 | 145 | # JIRA plugin 146 | atlassian-ide-plugin.xml 147 | 148 | # Cursive Clojure plugin 149 | .idea/replstate.xml 150 | 151 | # Ruby plugin and RubyMine 152 | /.rakeTasks 153 | 154 | # Crashlytics plugin (for Android Studio and IntelliJ) 155 | com_crashlytics_export_strings.xml 156 | crashlytics.properties 157 | crashlytics-build.properties 158 | fabric.properties 159 | 160 | ### Intellij Patch ### 161 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 162 | 163 | # *.iml 164 | # modules.xml 165 | # .idea/misc.xml 166 | # *.ipr 167 | 168 | # Sonarlint plugin 169 | .idea/sonarlint 170 | 171 | ### Leiningen ### 172 | 173 | ### Linux ### 174 | 175 | # temporary files which can be created if a process still has a handle open of a deleted file 176 | .fuse_hidden* 177 | 178 | # KDE directory preferences 179 | .directory 180 | 181 | # Linux trash folder which might appear on any partition or disk 182 | .Trash-* 183 | 184 | # .nfs files are created when an open file is removed but is still being accessed 185 | .nfs* 186 | 187 | ### macOS ### 188 | *.DS_Store 189 | .AppleDouble 190 | .LSOverride 191 | 192 | # Icon must end with two \r 193 | Icon 194 | 195 | # Thumbnails 196 | ._* 197 | 198 | # Files that might appear in the root of a volume 199 | .DocumentRevisions-V100 200 | .fseventsd 201 | .Spotlight-V100 202 | .TemporaryItems 203 | .Trashes 204 | .VolumeIcon.icns 205 | .com.apple.timemachine.donotpresent 206 | 207 | # Directories potentially created on remote AFP share 208 | .AppleDB 209 | .AppleDesktop 210 | Network Trash Folder 211 | Temporary Items 212 | .apdisk 213 | 214 | ### Scala ### 215 | *.log 216 | 217 | ### SublimeText ### 218 | # cache files for sublime text 219 | *.tmlanguage.cache 220 | *.tmPreferences.cache 221 | *.stTheme.cache 222 | 223 | # workspace files are user-specific 224 | *.sublime-workspace 225 | 226 | # project files should be checked into the repository, unless a significant 227 | # proportion of contributors will probably not be using SublimeText 228 | # *.sublime-project 229 | 230 | # sftp configuration file 231 | sftp-config.json 232 | 233 | # Package control specific files 234 | Package Control.last-run 235 | Package Control.ca-list 236 | Package Control.ca-bundle 237 | Package Control.system-ca-bundle 238 | Package Control.cache/ 239 | Package Control.ca-certs/ 240 | Package Control.merged-ca-bundle 241 | Package Control.user-ca-bundle 242 | oscrypto-ca-bundle.crt 243 | bh_unicode_properties.cache 244 | 245 | # Sublime-github package stores a github token in this file 246 | # https://packagecontrol.io/packages/sublime-github 247 | GitHub.sublime-settings 248 | 249 | ### Vim ### 250 | # swap 251 | [._]*.s[a-v][a-z] 252 | [._]*.sw[a-p] 253 | [._]s[a-v][a-z] 254 | [._]sw[a-p] 255 | # session 256 | Session.vim 257 | # temporary 258 | .netrwhist 259 | # auto-generated tag files 260 | tags 261 | 262 | ### Windows ### 263 | # Windows thumbnail cache files 264 | Thumbs.db 265 | ehthumbs.db 266 | ehthumbs_vista.db 267 | 268 | # Folder config file 269 | Desktop.ini 270 | 271 | # Recycle Bin used on file shares 272 | $RECYCLE.BIN/ 273 | 274 | # Windows Installer files 275 | *.cab 276 | *.msi 277 | *.msm 278 | *.msp 279 | 280 | # Windows shortcuts 281 | *.lnk 282 | 283 | # End of https://www.gitignore.io/api/vim,linux,macos,scala,emacs,clojure,windows,haskell,intellij,leiningen,sublimetext 284 | -------------------------------------------------------------------------------- /.nvimrc: -------------------------------------------------------------------------------- 1 | :set spelllang=en,cjk 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | 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 code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' 19 | from a Contributor if it was added to the Program by such Contributor 20 | itself or anyone acting on such Contributor's behalf. Contributions do not 21 | include additions to the Program which: (i) are separate modules of 22 | software distributed in conjunction with the Program under their own 23 | license agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this 32 | Agreement. 33 | 34 | "Recipient" means anyone who receives the Program under this Agreement, 35 | including all Contributors. 36 | 37 | 2. GRANT OF RIGHTS 38 | a) Subject to the terms of this Agreement, each Contributor hereby grants 39 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 40 | reproduce, prepare derivative works of, publicly display, publicly 41 | perform, distribute and sublicense the Contribution of such Contributor, 42 | if any, and such derivative works, in source code and object code form. 43 | b) Subject to the terms of this Agreement, each Contributor hereby grants 44 | Recipient a non-exclusive, worldwide, royalty-free patent license under 45 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 46 | transfer the Contribution of such Contributor, if any, in source code and 47 | object code form. This patent license shall apply to the combination of 48 | the Contribution and the Program if, at the time the Contribution is 49 | added by the Contributor, such addition of the Contribution causes such 50 | combination to be covered by the Licensed Patents. The patent license 51 | shall not apply to any other combinations which include the Contribution. 52 | No hardware per se is licensed hereunder. 53 | c) Recipient understands that although each Contributor grants the licenses 54 | to its Contributions set forth herein, no assurances are provided by any 55 | Contributor that the Program does not infringe the patent or other 56 | intellectual property rights of any other entity. Each Contributor 57 | disclaims any liability to Recipient for claims brought by any other 58 | entity based on infringement of intellectual property rights or 59 | otherwise. As a condition to exercising the rights and licenses granted 60 | hereunder, each Recipient hereby assumes sole responsibility to secure 61 | any other intellectual property rights needed, if any. For example, if a 62 | third party patent license is required to allow Recipient to distribute 63 | the Program, it is Recipient's responsibility to acquire that license 64 | before distributing the Program. 65 | d) Each Contributor represents that to its knowledge it has sufficient 66 | copyright rights in its Contribution, if any, to grant the copyright 67 | license set forth in this Agreement. 68 | 69 | 3. REQUIREMENTS 70 | 71 | A Contributor may choose to distribute the Program in object code form under 72 | its own license agreement, provided that: 73 | 74 | a) it complies with the terms and conditions of this Agreement; and 75 | b) its license agreement: 76 | i) effectively disclaims on behalf of all Contributors all warranties 77 | and conditions, express and implied, including warranties or 78 | conditions of title and non-infringement, and implied warranties or 79 | conditions of merchantability and fitness for a particular purpose; 80 | ii) effectively excludes on behalf of all Contributors all liability for 81 | damages, including direct, indirect, special, incidental and 82 | consequential damages, such as lost profits; 83 | iii) states that any provisions which differ from this Agreement are 84 | offered by that Contributor alone and not by any other party; and 85 | iv) states that source code for the Program is available from such 86 | Contributor, and informs licensees how to obtain it in a reasonable 87 | manner on or through a medium customarily used for software exchange. 88 | 89 | When the Program is made available in source code form: 90 | 91 | a) it must be made available under this Agreement; and 92 | b) a copy of this Agreement must be included with each copy of the Program. 93 | Contributors may not remove or alter any copyright notices contained 94 | within the Program. 95 | 96 | Each Contributor must identify itself as the originator of its Contribution, 97 | if 98 | any, in a manner that reasonably allows subsequent Recipients to identify the 99 | originator of the Contribution. 100 | 101 | 4. COMMERCIAL DISTRIBUTION 102 | 103 | Commercial distributors of software may accept certain responsibilities with 104 | respect to end users, business partners and the like. While this license is 105 | intended to facilitate the commercial use of the Program, the Contributor who 106 | includes the Program in a commercial product offering should do so in a manner 107 | which does not create potential liability for other Contributors. Therefore, 108 | if a Contributor includes the Program in a commercial product offering, such 109 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 110 | every other Contributor ("Indemnified Contributor") against any losses, 111 | damages and costs (collectively "Losses") arising from claims, lawsuits and 112 | other legal actions brought by a third party against the Indemnified 113 | Contributor to the extent caused by the acts or omissions of such Commercial 114 | Contributor in connection with its distribution of the Program in a commercial 115 | product offering. The obligations in this section do not apply to any claims 116 | or Losses relating to any actual or alleged intellectual property 117 | infringement. In order to qualify, an Indemnified Contributor must: 118 | a) promptly notify the Commercial Contributor in writing of such claim, and 119 | b) allow the Commercial Contributor to control, and cooperate with the 120 | Commercial Contributor in, the defense and any related settlement 121 | negotiations. The Indemnified Contributor may participate in any such claim at 122 | its own expense. 123 | 124 | For example, a Contributor might include the Program in a commercial product 125 | offering, Product X. That Contributor is then a Commercial Contributor. If 126 | that Commercial Contributor then makes performance claims, or offers 127 | warranties related to Product X, those performance claims and warranties are 128 | such Commercial Contributor's responsibility alone. Under this section, the 129 | Commercial Contributor would have to defend claims against the other 130 | Contributors related to those performance claims and warranties, and if a 131 | court requires any other Contributor to pay any damages as a result, the 132 | Commercial Contributor must pay those damages. 133 | 134 | 5. NO WARRANTY 135 | 136 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 137 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 138 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 140 | Recipient is solely responsible for determining the appropriateness of using 141 | and distributing the Program and assumes all risks associated with its 142 | exercise of rights under this Agreement , including but not limited to the 143 | risks and costs of program errors, compliance with applicable laws, damage to 144 | or loss of data, programs or equipment, and unavailability or interruption of 145 | operations. 146 | 147 | 6. DISCLAIMER OF LIABILITY 148 | 149 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 150 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 151 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 152 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 153 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 154 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 155 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 156 | OF SUCH DAMAGES. 157 | 158 | 7. GENERAL 159 | 160 | If any provision of this Agreement is invalid or unenforceable under 161 | applicable law, it shall not affect the validity or enforceability of the 162 | remainder of the terms of this Agreement, and without further action by the 163 | parties hereto, such provision shall be reformed to the minimum extent 164 | necessary to make such provision valid and enforceable. 165 | 166 | If Recipient institutes patent litigation against any entity (including a 167 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 168 | (excluding combinations of the Program with other software or hardware) 169 | infringes such Recipient's patent(s), then such Recipient's rights granted 170 | under Section 2(b) shall terminate as of the date such litigation is filed. 171 | 172 | All Recipient's rights under this Agreement shall terminate if it fails to 173 | comply with any of the material terms or conditions of this Agreement and does 174 | not cure such failure in a reasonable period of time after becoming aware of 175 | such noncompliance. If all Recipient's rights under this Agreement terminate, 176 | Recipient agrees to cease use and distribution of the Program as soon as 177 | reasonably practicable. However, Recipient's obligations under this Agreement 178 | and any licenses granted by Recipient relating to the Program shall continue 179 | and survive. 180 | 181 | Everyone is permitted to copy and distribute copies of this Agreement, but in 182 | order to avoid inconsistency the Agreement is copyrighted and may only be 183 | modified in the following manner. The Agreement Steward reserves the right to 184 | publish new versions (including revisions) of this Agreement from time to 185 | time. No one other than the Agreement Steward has the right to modify this 186 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 187 | Eclipse Foundation may assign the responsibility to serve as the Agreement 188 | Steward to a suitable separate entity. Each new version of the Agreement will 189 | be given a distinguishing version number. The Program (including 190 | Contributions) may always be distributed subject to the version of the 191 | Agreement under which it was received. In addition, after a new version of the 192 | Agreement is published, Contributor may elect to distribute the Program 193 | (including its Contributions) under the new version. Except as expressly 194 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 195 | licenses to the intellectual property of any Contributor under this Agreement, 196 | whether expressly, by implication, estoppel or otherwise. All rights in the 197 | Program not expressly granted under this Agreement are reserved. 198 | 199 | This Agreement is governed by the laws of the State of New York and the 200 | intellectual property laws of the United States of America. No party to this 201 | Agreement will bring a legal action under this Agreement more than one year 202 | after the cause of action arose. Each party waives its rights to a jury trial in 203 | any resulting litigation. 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | 4 | **お知らせ** 5 | 6 | 12/12開催のclj_nakano#2では仕様第一版とその実装を紹介し、仕様第二版はclj_nakano#3で扱いたいと考えております。 7 | 8 | --- 9 | 10 | # situated-program-challenge 11 | Rich HickeyのClojure/Conj2017キーノートの考察として、外界の変化を言語ごとにどれだけ上手く扱えるか比較する 12 | 13 | ## Situated Programに求められる要件 14 | * 複数のシステムから構成されていること 15 | * 他者によって書かれたライブラリを含んでいること 16 | * データベースを使用していること 17 | * 時間の経過とともに変わる要件に柔軟に対応できること 18 | 19 | ## リポジトリ管理 20 | * 本リポジトリを自分のアカウントにフォークし、選択した言語で実装し、ソースコードをコミットする。 21 | * 本リポジトリにプルリクをする必要はなく、発表時はフォークしたリポジトリを用いる。 22 | 23 | ## 初期仕様 24 | * `version1` gitブランチに保持すること 25 | ### システム1 RESTサーバ 26 | * 本リポジトリ内で定義された `swagger-spec-v1.json` に基づいたエンドポイントを持つ。 27 | * HTTPリクエストを処理し、レスポンスを返す。 28 | * POSTデータをPostgreSQLに格納し、GETリクエストはPostgreSQLから読み出す。 29 | * テーブルスキーマは本プロジェクト内で `psql-v1.sql` として定義されている。 30 | 31 | ### システム2 RESTクライアント 32 | * コマンドラインプログラムであり、サーバURL, コマンド, 引数をとり、標準出力にデータを表示し、終了する。 33 | * イメージ 34 | 35 | ``` 36 | java -jar situated-program-scala.jar http://localhost:3000/person POST key1=value1 key2=value2 37 | ``` 38 | 39 | * システム1と通信し、RESTful POST/GETエンドポイントを呼び出す。 40 | * 本システムは、POSTとGETという2つのコマンドをコマンドライン引数にとる。 41 | * POSTコマンドは、コマンドライン引数から与えられたデータをPOSTで送信する。 42 | * GETはシステム1からデータを取得し、標準出力に表示する。 43 | 44 | ### PostgresSQLデータベース 45 | * PostgresSQLを起動する`docker-compose.yml`が本リポジトリで提供されているので、それを利用してサーバを起動する。 46 | * PSQLは所与の環境として考え、比較の対象外である。 47 | 48 | ## 仕様変更 49 | ### バージョン2変更点 50 | * オンライン上でのミートアップもサポートするため、`online-venue`(オンライン会場)エンティティが追加された。 51 | * `/groups/{group-id}/online-venues`エンドポイントが新設され、GET, POSTメソッドが追加された。 52 | * バージョン1から存在した`venue`(会場)と、オンライン会場は、ミートアップに両方とも紐付けて共存することができる。(会場からの中継を想定) 53 | * `venues`データベーステーブルに、下記のカラムが追加された。 54 | * `venue_type` ... PostgreSQLのEnumタイプ。取りうる値は`physical`か`online`。既存のデータはマイグレーション時に`physical`に設定される。 55 | * `url` ... オンライン会場用のURLを格納する。 56 | 57 | ### 実装 58 | * `version2` gitブランチに保持すること 59 | 60 | ### RESTエンドポイントの変更 61 | * 本リポジトリ内で定義された `swagger-spec-v2.json` に合わせ、エンドポイントを修正する。 62 | 63 | ### DBスキーマの変更 64 | * スキーマ変更を反映させるためには、下記コマンドを実行。 65 | ``` 66 | lein migratus up 20180115065446 67 | ``` 68 | 69 | 70 | ## 評価ポイント 71 | * 初期仕様におけるコードサイズ 72 | * サードパーティライブラリを最大限活用して、自前で書くコードを最小限に抑える 73 | * 仕様変更にかかる変更量を、`version1` `version2` ブランチ間のdiffファイルサイズで判定 74 | * 開発者にとって修正のやりさすさ、変更のわかりやすさも主観的に判定 75 | 76 | ## 開発環境 77 | * docker, docker-compose 78 | * MacOS: https://docs.docker.com/docker-for-mac/install/ 79 | * Windows: https://docs.docker.com/docker-for-windows/install/ 80 | * Linux: https://github.com/docker/compose/releases 81 | * 自分の選択した言語に必要な開発環境は追記してください。 82 | * dockerを使う必要はありません。 83 | 84 | ### Postgresqlサーバの起動 85 | 86 | ``` 87 | docker-compose up 88 | ``` 89 | * Daemonとして走らせたい場合は `-d` オプションを追加。 90 | * 5432番ポートでソケットを開いているので、ホストOSのpsql、または他のSQLツールで接続可能。 91 | * 接続情報は `docker-compose.yml` を参照のこと。 92 | * Dockerからpsqlを利用することも可能。 93 | 94 | ``` 95 | docker run -it -e PGPASSWORD=password123 -v $PWD:/project postgres:9.6 psql -U meetup -h docker.for.mac.localhost meetup 96 | ``` 97 | 98 | ### DBスキーマの設定 99 | 100 | * 仕様バージョン1のスキーマを設定するには、下記コマンドを実行。 101 | * Java9で動作しない問題があったが、migratusを1.0.3にバージョンアップし、問題が解消されていることを確認した。 102 | ``` 103 | lein migratus up 20171204200333 104 | ``` 105 | * 仕様バージョン2のスキーマにアップグレードするには、下記コマンドを実行。 106 | ``` 107 | lein migratus migrate 108 | ``` 109 | 110 | ### swagger.jsonの使用方法 111 | * [swagger editor](https://swagger.io/swagger-editor/)でswagger-spec-v1.jsonを開く。 112 | * スペックをUIで確認。 113 | * Swagger codegenでコード生成することも可能だが、必ずしもコードのクオリティが高くないので、見極めが必要。 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | postgres: 4 | image: postgres:9.6 5 | ports: 6 | - "5432:5432" 7 | volumes: 8 | - pgdata:/var/lib/postgresql/data 9 | environment: 10 | POSTGRES_PASSWORD: "password123" 11 | POSTGRES_USER: meetup 12 | POSTGRES_DB: meetup 13 | 14 | volumes: 15 | pgdata: 16 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject situated-program-challenge "0.1.0-SNAPSHOT" 2 | :description "Spec for Situated Program Challenge at clj-nakano #2 3 | https://clj-nakano.connpass.com/event/71884/" 4 | :url "https://github.com/clj-nakano/situated-program-challenge" 5 | :license {:name "Eclipse Public License" 6 | :url "http://www.eclipse.org/legal/epl-v10.html"} 7 | :min-lein-version "2.8.1" 8 | :dependencies [[org.clojure/clojure "1.8.0"] 9 | [migratus "1.0.3"] 10 | [com.fzakaria/slf4j-timbre "0.3.2"] 11 | [com.taoensso/timbre "4.10.0"] 12 | [org.postgresql/postgresql "9.4-1206-jdbc42"]] 13 | :migratus {:store :database 14 | :migration-dir "migrations" 15 | :init-script "init.sql" 16 | :db "postgres://meetup:password123@localhost:5432/meetup"} 17 | :profiles {:dev {:source-paths ["dev"] 18 | :plugins [[migratus-lein "0.5.4"]]}}) 19 | -------------------------------------------------------------------------------- /resources/migrations/20171124121048-version1.down.sql: -------------------------------------------------------------------------------- 1 | drop table meetups; 2 | --;; 3 | drop table members; 4 | --;; 5 | drop table venues; 6 | --;; 7 | drop table meetups_members; 8 | -------------------------------------------------------------------------------- /resources/migrations/20171124121048-version1.up.sql: -------------------------------------------------------------------------------- 1 | create table meetups ( 2 | id serial primary key, 3 | title text not null, 4 | start_at timestamp, 5 | end_at timestamp, 6 | venue_id int 7 | ); 8 | --;; 9 | create index meetups_title on meetups (title); 10 | --;; 11 | create table members ( 12 | id serial primary key, 13 | first_name text, 14 | last_name text, 15 | email text 16 | ); 17 | --;; 18 | create index members_email on members (email); 19 | --;; 20 | create table venues ( 21 | id serial primary key, 22 | name text, 23 | postal_code text, 24 | prefecture text, 25 | city text, 26 | street1 text, 27 | street2 text, 28 | building text 29 | ); 30 | --;; 31 | create index venues_name on venues (name); 32 | --;; 33 | create table meetups_members ( 34 | meetup_id int, 35 | member_id int, 36 | primary key (meetup_id, member_id) 37 | ); 38 | 39 | -------------------------------------------------------------------------------- /resources/migrations/20171128151001-add-groups-table.down.sql: -------------------------------------------------------------------------------- 1 | drop table groups_members; 2 | --;; 3 | alter table venues drop column group_id; 4 | --;; 5 | alter table meetups drop column group_id; 6 | --;; 7 | drop table groups; 8 | 9 | -------------------------------------------------------------------------------- /resources/migrations/20171128151001-add-groups-table.up.sql: -------------------------------------------------------------------------------- 1 | create table groups ( 2 | id serial primary key, 3 | name text, 4 | created_at timestamp 5 | ); 6 | --;; 7 | create index groups_name on groups (name); 8 | --;; 9 | alter table meetups add column group_id int; 10 | --;; 11 | alter table venues add column group_id int; 12 | --;; 13 | create table groups_members ( 14 | group_id int, 15 | member_id int, 16 | primary key (group_id, member_id) 17 | ); 18 | 19 | 20 | -------------------------------------------------------------------------------- /resources/migrations/20171204183639-add-admin-column-to-groups-members.down.sql: -------------------------------------------------------------------------------- 1 | alter table groups_members drop column admin; 2 | -------------------------------------------------------------------------------- /resources/migrations/20171204183639-add-admin-column-to-groups-members.up.sql: -------------------------------------------------------------------------------- 1 | alter table groups_members add column admin boolean default false; 2 | 3 | -------------------------------------------------------------------------------- /resources/migrations/20171204200333-drop-building-from-venues.down.sql: -------------------------------------------------------------------------------- 1 | alter table venues add column building text; 2 | 3 | -------------------------------------------------------------------------------- /resources/migrations/20171204200333-drop-building-from-venues.up.sql: -------------------------------------------------------------------------------- 1 | alter table venues drop column building; 2 | 3 | -------------------------------------------------------------------------------- /resources/migrations/20180115065446-version2.down.sql: -------------------------------------------------------------------------------- 1 | alter table venues drop column venue_type; 2 | --;; 3 | alter table venues drop column url; 4 | --;; 5 | drop type venue_type; 6 | -------------------------------------------------------------------------------- /resources/migrations/20180115065446-version2.up.sql: -------------------------------------------------------------------------------- 1 | create type venue_type as enum ('physical', 'online'); 2 | --;; 3 | alter table venues add column url text; 4 | --;; 5 | alter table venues add column venue_type venue_type default 'physical'; 6 | -------------------------------------------------------------------------------- /resources/migrations/20180124090419-add-online-venue-id-to-meetups.down.sql: -------------------------------------------------------------------------------- 1 | alter table meetups drop column online_venue_id; 2 | -------------------------------------------------------------------------------- /resources/migrations/20180124090419-add-online-venue-id-to-meetups.up.sql: -------------------------------------------------------------------------------- 1 | alter table meetups add column online_venue_id int; 2 | -------------------------------------------------------------------------------- /swagger-spec-v1.json: -------------------------------------------------------------------------------- 1 | {"swagger":"2.0","info":{"title":"Situated Program Challenge","version":"0.0.1"},"produces":["application/json"],"consumes":["application/json"],"basePath":"/","paths":{"/groups":{"get":{"tags":["groups"],"summary":"グループ一覧の取得","description":"現在登録されているグループ一覧を取得します。","responses":{"default":{"description":""},"200":{"schema":{"type":"array","items":{"type":"object","properties":{"group-id":{"type":"integer","format":"int64","description":"自動採番ID"},"group-name":{"type":"string","description":"グループ名"},"admin":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}},"venues":{"type":"array","items":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]}},"meetups":{"type":"array","items":{"type":"object","properties":{"event-id":{"type":"integer","format":"int64","description":"自動採番ID"},"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"members":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["event-id","title","start-at","end-at","venue","members"]}}},"required":["group-id","group-name","admin","venues","meetups"]}},"description":""}}},"post":{"tags":["groups"],"summary":"グループの登録","description":"新しいグループを登録します。","responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"group-id":{"type":"integer","format":"int64","description":"自動採番ID"},"group-name":{"type":"string","description":"グループ名"},"admin":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["group-id","group-name","admin"]},"description":""}},"parameters":[{"in":"body","name":"","description":"","required":true,"schema":{"type":"object","properties":{"group-name":{"type":"string","description":"グループ名"},"admin-member-ids":{"type":"array","items":{"type":"integer","format":"int64","description":"自動採番ID"}}},"required":["group-name","admin-member-ids"]}}]}},"/groups/{group-id}/meetups":{"get":{"tags":["meetups"],"summary":"ミートアップイベント一覧情報の取得","description":"現在登録されているミートアップイベントの一覧を取得します。","parameters":[{"in":"path","name":"group-id","description":"","required":true,"type":"string"}],"responses":{"default":{"description":""},"200":{"schema":{"type":"array","items":{"type":"object","properties":{"event-id":{"type":"integer","format":"int64","description":"自動採番ID"},"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"members":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["event-id","title","start-at","end-at","venue","members"]}},"description":""}}},"post":{"tags":["meetups"],"summary":"ミートアップイベントの登録","description":"新しいミートアップを登録します。","parameters":[{"in":"path","name":"group-id","description":"","required":true,"type":"string"},{"in":"body","name":"","description":"","required":true,"schema":{"type":"object","properties":{"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"}},"required":["title","start-at","end-at","venue-id"]}}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"event-id":{"type":"integer","format":"int64","description":"自動採番ID"},"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"members":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["event-id","title","start-at","end-at","venue","members"]},"description":""}}}},"/groups/{group-id}/meetups/{event-id}":{"get":{"tags":["meetups"],"summary":"ミートアップイベント情報の取得","description":"IDで指定されたミートアップイベントを取得します。","parameters":[{"in":"path","name":"group-id","description":"","required":true,"type":"string"},{"in":"path","name":"event-id","description":"自動採番ID","type":"integer","required":true,"format":"int64"}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"event-id":{"type":"integer","format":"int64","description":"自動採番ID"},"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"members":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["event-id","title","start-at","end-at","venue","members"]},"description":""}}}},"/groups/{group-id}/venues":{"get":{"tags":["venues"],"summary":"会場一覧の取得","description":"登録されている会場の一覧を取得します。","parameters":[{"in":"path","name":"group-id","description":"","required":true,"type":"string"}],"responses":{"default":{"description":""},"200":{"schema":{"type":"array","items":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]}},"description":""}}},"post":{"tags":["venues"],"summary":"会場の登録","description":"会場を登録します。","parameters":[{"in":"path","name":"group-id","description":"","required":true,"type":"string"},{"in":"body","name":"","description":"","required":true,"schema":{"type":"object","properties":{"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-name","address"]}}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"description":""}}}},"/members":{"get":{"tags":["members"],"summary":"メンバー一覧情報の取得","description":"現在登録されているメンバーの一覧を取得します。","responses":{"default":{"description":""},"200":{"schema":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}},"description":""}}},"post":{"tags":["members"],"summary":"メンバーの登録","description":"メンバーを新規登録します。","responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]},"description":""}},"parameters":[{"in":"body","name":"","description":"","required":true,"schema":{"type":"object","properties":{"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["first-name","last-name","email"]}}]}},"/members/{member-id}":{"get":{"tags":["members"],"summary":"メンバー情報の取得","description":"IDで指定されたメンバー情報を取得します。","parameters":[{"in":"path","name":"member-id","description":"自動採番ID","type":"integer","required":true,"format":"int64"}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]},"description":""}}}},"/members/{member-id}/meetups/{event-id}":{"post":{"tags":["members"],"summary":"ミートアップへの参加","description":"メンバーがミートアップに参加申し込みをします。","parameters":[{"in":"path","name":"member-id","description":"","required":true,"type":"string"},{"in":"path","name":"event-id","description":"","required":true,"type":"string"}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"event-id":{"type":"integer","format":"int64","description":"自動採番ID"},"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"members":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["event-id","title","start-at","end-at","venue","members"]},"description":""}}}},"/members/{member-id}/groups/{group-id}":{"post":{"tags":["members"],"summary":"グループへの参加","description":"メンバーがグループに参加申し込みをします。","parameters":[{"in":"path","name":"member-id","description":"","required":true,"type":"string"},{"in":"path","name":"group-id","description":"","required":true,"type":"string"},{"in":"body","name":"","description":"","required":true,"schema":{"type":"object","properties":{"admin":{"type":"boolean","description":"管理者フラグ"}},"required":["admin"]}}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"group-id":{"type":"integer","format":"int64","description":"自動採番ID"},"group-name":{"type":"string","description":"グループ名"},"admin":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}},"venues":{"type":"array","items":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]}},"meetups":{"type":"array","items":{"type":"object","properties":{"event-id":{"type":"integer","format":"int64","description":"自動採番ID"},"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"members":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["event-id","title","start-at","end-at","venue","members"]}}},"required":["group-id","group-name","admin","venues","meetups"]},"description":""}}}}},"tags":[{"name":"meetups","description":"ミートアップエンティティ"},{"name":"members","description":"メンバーエンティティ"},{"name":"venues","description":"会場エンティティ"},{"name":"groups","description":"グループエンティティ"}],"definitions":{}} -------------------------------------------------------------------------------- /swagger-spec-v2.json: -------------------------------------------------------------------------------- 1 | {"swagger":"2.0","info":{"title":"Situated Program Challenge","version":"0.0.2"},"produces":["application/json"],"consumes":["application/json"],"basePath":"/","paths":{"/groups":{"get":{"tags":["groups"],"summary":"グループ一覧の取得","description":"現在登録されているグループ一覧を取得します。","responses":{"default":{"description":""},"200":{"schema":{"type":"array","items":{"type":"object","properties":{"group-id":{"type":"integer","format":"int64","description":"自動採番ID"},"group-name":{"type":"string","description":"グループ名"},"admin":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}},"venues":{"type":"array","items":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]}},"online-venues":{"type":"array","items":{"type":"object","properties":{"online-venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"url":{"type":"string","description":"オンライン会場URL"}},"required":["online-venue-id","venue-name","url"]}},"meetups":{"type":"array","items":{"type":"object","properties":{"event-id":{"type":"integer","format":"int64","description":"自動採番ID"},"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"online-venue":{"type":"object","properties":{"online-venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"url":{"type":"string","description":"オンライン会場URL"}},"required":["online-venue-id","venue-name","url"]},"members":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["event-id","title","start-at","end-at","venue","online-venue","members"]}}},"required":["group-id","group-name","admin","venues","online-venues","meetups"]}},"description":""}}},"post":{"tags":["groups"],"summary":"グループの登録","description":"新しいグループを登録します。","responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"group-id":{"type":"integer","format":"int64","description":"自動採番ID"},"group-name":{"type":"string","description":"グループ名"},"admin":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["group-id","group-name","admin"]},"description":""}},"parameters":[{"in":"body","name":"","description":"","required":true,"schema":{"type":"object","properties":{"group-name":{"type":"string","description":"グループ名"},"admin-member-ids":{"type":"array","items":{"type":"integer","format":"int64","description":"自動採番ID"}}},"required":["group-name","admin-member-ids"]}}]}},"/groups/{group-id}/meetups":{"get":{"tags":["meetups"],"summary":"ミートアップイベント一覧情報の取得","description":"現在登録されているミートアップイベントの一覧を取得します。","parameters":[{"in":"path","name":"group-id","description":"","required":true,"type":"string"}],"responses":{"default":{"description":""},"200":{"schema":{"type":"array","items":{"type":"object","properties":{"event-id":{"type":"integer","format":"int64","description":"自動採番ID"},"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"online-venue":{"type":"object","properties":{"online-venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"url":{"type":"string","description":"オンライン会場URL"}},"required":["online-venue-id","venue-name","url"]},"members":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["event-id","title","start-at","end-at","venue","online-venue","members"]}},"description":""}}},"post":{"tags":["meetups"],"summary":"ミートアップイベントの登録","description":"新しいミートアップを登録します。","parameters":[{"in":"path","name":"group-id","description":"","required":true,"type":"string"},{"in":"body","name":"","description":"","required":true,"schema":{"type":"object","properties":{"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"online-venue-id":{"type":"integer","format":"int64","description":"自動採番ID"}},"required":["title","start-at","end-at","venue-id","online-venue-id"]}}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"event-id":{"type":"integer","format":"int64","description":"自動採番ID"},"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"online-venue":{"type":"object","properties":{"online-venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"url":{"type":"string","description":"オンライン会場URL"}},"required":["online-venue-id","venue-name","url"]},"members":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["event-id","title","start-at","end-at","venue","online-venue","members"]},"description":""}}}},"/groups/{group-id}/meetups/{event-id}":{"get":{"tags":["meetups"],"summary":"ミートアップイベント情報の取得","description":"IDで指定されたミートアップイベントを取得します。","parameters":[{"in":"path","name":"group-id","description":"","required":true,"type":"string"},{"in":"path","name":"event-id","description":"自動採番ID","type":"integer","required":true,"format":"int64"}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"event-id":{"type":"integer","format":"int64","description":"自動採番ID"},"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"online-venue":{"type":"object","properties":{"online-venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"url":{"type":"string","description":"オンライン会場URL"}},"required":["online-venue-id","venue-name","url"]},"members":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["event-id","title","start-at","end-at","venue","online-venue","members"]},"description":""}}}},"/groups/{group-id}/venues":{"get":{"tags":["venues"],"summary":"会場一覧の取得","description":"登録されている会場の一覧を取得します。","parameters":[{"in":"path","name":"group-id","description":"","required":true,"type":"string"}],"responses":{"default":{"description":""},"200":{"schema":{"type":"array","items":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]}},"description":""}}},"post":{"tags":["venues"],"summary":"会場の登録","description":"会場を登録します。","parameters":[{"in":"path","name":"group-id","description":"","required":true,"type":"string"},{"in":"body","name":"","description":"","required":true,"schema":{"type":"object","properties":{"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-name","address"]}}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"description":""}}}},"/groups/{group-id}/online-venues":{"get":{"tags":["online-venues"],"summary":"オンライン会場一覧の取得","description":"登録されているオンライン会場の一覧を取得します。","parameters":[{"in":"path","name":"group-id","description":"","required":true,"type":"string"}],"responses":{"default":{"description":""},"200":{"schema":{"type":"array","items":{"type":"object","properties":{"online-venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"url":{"type":"string","description":"オンライン会場URL"}},"required":["online-venue-id","venue-name","url"]}},"description":""}}},"post":{"tags":["online-venues"],"summary":"オンライン会場の登録","description":"オンライン会場を登録します。","parameters":[{"in":"path","name":"group-id","description":"","required":true,"type":"string"},{"in":"body","name":"","description":"","required":true,"schema":{"type":"object","properties":{"venue-name":{"type":"string","description":"会場名"},"url":{"type":"string","description":"オンライン会場URL"}},"required":["venue-name","url"]}}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"online-venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"url":{"type":"string","description":"オンライン会場URL"}},"required":["online-venue-id","venue-name","url"]},"description":""}}}},"/members":{"get":{"tags":["members"],"summary":"メンバー一覧情報の取得","description":"現在登録されているメンバーの一覧を取得します。","responses":{"default":{"description":""},"200":{"schema":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}},"description":""}}},"post":{"tags":["members"],"summary":"メンバーの登録","description":"メンバーを新規登録します。","responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]},"description":""}},"parameters":[{"in":"body","name":"","description":"","required":true,"schema":{"type":"object","properties":{"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["first-name","last-name","email"]}}]}},"/members/{member-id}":{"get":{"tags":["members"],"summary":"メンバー情報の取得","description":"IDで指定されたメンバー情報を取得します。","parameters":[{"in":"path","name":"member-id","description":"自動採番ID","type":"integer","required":true,"format":"int64"}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]},"description":""}}}},"/members/{member-id}/meetups/{event-id}":{"post":{"tags":["members"],"summary":"ミートアップへの参加","description":"メンバーがミートアップに参加申し込みをします。","parameters":[{"in":"path","name":"member-id","description":"","required":true,"type":"string"},{"in":"path","name":"event-id","description":"","required":true,"type":"string"}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"event-id":{"type":"integer","format":"int64","description":"自動採番ID"},"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"online-venue":{"type":"object","properties":{"online-venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"url":{"type":"string","description":"オンライン会場URL"}},"required":["online-venue-id","venue-name","url"]},"members":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["event-id","title","start-at","end-at","venue","online-venue","members"]},"description":""}}}},"/members/{member-id}/groups/{group-id}":{"post":{"tags":["members"],"summary":"グループへの参加","description":"メンバーがグループに参加申し込みをします。","parameters":[{"in":"path","name":"member-id","description":"","required":true,"type":"string"},{"in":"path","name":"group-id","description":"","required":true,"type":"string"},{"in":"body","name":"","description":"","required":true,"schema":{"type":"object","properties":{"admin":{"type":"boolean","description":"管理者フラグ"}},"required":["admin"]}}],"responses":{"default":{"description":""},"200":{"schema":{"type":"object","properties":{"group-id":{"type":"integer","format":"int64","description":"自動採番ID"},"group-name":{"type":"string","description":"グループ名"},"admin":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}},"venues":{"type":"array","items":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]}},"online-venues":{"type":"array","items":{"type":"object","properties":{"online-venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"url":{"type":"string","description":"オンライン会場URL"}},"required":["online-venue-id","venue-name","url"]}},"meetups":{"type":"array","items":{"type":"object","properties":{"event-id":{"type":"integer","format":"int64","description":"自動採番ID"},"title":{"type":"string","description":"イベント名"},"start-at":{"type":"string","format":"date-time","description":"開始日時"},"end-at":{"type":"string","format":"date-time","description":"終了日時"},"venue":{"type":"object","properties":{"venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"address":{"type":"object","properties":{"postal-code":{"type":"string","description":"郵便番号"},"prefecture":{"type":"string","description":"都道府県"},"city":{"type":"string","description":"市区町村"},"address1":{"type":"string","description":"丁目番地"},"address2":{"type":"string","description":"建物名"}},"required":["postal-code","prefecture","city","address1"]}},"required":["venue-id","venue-name","address"]},"online-venue":{"type":"object","properties":{"online-venue-id":{"type":"integer","format":"int64","description":"自動採番ID"},"venue-name":{"type":"string","description":"会場名"},"url":{"type":"string","description":"オンライン会場URL"}},"required":["online-venue-id","venue-name","url"]},"members":{"type":"array","items":{"type":"object","properties":{"member-id":{"type":"integer","format":"int64","description":"自動採番ID"},"first-name":{"type":"string","description":"名"},"last-name":{"type":"string","description":"姓"},"email":{"type":"string","description":"Eメールアドレス"}},"required":["member-id","first-name","last-name","email"]}}},"required":["event-id","title","start-at","end-at","venue","online-venue","members"]}}},"required":["group-id","group-name","admin","venues","online-venues","meetups"]},"description":""}}}}},"tags":[{"name":"meetups","description":"ミートアップエンティティ"},{"name":"members","description":"メンバーエンティティ"},{"name":"venues","description":"会場エンティティ"},{"name":"online-venues","description":"オンライン会場エンティティ"},{"name":"groups","description":"グループエンティティ"}],"definitions":{}} --------------------------------------------------------------------------------