diff --git a/docs/rest-api/openapi.yml b/docs/rest-api/openapi.yml index 718acaa1..4573b790 100644 --- a/docs/rest-api/openapi.yml +++ b/docs/rest-api/openapi.yml @@ -65,7 +65,7 @@ paths: status: type: string example: operational - /system/health: + /v1/system/health: get: summary: Get API server health tags: @@ -73,7 +73,7 @@ paths: responses: 200: description: API server is runnning - /system/metadata: + /v1/system/metadata: get: summary: Get system metadata tags: @@ -106,8 +106,8 @@ paths: example: "Not Found" instance: type: string - example: "/system/metadata" - /token: + example: "/v1/system/metadata" + /v1/token: post: summary: Create an IDToken tags: @@ -149,10 +149,10 @@ paths: example: "Bad Request" instance: type: string - example: "/token" + example: "/v1/token" 401: description: Unauthorized - /archives: + /v1/archives: get: summary: Get all articles tags: @@ -166,7 +166,7 @@ paths: type: array items: $ref: '#/components/schemas/simpleArticle' - /articles: + /v1/articles: get: summary: Get articles with count tags: @@ -198,7 +198,7 @@ paths: type: array items: $ref: '#/components/schemas/article' - /authors: + /v1/authors: get: summary: Get all authors tags: @@ -212,7 +212,7 @@ paths: type: array items: $ref: '#/components/schemas/author' - /authors/{authorName}: + /v1/authors/{authorName}: get: summary: Get an author tags: @@ -246,8 +246,8 @@ paths: example: "Not Found" instance: type: string - example: "/author/{authorName}" - /contents: + example: "/v1/author/{authorName}" + /v1/contents: post: summary: Create or Update a content tags: @@ -289,7 +289,7 @@ paths: example: "Bad Request" instance: type: string - example: "/contents/" + example: "/v1/contents/" 401: description: Unauthorized 422: @@ -316,8 +316,8 @@ paths: example: "Unprocessable Entity" instance: type: string - example: "/contents" - /contents/{contentPath}: + example: "/v1/contents" + /v1/contents/{contentPath}: get: summary: Get a content tags: @@ -350,8 +350,8 @@ paths: example: "Not Found" instance: type: string - example: "/contents/{contentPath}" - /contents/{contentId}: + example: "/v1/contents/{contentPath}" + /v1/contents/{contentId}: delete: summary: Delete a content tags: @@ -384,8 +384,8 @@ paths: example: "Not Found" instance: type: string - example: "/contents/{contentId}" - /tags: + example: "/v1/contents/{contentId}" + /v1/tags: get: summary: Get a tags tags: @@ -398,7 +398,7 @@ paths: schema: items: $ref: '#/components/schemas/tagWithCount' - /tags/{tagName}: + /v1/tags/{tagName}: get: summary: Get an articles by tagName tags: @@ -432,8 +432,8 @@ paths: example: "Not Found" instance: type: string - example: "/tags/{tagName}" - /tags/{tagId}: + example: "/v1/tags/{tagName}" + /v1/tags/{tagId}: delete: summary: Delete a tag tags: @@ -466,8 +466,8 @@ paths: example: "Not Found" instance: type: string - example: "/tags/{tagId}" - /content-types/{contentPath}: + example: "/v1/tags/{tagId}" + /v1/content-types/{contentPath}: get: summary: Get Content-Types tags: @@ -480,7 +480,7 @@ paths: schema: items: $ref: '#/components/schemas/contentType' - /content-types/{contentTypeName}: + /v1/content-types/{contentTypeName}: get: summary: Get a Content-Type tags: @@ -513,8 +513,8 @@ paths: example: "Not Found" instance: type: string - example: "/content-types/{contentTypeName}" - /sitemaps: + example: "/v1/content-types/{contentTypeName}" + /v1/sitemaps: get: summary: Get an articles url & updated date for sitemaps.xml tags: @@ -528,7 +528,7 @@ paths: type: array items: $ref: '#/components/schemas/sitemaps' - /feeds/{feedName}: + /v1/feeds/{feedName}: get: summary: Get a feeds tags: @@ -542,7 +542,7 @@ paths: type: array items: $ref: '#/components/schemas/feed' - /caches/: + /v1/caches/: delete: summary: Invalidate all caches tags: @@ -554,7 +554,7 @@ paths: description: Caches are invalidated 401: description: Unauthorized - /search: + /v1/search: get: summary: Search contents tags: @@ -602,8 +602,8 @@ paths: example: "Unprocessable Entity" instance: type: string - example: "/search" - /series: + example: "/v1/search" + /v1/series: get: summary: Get all series tags: @@ -658,10 +658,10 @@ paths: example: "Bad Request" instance: type: string - example: "/series/" + example: "/v1/series/" 401: description: Unauthorized - /series/{seriesName}: + /v1/series/{seriesName}: get: summary: Get a series tags: @@ -696,7 +696,7 @@ paths: example: "Not Found" instance: type: string - example: "/series/{seriesName}" + example: "/v1/series/{seriesName}" components: securitySchemes: basicAuth: diff --git a/src/main/scala/net/yoshinorin/qualtet/BootStrap.scala b/src/main/scala/net/yoshinorin/qualtet/BootStrap.scala index 38fe7975..7bc7f371 100644 --- a/src/main/scala/net/yoshinorin/qualtet/BootStrap.scala +++ b/src/main/scala/net/yoshinorin/qualtet/BootStrap.scala @@ -12,21 +12,21 @@ import org.typelevel.log4cats.{LoggerFactory => Log4CatsLoggerFactory} import org.typelevel.log4cats.slf4j.{Slf4jFactory => Log4CatsSlf4jFactory} import org.typelevel.log4cats.slf4j.{Slf4jLogger => Log4CatsSlf4jLogger} import net.yoshinorin.qualtet.http.{AuthProvider, CorsProvider} -import net.yoshinorin.qualtet.http.routes.{ - ArchiveRoute, - ArticleRoute, - AuthRoute, - AuthorRoute, - CacheRoute, - ContentRoute, - ContentTypeRoute, - FeedRoute, - HomeRoute, - SearchRoute, - SeriesRoute, - SitemapRoute, - SystemRoute, - TagRoute +import net.yoshinorin.qualtet.http.routes.HomeRoute +import net.yoshinorin.qualtet.http.routes.v1.{ + ArchiveRoute => ArchiveRouteV1, + ArticleRoute => ArticleRouteV1, + AuthRoute => AuthRouteV1, + AuthorRoute => AuthorRouteV1, + CacheRoute => CacheRouteV1, + ContentRoute => ContentRouteV1, + ContentTypeRoute => ContentTypeRouteV1, + FeedRoute => FeedRouteV1, + SearchRoute => SearchRouteV1, + SeriesRoute => SeriesRouteV1, + SitemapRoute => SitemapRouteV1, + SystemRoute => SystemRouteV1, + TagRoute => TagRouteV1 } import scala.concurrent.duration._ @@ -42,37 +42,37 @@ object BootStrap extends IOApp { val authProvider = new AuthProvider(Modules.authService) val corsProvider = new CorsProvider(Modules.config.cors) - val archiveRoute = new ArchiveRoute(Modules.archiveService) - val articleRoute = new ArticleRoute(Modules.articleService) - val authorRoute = new AuthorRoute(Modules.authorService) - val authRoute = new AuthRoute(Modules.authService) - val cacheRoute = new CacheRoute(authProvider, Modules.cacheService) - val contentTypeRoute = new ContentTypeRoute(Modules.contentTypeService) - val contentRoute = new ContentRoute(authProvider, Modules.contentService) - val feedRoute = new FeedRoute(Modules.feedService) + val archiveRouteV1 = new ArchiveRouteV1(Modules.archiveService) + val articleRouteV1 = new ArticleRouteV1(Modules.articleService) + val authorRouteV1 = new AuthorRouteV1(Modules.authorService) + val authRouteV1 = new AuthRouteV1(Modules.authService) + val cacheRouteV1 = new CacheRouteV1(authProvider, Modules.cacheService) + val contentTypeRouteV1 = new ContentTypeRouteV1(Modules.contentTypeService) + val contentRouteV1 = new ContentRouteV1(authProvider, Modules.contentService) + val feedRouteV1 = new FeedRouteV1(Modules.feedService) val homeRoute: HomeRoute = new HomeRoute() - val searchRoute = new SearchRoute(Modules.searchService) - val seriesRoute = new SeriesRoute(authProvider, Modules.seriesService) - val sitemapRoute = new SitemapRoute(Modules.sitemapService) - val systemRoute = new SystemRoute(Modules.config.http.endpoints.system) - val tagRoute = new TagRoute(authProvider, Modules.tagService, Modules.articleService) + val searchRouteV1 = new SearchRouteV1(Modules.searchService) + val seriesRouteV1 = new SeriesRouteV1(authProvider, Modules.seriesService) + val sitemapRouteV1 = new SitemapRouteV1(Modules.sitemapService) + val systemRouteV1 = new SystemRouteV1(Modules.config.http.endpoints.system) + val tagRouteV1 = new TagRouteV1(authProvider, Modules.tagService, Modules.articleService) val router = new net.yoshinorin.qualtet.http.Router( corsProvider, - archiveRoute, - articleRoute, - authorRoute, - authRoute, - cacheRoute, - contentRoute, - contentTypeRoute, - feedRoute, + archiveRouteV1, + articleRouteV1, + authorRouteV1, + authRouteV1, + cacheRouteV1, + contentRouteV1, + contentTypeRouteV1, + feedRouteV1, homeRoute, - searchRoute, - seriesRoute, - sitemapRoute, - systemRoute, - tagRoute + searchRouteV1, + seriesRouteV1, + sitemapRouteV1, + systemRouteV1, + tagRouteV1 ) private[this] def server(host: Ipv4Address, port: Port, httpApp: HttpApp[IO]): Resource[IO, Server] = { diff --git a/src/main/scala/net/yoshinorin/qualtet/http/Router.scala b/src/main/scala/net/yoshinorin/qualtet/http/Router.scala index 62b8b491..3065710f 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/Router.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/Router.scala @@ -2,57 +2,57 @@ package net.yoshinorin.qualtet.http import cats.Monad import org.http4s.server.{Router => Http4sRouter} -import net.yoshinorin.qualtet.http.routes.{ - ArchiveRoute, - ArticleRoute, - AuthRoute, - AuthorRoute, - CacheRoute, - ContentRoute, - ContentTypeRoute, - FeedRoute, - HomeRoute, - SearchRoute, - SeriesRoute, - SitemapRoute, - SystemRoute, - TagRoute +import net.yoshinorin.qualtet.http.routes.HomeRoute +import net.yoshinorin.qualtet.http.routes.v1.{ + ArchiveRoute => ArchiveRouteV1, + ArticleRoute => ArticleRouteV1, + AuthRoute => AuthRouteV1, + AuthorRoute => AuthorRouteV1, + CacheRoute => CacheRouteV1, + ContentRoute => ContentRouteV1, + ContentTypeRoute => ContentTypeRouteV1, + FeedRoute => FeedRouteV1, + SearchRoute => SearchRouteV1, + SeriesRoute => SeriesRouteV1, + SitemapRoute => SitemapRouteV1, + SystemRoute => SystemRouteV1, + TagRoute => TagRouteV1 } class Router[F[_]: Monad]( corsProvider: CorsProvider, - archiveRoute: ArchiveRoute[F], - articleRoute: ArticleRoute[F], - authorRoute: AuthorRoute[F], - authRoute: AuthRoute[F], - cacheRoute: CacheRoute[F], - contentRoute: ContentRoute[F], - contentTypeRoute: ContentTypeRoute[F], - feedRoute: FeedRoute[F], + archiveRouteV1: ArchiveRouteV1[F], + articleRouteV1: ArticleRouteV1[F], + authorRouteV1: AuthorRouteV1[F], + authRouteV1: AuthRouteV1[F], + cacheRouteV1: CacheRouteV1[F], + contentRouteV1: ContentRouteV1[F], + contentTypeRouteV1: ContentTypeRouteV1[F], + feedRouteV1: FeedRouteV1[F], homeRoute: HomeRoute, - searchRoute: SearchRoute[F], - seriesRoute: SeriesRoute[F], - sitemapRoute: SitemapRoute[F], - systemRoute: SystemRoute, - tagRoute: TagRoute[F] + searchRouteV1: SearchRouteV1[F], + seriesRouteV1: SeriesRouteV1[F], + sitemapRouteV1: SitemapRouteV1[F], + systemRouteV1: SystemRouteV1, + tagRouteV1: TagRouteV1[F] ) { def withCors = corsProvider.httpRouter(routes) def routes = Http4sRouter( "/" -> homeRoute.index, - "/archives" -> archiveRoute.index, - "/articles" -> articleRoute.index, - "/authors" -> authorRoute.index, - "/caches" -> cacheRoute.index, - "/contents" -> contentRoute.index, - "/content-types" -> contentTypeRoute.index, - "/feeds" -> feedRoute.index, - "/search" -> searchRoute.index, - "/series" -> seriesRoute.index, - "/sitemaps" -> sitemapRoute.index, - "/system" -> systemRoute.index, - "/tags" -> tagRoute.index, - "/token" -> authRoute.index + "/v1/archives" -> archiveRouteV1.index, + "/v1/articles" -> articleRouteV1.index, + "/v1/authors" -> authorRouteV1.index, + "/v1/caches" -> cacheRouteV1.index, + "/v1/contents" -> contentRouteV1.index, + "/v1/content-types" -> contentTypeRouteV1.index, + "/v1/feeds" -> feedRouteV1.index, + "/v1/search" -> searchRouteV1.index, + "/v1/series" -> seriesRouteV1.index, + "/v1/sitemaps" -> sitemapRouteV1.index, + "/v1/system" -> systemRouteV1.index, + "/v1/tags" -> tagRouteV1.index, + "/v1/token" -> authRouteV1.index ) } diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/ArchiveRoute.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/ArchiveRoute.scala similarity index 95% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/ArchiveRoute.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/ArchiveRoute.scala index 7f49c2bf..633ab9f5 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/ArchiveRoute.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/ArchiveRoute.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import cats.Monad diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/ArticleRoute.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/ArticleRoute.scala similarity index 96% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/ArticleRoute.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/ArticleRoute.scala index c259e76c..95256e0c 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/ArticleRoute.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/ArticleRoute.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import cats.Monad diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/AuthRoute.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/AuthRoute.scala similarity index 96% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/AuthRoute.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/AuthRoute.scala index b8561cda..9ccfd360 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/AuthRoute.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/AuthRoute.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import cats.Monad diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/AuthorRoute.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/AuthorRoute.scala similarity index 96% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/AuthorRoute.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/AuthorRoute.scala index 33290e40..e3214bfa 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/AuthorRoute.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/AuthorRoute.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import cats.Monad diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/CacheRoute.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/CacheRoute.scala similarity index 95% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/CacheRoute.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/CacheRoute.scala index 2f0722db..c2b36751 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/CacheRoute.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/CacheRoute.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import cats.Monad diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/ContentRoute.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/ContentRoute.scala similarity index 95% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/ContentRoute.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/ContentRoute.scala index a36ac5d4..9e8a9402 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/ContentRoute.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/ContentRoute.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.Monad import cats.implicits.* @@ -36,7 +36,7 @@ class ContentRoute[F[_]: Monad]( */ case request @ GET -> _ => this - .get(request.uri.path.toString().replace("/contents/", ""))(request) + .get(request.uri.path.toString().replace("/v1/contents/", ""))(request) .handleErrorWith(_.logWithStackTrace[IO].andResponse(request)) } diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/ContentTypeRoute.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/ContentTypeRoute.scala similarity index 96% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/ContentTypeRoute.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/ContentTypeRoute.scala index e16286bf..44546a45 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/ContentTypeRoute.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/ContentTypeRoute.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import cats.Monad diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/FeedRoute.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/FeedRoute.scala similarity index 96% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/FeedRoute.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/FeedRoute.scala index 65886e76..6f6fc27a 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/FeedRoute.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/FeedRoute.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import cats.Monad diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/SearchRoute.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/SearchRoute.scala similarity index 96% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/SearchRoute.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/SearchRoute.scala index 174a1292..46cf440c 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/SearchRoute.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/SearchRoute.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.* import cats.Monad diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/SeriesRoute.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/SeriesRoute.scala similarity index 98% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/SeriesRoute.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/SeriesRoute.scala index b77dd470..065db54e 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/SeriesRoute.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/SeriesRoute.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.* import cats.Monad diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/SitemapRoute.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/SitemapRoute.scala similarity index 95% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/SitemapRoute.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/SitemapRoute.scala index ae1e36b2..143af619 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/SitemapRoute.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/SitemapRoute.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import cats.Monad diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/SystemRoutes.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/SystemRoutes.scala similarity index 96% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/SystemRoutes.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/SystemRoutes.scala index 4cdba4b7..8c1dd5c9 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/SystemRoutes.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/SystemRoutes.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.headers.{Allow, `Content-Type`} diff --git a/src/main/scala/net/yoshinorin/qualtet/http/routes/TagRoute.scala b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/TagRoute.scala similarity index 98% rename from src/main/scala/net/yoshinorin/qualtet/http/routes/TagRoute.scala rename to src/main/scala/net/yoshinorin/qualtet/http/routes/v1/TagRoute.scala index 1d0c9d61..5f0a251c 100644 --- a/src/main/scala/net/yoshinorin/qualtet/http/routes/TagRoute.scala +++ b/src/main/scala/net/yoshinorin/qualtet/http/routes/v1/TagRoute.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import cats.Monad diff --git a/src/test/scala/net/yoshinorin/qualtet/fixture/Fixture.scala b/src/test/scala/net/yoshinorin/qualtet/fixture/Fixture.scala index 4b6b1db0..180648fc 100644 --- a/src/test/scala/net/yoshinorin/qualtet/fixture/Fixture.scala +++ b/src/test/scala/net/yoshinorin/qualtet/fixture/Fixture.scala @@ -18,25 +18,25 @@ import net.yoshinorin.qualtet.domains.contents.* import net.yoshinorin.qualtet.domains.contentTypes.* import net.yoshinorin.qualtet.domains.robots.* import net.yoshinorin.qualtet.domains.sitemaps.{SitemapsRepositoryDoobieInterpreter, SitemapService, Url} -import net.yoshinorin.qualtet.http.routes.{ - ArchiveRoute, - ArticleRoute, - AuthRoute, - AuthorRoute, - ContentRoute, - ContentTypeRoute, - FeedRoute, - HomeRoute, - SearchRoute, - SeriesRoute, - SitemapRoute, - SystemRoute, - TagRoute +import net.yoshinorin.qualtet.http.routes.HomeRoute +import net.yoshinorin.qualtet.http.routes.v1.{ + ArchiveRoute => ArchiveRouteV1, + ArticleRoute => ArticleRouteV1, + AuthRoute => AuthRouteV1, + AuthorRoute => AuthorRouteV1, + CacheRoute => CacheRouteV1, + ContentRoute => ContentRouteV1, + ContentTypeRoute => ContentTypeRouteV1, + FeedRoute => FeedRouteV1, + SearchRoute => SearchRouteV1, + SeriesRoute => SeriesRouteV1, + SitemapRoute => SitemapRouteV1, + SystemRoute => SystemRouteV1, + TagRoute => TagRouteV1 } import java.util.concurrent.TimeUnit import wvlet.airframe.ulid.ULID import net.yoshinorin.qualtet.domains.feeds.FeedService -import net.yoshinorin.qualtet.http.routes.CacheRoute import net.yoshinorin.qualtet.domains.articles.ResponseArticleWithCount import net.yoshinorin.qualtet.Modules import net.yoshinorin.qualtet.syntax.* @@ -78,70 +78,70 @@ object Fixture { val authProvider = new AuthProvider(Modules.authService) val corsProvider = new CorsProvider(Modules.config.cors) - val archiveRoute = new ArchiveRoute(Modules.archiveService) - val articleRoute = new ArticleRoute(Modules.articleService) - val authorRoute = new AuthorRoute(Modules.authorService) - val authRoute = new AuthRoute(Modules.authService) - val cacheRoute = new CacheRoute(authProvider, Modules.cacheService) - val contentTypeRoute = new ContentTypeRoute(Modules.contentTypeService) - val contentRoute = new ContentRoute(authProvider, Modules.contentService) - val feedRoute = new FeedRoute(Modules.feedService) + val archiveRouteV1 = new ArchiveRouteV1(Modules.archiveService) + val articleRouteV1 = new ArticleRouteV1(Modules.articleService) + val authorRouteV1 = new AuthorRouteV1(Modules.authorService) + val authRouteV1 = new AuthRouteV1(Modules.authService) + val cacheRouteV1 = new CacheRouteV1(authProvider, Modules.cacheService) + val contentTypeRouteV1 = new ContentTypeRouteV1(Modules.contentTypeService) + val contentRouteV1 = new ContentRouteV1(authProvider, Modules.contentService) + val feedRouteV1 = new FeedRouteV1(Modules.feedService) val homeRoute: HomeRoute = new HomeRoute() - val searchRoute = new SearchRoute(Modules.searchService) - val seriesRoute = new SeriesRoute(authProvider, Modules.seriesService) - val sitemapRoute = new SitemapRoute(Modules.sitemapService) - val systemRoute = new SystemRoute(Modules.config.http.endpoints.system) - val tagRoute = new TagRoute(authProvider, Modules.tagService, Modules.articleService) + val searchRouteV1 = new SearchRouteV1(Modules.searchService) + val seriesRouteV1 = new SeriesRouteV1(authProvider, Modules.seriesService) + val sitemapRouteV1 = new SitemapRouteV1(Modules.sitemapService) + val systemRouteV1 = new SystemRouteV1(Modules.config.http.endpoints.system) + val tagRouteV1 = new TagRouteV1(authProvider, Modules.tagService, Modules.articleService) val router = new net.yoshinorin.qualtet.http.Router( corsProvider, - archiveRoute, - articleRoute, - authorRoute, - authRoute, - cacheRoute, - contentRoute, - contentTypeRoute, - feedRoute, + archiveRouteV1, + articleRouteV1, + authorRouteV1, + authRouteV1, + cacheRouteV1, + contentRouteV1, + contentTypeRouteV1, + feedRouteV1, homeRoute, - searchRoute, - seriesRoute, - sitemapRoute, - systemRoute, - tagRoute + searchRouteV1, + seriesRouteV1, + sitemapRouteV1, + systemRouteV1, + tagRouteV1 ) def makeRouter[F[_]: Monad]( - archiveRoute: ArchiveRoute[F] = archiveRoute, - articleRoute: ArticleRoute[F] = articleRoute, - authorRoute: AuthorRoute[F] = authorRoute, - authRoute: AuthRoute[F] = authRoute, - cacheRoute: CacheRoute[F] = cacheRoute, - contentRoute: ContentRoute[F] = contentRoute, - contentTypeRoute: ContentTypeRoute[F] = contentTypeRoute, - feedRoute: FeedRoute[F] = feedRoute, + archiveRouteV1: ArchiveRouteV1[F] = archiveRouteV1, + articleRouteV1: ArticleRouteV1[F] = articleRouteV1, + authorRouteV1: AuthorRouteV1[F] = authorRouteV1, + authRouteV1: AuthRouteV1[F] = authRouteV1, + cacheRouteV1: CacheRouteV1[F] = cacheRouteV1, + contentRouteV1: ContentRouteV1[F] = contentRouteV1, + contentTypeRouteV1: ContentTypeRouteV1[F] = contentTypeRouteV1, + feedRouteV1: FeedRouteV1[F] = feedRouteV1, homeRoute: HomeRoute = homeRoute, - searchRoute: SearchRoute[F] = searchRoute, - seriesRoute: SeriesRoute[F] = seriesRoute, - sitemapRoute: SitemapRoute[F] = sitemapRoute, - tagRoute: TagRoute[F] = tagRoute, - systemRoute: SystemRoute = systemRoute + searchRouteV1: SearchRouteV1[F] = searchRouteV1, + seriesRouteV1: SeriesRouteV1[F] = seriesRouteV1, + sitemapRouteV1: SitemapRouteV1[F] = sitemapRouteV1, + tagRouteV1: TagRouteV1[F] = tagRouteV1, + systemRouteV1: SystemRouteV1 = systemRouteV1 ) = new net.yoshinorin.qualtet.http.Router( corsProvider = corsProvider, - archiveRoute = archiveRoute, - articleRoute = articleRoute, - authorRoute = authorRoute, - authRoute = authRoute, - cacheRoute = cacheRoute, - contentRoute = contentRoute, - contentTypeRoute = contentTypeRoute, - feedRoute = feedRoute, + archiveRouteV1 = archiveRouteV1, + articleRouteV1 = articleRouteV1, + authorRouteV1 = authorRouteV1, + authRouteV1 = authRouteV1, + cacheRouteV1 = cacheRouteV1, + contentRouteV1 = contentRouteV1, + contentTypeRouteV1 = contentTypeRouteV1, + feedRouteV1 = feedRouteV1, homeRoute = homeRoute, - searchRoute = searchRoute, - seriesRoute = seriesRoute, - sitemapRoute = sitemapRoute, - systemRoute = systemRoute, - tagRoute = tagRoute + searchRouteV1 = searchRouteV1, + seriesRouteV1 = seriesRouteV1, + sitemapRouteV1 = sitemapRouteV1, + systemRouteV1 = systemRouteV1, + tagRouteV1 = tagRouteV1 ) val authorId: AuthorId = AuthorId("01febb8az5t42m2h68xj8c754a") diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/ArchiveRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/ArchiveRouteSpec.scala similarity index 85% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/ArchiveRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/ArchiveRouteSpec.scala index ce1ca3b1..609cc60a 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/ArchiveRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/ArchiveRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import doobie.ConnectionIO @@ -16,15 +16,15 @@ import org.scalatest.wordspec.AnyWordSpec import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.ArchiveRouteSpec -class ArchiveRouteSpec extends AnyWordSpec { +// testOnly net.yoshinorin.qualtet.http.routes.v1.ArchiveRouteSpec +class ArchiveRouteV1Spec extends AnyWordSpec { val mockArchiveService = Mockito.mock(classOf[ArchiveService[ConnectionIO]]) - val archiveRoute = new ArchiveRoute(mockArchiveService) + val archiveRouteV1 = new ArchiveRoute(mockArchiveService) - val router = Fixture.makeRouter(archiveRoute = archiveRoute) + val router = Fixture.makeRouter(archiveRouteV1 = archiveRouteV1) - val request: Request[IO] = Request(method = Method.GET, uri = uri"/archives") + val request: Request[IO] = Request(method = Method.GET, uri = uri"/v1/archives") val client: Client[IO] = Client.fromHttpApp(router.routes.orNotFound) when(mockArchiveService.get).thenReturn( @@ -77,7 +77,7 @@ class ArchiveRouteSpec extends AnyWordSpec { "be return Method Not Allowed" in { client - .run(Request(method = Method.DELETE, uri = uri"/archives")) + .run(Request(method = Method.DELETE, uri = uri"/v1/archives")) .use { response => IO { assert(response.status === MethodNotAllowed) diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/ArticleRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/ArticleRouteSpec.scala similarity index 86% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/ArticleRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/ArticleRouteSpec.scala index 5884d4b7..605390d4 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/ArticleRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/ArticleRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.client.Client @@ -17,8 +17,8 @@ import org.scalatest.wordspec.AnyWordSpec import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.ArticleRouteSpec -class ArticleRouteSpec extends AnyWordSpec { +// testOnly net.yoshinorin.qualtet.http.routes.v1.ArticleRouteSpec +class ArticleRouteV1Spec extends AnyWordSpec { val requestContents: List[RequestContent] = { (0 until 20).toList @@ -45,7 +45,7 @@ class ArticleRouteSpec extends AnyWordSpec { "ArticleRoute" should { "be return articles with default query params" in { client - .run(Request(method = Method.GET, uri = uri"/articles")) + .run(Request(method = Method.GET, uri = uri"/v1/articles")) .use { response => IO { assert(response.status === Ok) @@ -61,7 +61,7 @@ class ArticleRouteSpec extends AnyWordSpec { "be return articles with query params" in { client - .run(Request(method = Method.GET, uri = uri"/articles/?page=1&limit=5")) + .run(Request(method = Method.GET, uri = uri"/v1/articles/?page=1&limit=5")) .use { response => IO { assert(response.status === Ok) @@ -77,7 +77,7 @@ class ArticleRouteSpec extends AnyWordSpec { "be return 10 articles with query params" in { client - .run(Request(method = Method.GET, uri = uri"/articles?page=1&limit=50")) + .run(Request(method = Method.GET, uri = uri"/v1/articles?page=1&limit=50")) .use { response => IO { assert(response.status === Ok) @@ -93,7 +93,7 @@ class ArticleRouteSpec extends AnyWordSpec { "not be return articles with query params" in { client - .run(Request(method = Method.GET, uri = uri"/articles?page=9999&limit=10")) + .run(Request(method = Method.GET, uri = uri"/v1/articles?page=9999&limit=10")) .use { response => IO { assert(response.status === NotFound) @@ -103,7 +103,7 @@ class ArticleRouteSpec extends AnyWordSpec { assert(maybeError.title === "Not Found") assert(maybeError.status === 404) assert(maybeError.detail === "articles not found") - assert(maybeError.instance === "/articles?page=9999&limit=10") + assert(maybeError.instance === "/v1/articles?page=9999&limit=10") } } .unsafeRunSync() @@ -111,7 +111,7 @@ class ArticleRouteSpec extends AnyWordSpec { "be return Method Not Allowed" in { client - .run(Request(method = Method.DELETE, uri = uri"/articles")) + .run(Request(method = Method.DELETE, uri = uri"/v1/articles")) .use { response => IO { assert(response.status === MethodNotAllowed) diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/AuthRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/AuthRouteSpec.scala similarity index 84% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/AuthRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/AuthRouteSpec.scala index 828433d4..c4330e52 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/AuthRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/AuthRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.client.Client @@ -14,8 +14,8 @@ import net.yoshinorin.qualtet.fixture.Fixture.{author, router, unsafeDecode} import org.scalatest.wordspec.AnyWordSpec import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.AuthRouteSpec -class AuthRouteSpec extends AnyWordSpec { +// testOnly net.yoshinorin.qualtet.http.routes.v1.AuthRouteSpec +class AuthRouteV1Spec extends AnyWordSpec { val a: ResponseAuthor = authorService.findByName(author.name).unsafeRunSync().get val client: Client[IO] = Client.fromHttpApp(router.routes.orNotFound) @@ -33,7 +33,7 @@ class AuthRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/token/", entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/token/", entity = entity)) .use { response => IO { assert(response.status === Ok) @@ -57,7 +57,7 @@ class AuthRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity(wrongJsonFormat) client - .run(Request(method = Method.POST, uri = uri"/token/", entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/token/", entity = entity)) .use { response => IO { assert(response.status === Unauthorized) @@ -77,7 +77,7 @@ class AuthRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity(wrongJson) client - .run(Request(method = Method.POST, uri = uri"/token/", entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/token/", entity = entity)) .use { response => IO { assert(response.status === Unauthorized) @@ -97,7 +97,7 @@ class AuthRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity(wrongJson) client - .run(Request(method = Method.POST, uri = uri"/token/", entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/token/", entity = entity)) .use { response => IO { assert(response.status === Unauthorized) @@ -118,7 +118,7 @@ class AuthRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/token/", entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/token/", entity = entity)) .use { response => IO { assert(response.status === Unauthorized) @@ -139,7 +139,7 @@ class AuthRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/token/", entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/token/", entity = entity)) .use { response => IO { assert(response.status === NotFound) @@ -149,7 +149,7 @@ class AuthRouteSpec extends AnyWordSpec { assert(maybeError.title === "Not Found") assert(maybeError.status === 404) assert(maybeError.detail === "not-exists-user is not found.") - assert(maybeError.instance === "/token/") + assert(maybeError.instance === "/v1/token/") } } .unsafeRunSync() @@ -157,7 +157,7 @@ class AuthRouteSpec extends AnyWordSpec { "be return Method Not Allowed" in { client - .run(Request(method = Method.DELETE, uri = uri"/token")) + .run(Request(method = Method.DELETE, uri = uri"/v1/token")) .use { response => IO { assert(response.status === MethodNotAllowed) diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/AuthorRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/AuthorRouteSpec.scala similarity index 85% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/AuthorRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/AuthorRouteSpec.scala index 4db29af6..67357e40 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/AuthorRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/AuthorRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.client.Client @@ -13,8 +13,8 @@ import net.yoshinorin.qualtet.Modules.* import org.scalatest.wordspec.AnyWordSpec import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.AuthorRouteSpec -class AuthorRouteSpec extends AnyWordSpec { +// testOnly net.yoshinorin.qualtet.http.routes.v1.AuthorRouteSpec +class AuthorRouteV1Spec extends AnyWordSpec { val authorRoute = new AuthorRoute(authorService) @@ -42,7 +42,7 @@ class AuthorRouteSpec extends AnyWordSpec { """.stripMargin.replaceAll("\n", "").replaceAll(" ", "") client - .run(Request(method = Method.GET, uri = uri"/authors/")) + .run(Request(method = Method.GET, uri = uri"/v1/authors/")) .use { response => IO { assert(response.status === Ok) @@ -65,7 +65,7 @@ class AuthorRouteSpec extends AnyWordSpec { """.stripMargin.replaceAll("\n", "").replaceAll(" ", "") client - .run(Request(method = Method.GET, uri = uri"/authors/jhondue")) + .run(Request(method = Method.GET, uri = uri"/v1/authors/jhondue")) .use { response => IO { assert(response.status === Ok) @@ -78,7 +78,7 @@ class AuthorRouteSpec extends AnyWordSpec { "be return 404" in { client - .run(Request(method = Method.GET, uri = uri"/authors/jhondue-not-exists")) + .run(Request(method = Method.GET, uri = uri"/v1/authors/jhondue-not-exists")) .use { response => IO { assert(response.status === NotFound) @@ -88,7 +88,7 @@ class AuthorRouteSpec extends AnyWordSpec { assert(maybeError.title === "Not Found") assert(maybeError.status === 404) assert(maybeError.detail === "Not Found") - assert(maybeError.instance === "/authors/jhondue-not-exists") + assert(maybeError.instance === "/v1/authors/jhondue-not-exists") } } .unsafeRunSync() @@ -96,7 +96,7 @@ class AuthorRouteSpec extends AnyWordSpec { "be return Method Not Allowed" in { client - .run(Request(method = Method.DELETE, uri = uri"/authors")) + .run(Request(method = Method.DELETE, uri = uri"/v1/authors")) .use { response => IO { assert(response.status === MethodNotAllowed) diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/CacheRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/CacheRouteSpec.scala similarity index 74% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/CacheRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/CacheRouteSpec.scala index 7b4612d5..280f0624 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/CacheRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/CacheRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.client.Client @@ -14,7 +14,7 @@ import net.yoshinorin.qualtet.auth.RequestToken import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.CacheRouteSpec +// testOnly net.yoshinorin.qualtet.http.routes.v1.CacheRouteSpec class CacheRouteSpec extends AnyWordSpec { val validAuthor: ResponseAuthor = authorService.findByName(author.name).unsafeRunSync().get @@ -25,7 +25,7 @@ class CacheRouteSpec extends AnyWordSpec { "be invalidate all caches" in { client - .run(Request(method = Method.DELETE, uri = uri"/caches/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)))) + .run(Request(method = Method.DELETE, uri = uri"/v1/caches/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)))) .use { response => IO { assert(response.status === NoContent) @@ -37,7 +37,7 @@ class CacheRouteSpec extends AnyWordSpec { "be reject caused by expired token" in { client - .run(Request(method = Method.DELETE, uri = uri"/caches/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + expiredToken)))) + .run(Request(method = Method.DELETE, uri = uri"/v1/caches/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + expiredToken)))) .use { response => IO { assert(response.status === Unauthorized) @@ -49,7 +49,7 @@ class CacheRouteSpec extends AnyWordSpec { "be reject caused by the authorization header is empty" in { client - .run(Request(method = Method.DELETE, uri = uri"/caches/")) + .run(Request(method = Method.DELETE, uri = uri"/v1/caches/")) .use { response => IO { assert(response.status === Unauthorized) @@ -61,7 +61,7 @@ class CacheRouteSpec extends AnyWordSpec { "be reject caused by invalid token" ignore { client - .run(Request(method = Method.DELETE, uri = uri"/caches/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + "invalidToken")))) + .run(Request(method = Method.DELETE, uri = uri"/v1/caches/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + "invalidToken")))) .use { response => IO { assert(response.status === Unauthorized) @@ -73,7 +73,7 @@ class CacheRouteSpec extends AnyWordSpec { "be return user not found" in { client - .run(Request(method = Method.DELETE, uri = uri"/caches/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + nonExistsUserToken)))) + .run(Request(method = Method.DELETE, uri = uri"/v1/caches/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + nonExistsUserToken)))) .use { response => IO { assert(response.status === Unauthorized) @@ -85,7 +85,7 @@ class CacheRouteSpec extends AnyWordSpec { "be return Method Not Allowed" in { client - .run(Request(method = Method.POST, uri = uri"/caches", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)))) + .run(Request(method = Method.POST, uri = uri"/v1/caches", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)))) .use { response => IO { assert(response.status === MethodNotAllowed) diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/ContentRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/ContentRouteSpec.scala similarity index 86% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/ContentRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/ContentRouteSpec.scala index 645f0141..8b9ad175 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/ContentRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/ContentRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.client.Client @@ -20,8 +20,8 @@ import java.time.Instant import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.ContentRouteSpec -class ContentRouteSpec extends AnyWordSpec { +// testOnly net.yoshinorin.qualtet.http.routes.v1.ContentRouteSpec +class ContentRouteV1Spec extends AnyWordSpec { val validAuthor: ResponseAuthor = authorService.findByName(author.name).unsafeRunSync().get val validToken: String = authService.generateToken(RequestToken(validAuthor.id, "pass")).unsafeRunSync().token @@ -44,7 +44,7 @@ class ContentRouteSpec extends AnyWordSpec { """.stripMargin val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) .use { response => IO { assert(response.status === Created) @@ -81,7 +81,7 @@ class ContentRouteSpec extends AnyWordSpec { """.stripMargin val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/contents/", headers = Headers(Header.Raw(ci"Authorization", "bearer " + validToken)), entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/contents/", headers = Headers(Header.Raw(ci"Authorization", "bearer " + validToken)), entity = entity)) .use { response => IO { assert(response.status === Created) @@ -100,7 +100,7 @@ class ContentRouteSpec extends AnyWordSpec { .run( Request( method = Method.DELETE, - uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/contents/${content.id.value}")), + uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/v1/contents/${content.id.value}")), headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)) ) ) @@ -117,7 +117,7 @@ class ContentRouteSpec extends AnyWordSpec { .run( Request( method = Method.DELETE, - uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/contents/${content.id.value}")), + uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/v1/contents/${content.id.value}")), headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)) ) ) @@ -130,7 +130,7 @@ class ContentRouteSpec extends AnyWordSpec { assert(maybeError.title === "Not Found") assert(maybeError.status === 404) assert(maybeError.detail.startsWith("content not found: ")) - assert(maybeError.instance === s"/contents/${content.id.value}") + assert(maybeError.instance === s"/v1/contents/${content.id.value}") } } .unsafeRunSync() @@ -143,7 +143,7 @@ class ContentRouteSpec extends AnyWordSpec { .run( Request( method = Method.DELETE, - uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/contents/${id.value}")), + uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/v1/contents/${id.value}")), headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)) ) ) @@ -156,7 +156,7 @@ class ContentRouteSpec extends AnyWordSpec { assert(maybeError.title === "Not Found") assert(maybeError.status === 404) assert(maybeError.detail === s"content not found: ${id.value}") - assert(maybeError.instance === s"/contents/${id.value}") + assert(maybeError.instance === s"/v1/contents/${id.value}") } } .unsafeRunSync() @@ -167,7 +167,7 @@ class ContentRouteSpec extends AnyWordSpec { .run( Request( method = Method.DELETE, - uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/contents/reject")), + uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/v1/contents/reject")), headers = Headers(Header.Raw(ci"Authorization", "Bearer invalid token")) ) ) @@ -197,7 +197,7 @@ class ContentRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) .use { response => IO { assert(response.status === BadRequest) @@ -207,7 +207,7 @@ class ContentRouteSpec extends AnyWordSpec { assert(maybeError.title === "Bad Request") assert(maybeError.status === 400) assert(maybeError.detail === "title required.") - assert(maybeError.instance === s"/contents/") + assert(maybeError.instance === s"/v1/contents/") } } .unsafeRunSync() @@ -230,7 +230,7 @@ class ContentRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) .use { response => IO { assert(response.status === BadRequest) @@ -240,7 +240,7 @@ class ContentRouteSpec extends AnyWordSpec { assert(maybeError.title === "Bad Request") assert(maybeError.status === 400) assert(maybeError.detail === "rawContent required.") - assert(maybeError.instance === s"/contents/") + assert(maybeError.instance === s"/v1/contents/") } } .unsafeRunSync() @@ -263,7 +263,7 @@ class ContentRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) .use { response => IO { assert(response.status === BadRequest) @@ -273,7 +273,7 @@ class ContentRouteSpec extends AnyWordSpec { assert(maybeError.title === "Bad Request") assert(maybeError.status === 400) assert(maybeError.detail === "htmlContent required.") - assert(maybeError.instance === s"/contents/") + assert(maybeError.instance === s"/v1/contents/") } } .unsafeRunSync() @@ -296,7 +296,9 @@ class ContentRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + expiredToken)), entity = entity)) + .run( + Request(method = Method.POST, uri = uri"/v1/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + expiredToken)), entity = entity) + ) .use { response => IO { assert(response.status === Unauthorized) @@ -308,7 +310,7 @@ class ContentRouteSpec extends AnyWordSpec { "be reject POST endpoint caused by the authorization header is empty" in { client - .run(Request(method = Method.POST, uri = uri"/contents/")) + .run(Request(method = Method.POST, uri = uri"/v1/contents/")) .use { response => IO { assert(response.status === Unauthorized) @@ -322,7 +324,12 @@ class ContentRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity("") client .run( - Request(method = Method.POST, uri = uri"/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + "invalid Token")), entity = entity) + Request( + method = Method.POST, + uri = uri"/v1/contents/", + headers = Headers(Header.Raw(ci"Authorization", "Bearer " + "invalid Token")), + entity = entity + ) ) .use { response => IO { @@ -339,7 +346,7 @@ class ContentRouteSpec extends AnyWordSpec { .run( Request( method = Method.POST, - uri = uri"/contents/", + uri = uri"/v1/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + nonExistsUserToken)), entity = entity ) @@ -370,7 +377,7 @@ class ContentRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity(wrongJsonFormat) client - .run(Request(method = Method.POST, uri = uri"/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) .use { response => IO { assert(response.status === BadRequest) @@ -380,7 +387,7 @@ class ContentRouteSpec extends AnyWordSpec { assert(maybeError.title === "Bad Request") assert(maybeError.status === 400) assert(maybeError.detail === "Wrong JSON format or missing required field. Please see API document.") - assert(maybeError.instance === s"/contents/") + assert(maybeError.instance === s"/v1/contents/") } } .unsafeRunSync() @@ -398,7 +405,7 @@ class ContentRouteSpec extends AnyWordSpec { val entity = EntityEncoder[IO, String].toEntity(wrongJsonFormat) client - .run(Request(method = Method.POST, uri = uri"/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/contents/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) .use { response => IO { assert(response.status === BadRequest) @@ -408,7 +415,7 @@ class ContentRouteSpec extends AnyWordSpec { assert(maybeError.title === "Bad Request") assert(maybeError.status === 400) assert(maybeError.detail === "Wrong JSON format or missing required field. Please see API document.") - assert(maybeError.instance === s"/contents/") + assert(maybeError.instance === s"/v1/contents/") } } .unsafeRunSync() @@ -439,7 +446,7 @@ class ContentRouteSpec extends AnyWordSpec { .run( Request( method = Method.GET, - uri = uri"/contents/test/content/route/spec/2" + uri = uri"/v1/contents/test/content/route/spec/2" ) ) .use { response => @@ -480,7 +487,7 @@ class ContentRouteSpec extends AnyWordSpec { .run( Request( method = Method.GET, - uri = uri"/contents/this/is/a/404" + uri = uri"/v1/contents/this/is/a/404" ) ) .use { response => @@ -492,7 +499,7 @@ class ContentRouteSpec extends AnyWordSpec { assert(maybeError.title === "Not Found") assert(maybeError.status === 404) assert(maybeError.detail === "Not Found") - assert(maybeError.instance === "/contents/this/is/a/404") + assert(maybeError.instance === "/v1/contents/this/is/a/404") } } .unsafeRunSync() @@ -503,7 +510,7 @@ class ContentRouteSpec extends AnyWordSpec { .run( Request( method = Method.GET, - uri = uri"/contents/this/is/a/404/" + uri = uri"/v1/contents/this/is/a/404/" ) ) .use { response => @@ -515,7 +522,7 @@ class ContentRouteSpec extends AnyWordSpec { assert(maybeError.title === "Not Found") assert(maybeError.status === 404) assert(maybeError.detail === "Not Found") - assert(maybeError.instance === "/contents/this/is/a/404/") + assert(maybeError.instance === "/v1/contents/this/is/a/404/") } } .unsafeRunSync() @@ -526,7 +533,7 @@ class ContentRouteSpec extends AnyWordSpec { .run( Request( method = Method.PATCH, - uri = uri"/contents/this/is/a/404/", + uri = uri"/v1/contents/this/is/a/404/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)) ) ) diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/ContentTypeRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/ContentTypeRouteSpec.scala similarity index 81% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/ContentTypeRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/ContentTypeRouteSpec.scala index c56ce9fc..7470d03e 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/ContentTypeRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/ContentTypeRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.client.Client @@ -13,8 +13,8 @@ import org.scalatest.wordspec.AnyWordSpec import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.ContentTypeRouteSpec -class ContentTypeRouteSpec extends AnyWordSpec { +// testOnly net.yoshinorin.qualtet.http.routes.v1.ContentTypeRouteSpec +class ContentTypeRouteV1Spec extends AnyWordSpec { val client: Client[IO] = Client.fromHttpApp(router.routes.orNotFound) @@ -22,7 +22,7 @@ class ContentTypeRouteSpec extends AnyWordSpec { "be return content-types" in { client - .run(Request(method = Method.GET, uri = uri"/content-types/")) + .run(Request(method = Method.GET, uri = uri"/v1/content-types/")) .use { response => IO { assert(response.status === Ok) @@ -37,7 +37,7 @@ class ContentTypeRouteSpec extends AnyWordSpec { "be return content-type:articles" in { client - .run(Request(method = Method.GET, uri = uri"/content-types/article")) + .run(Request(method = Method.GET, uri = uri"/v1/content-types/article")) .use { response => IO { assert(response.status === Ok) @@ -53,7 +53,7 @@ class ContentTypeRouteSpec extends AnyWordSpec { "be return content-type:page" in { client - .run(Request(method = Method.GET, uri = uri"/content-types/page")) + .run(Request(method = Method.GET, uri = uri"/v1/content-types/page")) .use { response => IO { assert(response.status === Ok) @@ -69,7 +69,7 @@ class ContentTypeRouteSpec extends AnyWordSpec { "be return content-type:not-exists" in { client - .run(Request(method = Method.GET, uri = uri"/content-types/not-exists")) + .run(Request(method = Method.GET, uri = uri"/v1/content-types/not-exists")) .use { response => IO { assert(response.status === NotFound) @@ -79,7 +79,7 @@ class ContentTypeRouteSpec extends AnyWordSpec { assert(maybeError.title === "Not Found") assert(maybeError.status === 404) assert(maybeError.detail === "Not Found") - assert(maybeError.instance === "/content-types/not-exists") + assert(maybeError.instance === "/v1/content-types/not-exists") } } .unsafeRunSync() @@ -87,7 +87,7 @@ class ContentTypeRouteSpec extends AnyWordSpec { "be return Method Not Allowed" in { client - .run(Request(method = Method.DELETE, uri = uri"/content-types")) + .run(Request(method = Method.DELETE, uri = uri"/v1/content-types")) .use { response => IO { assert(response.status === MethodNotAllowed) diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/FeedRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/FeedRouteSpec.scala similarity index 81% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/FeedRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/FeedRouteSpec.scala index 96093891..38895b4d 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/FeedRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/FeedRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.client.Client @@ -13,8 +13,8 @@ import org.scalatest.BeforeAndAfterAll import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.FeedRouteSpec -class FeedRouteSpec extends AnyWordSpec with BeforeAndAfterAll { +// testOnly net.yoshinorin.qualtet.http.routes.v1.FeedRouteSpec +class FeedRouteV1Spec extends AnyWordSpec with BeforeAndAfterAll { val requestContents = makeRequestContents(2, "feedsRoute") @@ -28,7 +28,7 @@ class FeedRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "FeedRoute" should { "be return feeds" in { client - .run(Request(method = Method.GET, uri = uri"/feeds/index")) + .run(Request(method = Method.GET, uri = uri"/v1/feeds/index")) .use { response => IO { assert(response.status === Ok) @@ -44,7 +44,7 @@ class FeedRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be return Method Not Allowed" in { client - .run(Request(method = Method.DELETE, uri = uri"/feeds/index")) + .run(Request(method = Method.DELETE, uri = uri"/v1/feeds/index")) .use { response => IO { assert(response.status === MethodNotAllowed) diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/SearchRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/SearchRouteSpec.scala similarity index 85% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/SearchRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/SearchRouteSpec.scala index 3ba0fc66..51d1d139 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/SearchRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/SearchRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.client.Client @@ -16,8 +16,8 @@ import org.scalatest.BeforeAndAfterAll import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.SearchRouteSpec -class SearchRouteSpec extends AnyWordSpec with BeforeAndAfterAll { +// testOnly net.yoshinorin.qualtet.http.routes.v1.SearchRouteSpec +class SearchRouteV1Spec extends AnyWordSpec with BeforeAndAfterAll { val requestContents: List[RequestContent] = { (0 until 49).toList @@ -55,7 +55,7 @@ class SearchRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "SearchRoute" should { "be return search result" in { client - .run(Request(method = Method.GET, uri = uri"/search/?q=searchRoute")) + .run(Request(method = Method.GET, uri = uri"/v1/search/?q=searchRoute")) .use { response => IO { assert(response.status === Ok) @@ -76,7 +76,7 @@ class SearchRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be return UnprocessableEntity without query params" in { client - .run(Request(method = Method.GET, uri = uri"/search/")) + .run(Request(method = Method.GET, uri = uri"/v1/search/")) .use { response => IO { assert(response.status === UnprocessableEntity) @@ -86,7 +86,7 @@ class SearchRouteSpec extends AnyWordSpec with BeforeAndAfterAll { assert(maybeError.title === "Unprocessable Entity") assert(maybeError.status === 422) assert(maybeError.detail === "SEARCH_QUERY_REQUIRED") - assert(maybeError.instance === "/search/") + assert(maybeError.instance === "/v1/search/") } } .unsafeRunSync() @@ -94,7 +94,7 @@ class SearchRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be return UnprocessableEntity with query param short value" in { client - .run(Request(method = Method.GET, uri = uri"/search/?q=abc")) + .run(Request(method = Method.GET, uri = uri"/v1/search/?q=abc")) .use { response => IO { assert(response.status === UnprocessableEntity) @@ -104,7 +104,7 @@ class SearchRouteSpec extends AnyWordSpec with BeforeAndAfterAll { assert(maybeError.title === "Unprocessable Entity") assert(maybeError.status === 422) assert(maybeError.detail === "SEARCH_CHAR_LENGTH_TOO_SHORT") - assert(maybeError.instance === "/search/?q=abc") + assert(maybeError.instance === "/v1/search/?q=abc") } } .unsafeRunSync() @@ -112,7 +112,7 @@ class SearchRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be return UnprocessableEntity with invalid query param" in { client - .run(Request(method = Method.GET, uri = uri"/search/?invalid=abcd")) + .run(Request(method = Method.GET, uri = uri"/v1/search/?invalid=abcd")) .use { response => IO { assert(response.status === UnprocessableEntity) @@ -122,7 +122,7 @@ class SearchRouteSpec extends AnyWordSpec with BeforeAndAfterAll { assert(maybeError.title === "Unprocessable Entity") assert(maybeError.status === 422) assert(maybeError.detail === "SEARCH_QUERY_REQUIRED") - assert(maybeError.instance === "/search/?invalid=abcd") + assert(maybeError.instance === "/v1/search/?invalid=abcd") } } .unsafeRunSync() @@ -130,7 +130,7 @@ class SearchRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be return UnprocessableEntity with query param contains invalid values" in { client - .run(Request(method = Method.GET, uri = uri"/search/?q=a.b.c")) + .run(Request(method = Method.GET, uri = uri"/v1/search/?q=a.b.c")) .use { response => IO { assert(response.status === UnprocessableEntity) @@ -140,7 +140,7 @@ class SearchRouteSpec extends AnyWordSpec with BeforeAndAfterAll { assert(maybeError.title === "Unprocessable Entity") assert(maybeError.status === 422) assert(maybeError.detail === "INVALID_CHARS_INCLUDED") - assert(maybeError.instance === "/search/?q=a.b.c") + assert(maybeError.instance === "/v1/search/?q=a.b.c") } } .unsafeRunSync() @@ -148,7 +148,7 @@ class SearchRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be return UnprocessableEntity with too many query params" in { client - .run(Request(method = Method.GET, uri = uri"/search/?q=abcd&q=abcd&q=abcd&q=abcd")) + .run(Request(method = Method.GET, uri = uri"/v1/search/?q=abcd&q=abcd&q=abcd&q=abcd")) .use { response => IO { assert(response.status === UnprocessableEntity) @@ -158,7 +158,7 @@ class SearchRouteSpec extends AnyWordSpec with BeforeAndAfterAll { assert(maybeError.title === "Unprocessable Entity") assert(maybeError.status === 422) assert(maybeError.detail === "TOO_MANY_SEARCH_WORDS") - assert(maybeError.instance === "/search/?q=abcd&q=abcd&q=abcd&q=abcd") + assert(maybeError.instance === "/v1/search/?q=abcd&q=abcd&q=abcd&q=abcd") } } .unsafeRunSync() diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/SeriesRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/SeriesRouteSpec.scala similarity index 84% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/SeriesRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/SeriesRouteSpec.scala index ceb6e38d..a4945315 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/SeriesRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/SeriesRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.client.Client @@ -18,8 +18,8 @@ import org.scalatest.BeforeAndAfterAll import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.SeriesRouteSpec -class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { +// testOnly net.yoshinorin.qualtet.http.routes.v1.SeriesRouteSpec +class SeriesRouteV1Spec extends AnyWordSpec with BeforeAndAfterAll { val requestSeries: List[RequestSeries] = List( RequestSeries( @@ -48,8 +48,8 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { val validAuthor: ResponseAuthor = authorService.findByName(author.name).unsafeRunSync().get val validToken: String = authService.generateToken(RequestToken(validAuthor.id, "pass")).unsafeRunSync().token - val seriesRoute = new SeriesRoute(authProvider, seriesService) - val client: Client[IO] = Client.fromHttpApp(makeRouter(seriesRoute = seriesRoute).routes.orNotFound) + val seriesRouteV1 = new SeriesRoute(authProvider, seriesService) + val client: Client[IO] = Client.fromHttpApp(makeRouter(seriesRouteV1 = seriesRouteV1).routes.orNotFound) "SeriesRoute" should { @@ -67,7 +67,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { """.stripMargin val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/series/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/series/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) .use { response => IO { assert(response.status === Created) @@ -105,7 +105,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { """.stripMargin.replaceAll("\n", "").replaceAll(" ", "") client - .run(Request(method = Method.GET, uri = uri"/series/")) + .run(Request(method = Method.GET, uri = uri"/v1/series/")) .use { response => IO { assert(response.status === Ok) @@ -119,7 +119,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be return specific series" in { client - .run(Request(method = Method.GET, uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/series/${s1.name.value}")))) + .run(Request(method = Method.GET, uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/v1/series/${s1.name.value}")))) .use { response => IO { assert(response.status === Ok) @@ -138,7 +138,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be return 404" in { client - .run(Request(method = Method.GET, uri = uri"/series/not-exists")) + .run(Request(method = Method.GET, uri = uri"/v1/series/not-exists")) .use { response => IO { assert(response.status === NotFound) @@ -148,7 +148,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { assert(maybeError.title === "Not Found") assert(maybeError.status === 404) assert(maybeError.detail === "series not found: not-exists") - assert(maybeError.instance === "/series/not-exists") + assert(maybeError.instance === "/v1/series/not-exists") } } .unsafeRunSync() @@ -159,7 +159,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { .run( Request( method = Method.PATCH, - uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/series/${s1.name.value}")), + uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/v1/series/${s1.name.value}")), headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)) ) ) @@ -184,7 +184,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/series/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/series/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) .use { response => IO { assert(response.status === BadRequest) @@ -194,7 +194,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { assert(maybeError.title === "Bad Request") assert(maybeError.status === 400) assert(maybeError.detail === "name is required") - assert(maybeError.instance === "/series/") + assert(maybeError.instance === "/v1/series/") } } .unsafeRunSync() @@ -212,7 +212,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/series/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/series/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)), entity = entity)) .use { response => IO { assert(response.status === BadRequest) @@ -222,7 +222,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { assert(maybeError.title === "Bad Request") assert(maybeError.status === 400) assert(maybeError.detail === "title is required") - assert(maybeError.instance === "/series/") + assert(maybeError.instance === "/v1/series/") } } .unsafeRunSync() @@ -239,7 +239,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { """.stripMargin val entity = EntityEncoder[IO, String].toEntity(json) client - .run(Request(method = Method.POST, uri = uri"/series/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + expiredToken)), entity = entity)) + .run(Request(method = Method.POST, uri = uri"/v1/series/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + expiredToken)), entity = entity)) .use { response => IO { @@ -252,7 +252,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be reject POST endpoint caused by the authorization header is empty" in { client - .run(Request(method = Method.POST, uri = uri"/series/")) + .run(Request(method = Method.POST, uri = uri"/v1/series/")) .use { response => IO { assert(response.status === Unauthorized) @@ -266,7 +266,7 @@ class SeriesRouteSpec extends AnyWordSpec with BeforeAndAfterAll { val entity = EntityEncoder[IO, String].toEntity("") client .run( - Request(method = Method.POST, uri = uri"/series/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + "invalid Token")), entity = entity) + Request(method = Method.POST, uri = uri"/v1/series/", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + "invalid Token")), entity = entity) ) .use { response => IO { diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/SitemapRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/SitemapRouteSpec.scala similarity index 82% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/SitemapRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/SitemapRouteSpec.scala index 9be4300c..d6e6dbf5 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/SitemapRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/SitemapRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.client.Client @@ -13,8 +13,8 @@ import org.scalatest.BeforeAndAfterAll import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.SitemapRouteSpec -class SitemapRouteSpec extends AnyWordSpec with BeforeAndAfterAll { +// testOnly net.yoshinorin.qualtet.http.routes.v1.SitemapRouteSpec +class SitemapRouteV1Spec extends AnyWordSpec with BeforeAndAfterAll { val requestContents = makeRequestContents(2, "sitemapRoute") @@ -28,7 +28,7 @@ class SitemapRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "SitemapRoute" should { "be return json for sitemap.xml" in { client - .run(Request(method = Method.GET, uri = uri"/sitemaps")) + .run(Request(method = Method.GET, uri = uri"/v1/sitemaps")) .use { response => IO { assert(response.status === Ok) @@ -44,7 +44,7 @@ class SitemapRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be return Method Not Allowed" in { client - .run(Request(method = Method.DELETE, uri = uri"/sitemaps")) + .run(Request(method = Method.DELETE, uri = uri"/v1/sitemaps")) .use { response => IO { assert(response.status === MethodNotAllowed) diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/SystemRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/SystemRouteSpec.scala similarity index 73% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/SystemRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/SystemRouteSpec.scala index bee1fc0d..4cbca710 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/SystemRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/SystemRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.client.Client @@ -14,22 +14,22 @@ import net.yoshinorin.qualtet.fixture.Fixture.{makeRouter, unsafeDecode} import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.SystemRouteSpec -class SystemRouteSpec extends AnyWordSpec { +// testOnly net.yoshinorin.qualtet.http.routes.v1.SystemRouteSpec +class SystemRouteV1Spec extends AnyWordSpec { - val systemRoute: SystemRoute = new SystemRoute(HttpSystemEndpointConfig(metadata = HttpSystemEndpointMetadata(enabled = false))) - val router = makeRouter(systemRoute = systemRoute) + val systemRouteV1: SystemRoute = new SystemRoute(HttpSystemEndpointConfig(metadata = HttpSystemEndpointMetadata(enabled = false))) + val router = makeRouter(systemRouteV1 = systemRouteV1) val client: Client[IO] = Client.fromHttpApp(router.routes.orNotFound) - val enabledMetadataEndpointSystemRoute: SystemRoute = new SystemRoute(HttpSystemEndpointConfig(metadata = HttpSystemEndpointMetadata(enabled = true))) - val enabledMetadataEndpointRouter = makeRouter(systemRoute = enabledMetadataEndpointSystemRoute) + val enabledMetadataEndpointSystemRouteV1: SystemRoute = new SystemRoute(HttpSystemEndpointConfig(metadata = HttpSystemEndpointMetadata(enabled = true))) + val enabledMetadataEndpointRouter = makeRouter(systemRouteV1 = enabledMetadataEndpointSystemRouteV1) val clientForEnabledMetadataEndpoint: Client[IO] = Client.fromHttpApp(enabledMetadataEndpointRouter.routes.orNotFound) "SystemRoute" should { "be return Method Not Allowed" in { client - .run(Request(method = Method.DELETE, uri = uri"/system")) + .run(Request(method = Method.DELETE, uri = uri"/v1/system")) .use { response => IO { assert(response.status === MethodNotAllowed) @@ -42,7 +42,7 @@ class SystemRouteSpec extends AnyWordSpec { "health" should { "be return 200" in { client - .run(Request(method = Method.GET, uri = uri"/system/health")) + .run(Request(method = Method.GET, uri = uri"/v1/system/health")) .use { response => IO { assert(response.status === Ok) @@ -56,7 +56,7 @@ class SystemRouteSpec extends AnyWordSpec { "metadata" should { "be return 200 if config.http.endpoints.system.metadata is enabled" in { clientForEnabledMetadataEndpoint - .run(Request(method = Method.GET, uri = uri"/system/metadata")) + .run(Request(method = Method.GET, uri = uri"/v1/system/metadata")) .use { response => IO { assert(response.status === Ok) @@ -75,7 +75,7 @@ class SystemRouteSpec extends AnyWordSpec { "be return 404 if config.http.endpoints.system.metadata is disabled" in { client - .run(Request(method = Method.GET, uri = uri"/system/metadata")) + .run(Request(method = Method.GET, uri = uri"/v1/system/metadata")) .use { response => IO { assert(response.status === NotFound) diff --git a/src/test/scala/net/yoshinorin/qualtet/http/routes/TagRouteSpec.scala b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/TagRouteSpec.scala similarity index 88% rename from src/test/scala/net/yoshinorin/qualtet/http/routes/TagRouteSpec.scala rename to src/test/scala/net/yoshinorin/qualtet/http/routes/v1/TagRouteSpec.scala index 1cd480e7..6e9c4dd6 100644 --- a/src/test/scala/net/yoshinorin/qualtet/http/routes/TagRouteSpec.scala +++ b/src/test/scala/net/yoshinorin/qualtet/http/routes/v1/TagRouteSpec.scala @@ -1,4 +1,4 @@ -package net.yoshinorin.qualtet.http.routes +package net.yoshinorin.qualtet.http.routes.v1 import cats.effect.IO import org.http4s.client.Client @@ -19,8 +19,8 @@ import org.scalatest.BeforeAndAfterAll import cats.effect.unsafe.implicits.global -// testOnly net.yoshinorin.qualtet.http.routes.TagRouteSpec -class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { +// testOnly net.yoshinorin.qualtet.http.routes.v1.TagRouteSpec +class TagRouteV1Spec extends AnyWordSpec with BeforeAndAfterAll { val requestContents = makeRequestContents(10, "tagRoute") // NOTE: create content and related data for test @@ -35,8 +35,8 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { val validAuthor: ResponseAuthor = authorService.findByName(author.name).unsafeRunSync().get val validToken: String = authService.generateToken(RequestToken(validAuthor.id, "pass")).unsafeRunSync().token - val tagRoute = new TagRoute(authProvider, tagService, articleService) - val client: Client[IO] = Client.fromHttpApp(makeRouter(tagRoute = tagRoute).routes.orNotFound) + val tagRouteV1 = new TagRoute(authProvider, tagService, articleService) + val client: Client[IO] = Client.fromHttpApp(makeRouter(tagRouteV1 = tagRouteV1).routes.orNotFound) "TagRoute" should { @@ -58,7 +58,7 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { """.stripMargin.replaceAll("\n", "").replaceAll(" ", "") client - .run(Request(method = Method.GET, uri = uri"/tags/")) + .run(Request(method = Method.GET, uri = uri"/v1/tags/")) .use { response => IO { assert(response.status === Ok) @@ -71,7 +71,7 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be return specific tag" in { client - .run(Request(method = Method.GET, uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/tags/${t(0).name.value}")))) + .run(Request(method = Method.GET, uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/v1/tags/${t(0).name.value}")))) .use { response => IO { assert(response.status === Ok) @@ -85,7 +85,7 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { .unsafeRunSync() client - .run(Request(method = Method.GET, uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/tags/${t(1).name.value}")))) + .run(Request(method = Method.GET, uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/v1/tags/${t(1).name.value}")))) .use { response => IO { assert(response.status === Ok) @@ -132,7 +132,7 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be return 404" in { client - .run(Request(method = Method.GET, uri = uri"/tags/not-exists")) + .run(Request(method = Method.GET, uri = uri"/v1/tags/not-exists")) .use { response => IO { assert(response.status === NotFound) @@ -142,7 +142,7 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { assert(maybeError.title === "Not Found") assert(maybeError.status === 404) assert(maybeError.detail.startsWith("articles not found")) - assert(maybeError.instance === "/tags/not-exists") + assert(maybeError.instance === "/v1/tags/not-exists") } } .unsafeRunSync() @@ -156,7 +156,7 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { .run( Request( method = Method.DELETE, - uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/tags/${tag.id.value}")), + uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/v1/tags/${tag.id.value}")), headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)) ) ) @@ -174,7 +174,7 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { .run( Request( method = Method.DELETE, - uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/tags/${tag.id.value}")), + uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/v1/tags/${tag.id.value}")), headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)) ) ) @@ -187,7 +187,7 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { assert(maybeError.title === "Not Found") assert(maybeError.status === 404) assert(maybeError.detail.startsWith("tag not found: ")) - assert(maybeError.instance === s"/tags/${tag.id.value}") + assert(maybeError.instance === s"/v1/tags/${tag.id.value}") } } .unsafeRunSync() @@ -199,7 +199,7 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { .run( Request( method = Method.DELETE, - uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/tags/${id.value}")), + uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/v1/tags/${id.value}")), headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)) ) ) @@ -212,7 +212,7 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { assert(maybeError.title === "Not Found") assert(maybeError.status === 404) assert(maybeError.detail.startsWith("tag not found: ")) - assert(maybeError.instance === s"/tags/${id.value}") + assert(maybeError.instance === s"/v1/tags/${id.value}") } } .unsafeRunSync() @@ -220,7 +220,7 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { "be reject DELETE endpoint caused by invalid token" in { client - .run(Request(method = Method.DELETE, uri = uri"/tags/reject", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + "invalid token")))) + .run(Request(method = Method.DELETE, uri = uri"/v1/tags/reject", headers = Headers(Header.Raw(ci"Authorization", "Bearer " + "invalid token")))) .use { response => IO { assert(response.status === Unauthorized) @@ -236,7 +236,7 @@ class TagRouteSpec extends AnyWordSpec with BeforeAndAfterAll { .run( Request( method = Method.PATCH, - uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/tags/${tag.id.value}")), + uri = new Uri().withPath(Uri.Path.unsafeFromString(s"/v1/tags/${tag.id.value}")), headers = Headers(Header.Raw(ci"Authorization", "Bearer " + validToken)) ) )