From 7eded21c9baa147e149c3f9b2e15f2784e48f3da Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Tue, 22 Oct 2019 15:03:04 +0200 Subject: [PATCH 01/14] fix: send url to `bitly` to have cool curl command fixes #154 --- .../__tests__/launcher-quarkus.spec.tsx | 6 +++++- .../frontend/src/code-quarkus/backend-api.ts | 18 +++++++++++++++++- .../frontend/src/code-quarkus/code-quarkus.tsx | 5 ++++- .../frontend/src/code-quarkus/next-steps.tsx | 2 ++ 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/main/frontend/src/code-quarkus/__tests__/launcher-quarkus.spec.tsx b/src/main/frontend/src/code-quarkus/__tests__/launcher-quarkus.spec.tsx index 915449a4b..b07e659de 100644 --- a/src/main/frontend/src/code-quarkus/__tests__/launcher-quarkus.spec.tsx +++ b/src/main/frontend/src/code-quarkus/__tests__/launcher-quarkus.spec.tsx @@ -65,7 +65,11 @@ jest.mock('../backend-api', () => ({ "order": 6 }, ]), - fetchConfig: () => { throw new Error("not used"); } + fetchConfig: () => { throw new Error("not used"); }, + shortenUrl: async (url: string) => Promise.resolve( + { + link: 'http://bit.ly/2Mzdn9Z' + }) })); diff --git a/src/main/frontend/src/code-quarkus/backend-api.ts b/src/main/frontend/src/code-quarkus/backend-api.ts index b19ebbdf9..1c2379020 100644 --- a/src/main/frontend/src/code-quarkus/backend-api.ts +++ b/src/main/frontend/src/code-quarkus/backend-api.ts @@ -18,4 +18,20 @@ export async function fetchConfig() { environment: 'dev' } } -} \ No newline at end of file +} + +export async function shortenUrl(downloadLink: string) { + const response = await fetch('https://api-ssl.bitly.com/v4/shorten', { + method: 'POST', + headers: { + 'Accept': 'application/json', + 'Content-Type': 'application/json', + 'Authorization': 'Bearer a3badec1a128e46e42f36a138165cbbd21ed6cee' + }, + body: JSON.stringify({ + 'group_guid': 'Bjam8omLBjB', + 'long_url': downloadLink + }) + }); + return response.ok ? await response.json() : undefined +} diff --git a/src/main/frontend/src/code-quarkus/code-quarkus.tsx b/src/main/frontend/src/code-quarkus/code-quarkus.tsx index c793ac63e..ec46c0b1a 100644 --- a/src/main/frontend/src/code-quarkus/code-quarkus.tsx +++ b/src/main/frontend/src/code-quarkus/code-quarkus.tsx @@ -8,6 +8,7 @@ import { CodeQuarkusForm } from './form'; import { Header } from './header'; import './code-quarkus.scss'; import { NextSteps } from './next-steps'; +import { shortenUrl } from './backend-api'; enum Status { EDITION = 'EDITION', RUNNING = 'RUNNING', COMPLETED = 'COMPLETED', ERROR = 'ERROR', DOWNLOADED = 'DOWNLOADED' @@ -47,8 +48,10 @@ async function generateProject(project: QuarkusProject): Promise<{ downloadLink: } const backendUrl = process.env.REACT_APP_BACKEND_URL || publicUrl; const downloadLink = `${backendUrl}/api/download?${stringify(params)}`; + + const data = await shortenUrl(downloadLink); window.open(downloadLink, '_blank'); - return { downloadLink }; + return { downloadLink: data ? data.link : downloadLink }; } const DEFAULT_PROJECT = { diff --git a/src/main/frontend/src/code-quarkus/next-steps.tsx b/src/main/frontend/src/code-quarkus/next-steps.tsx index 007c9836d..19204362b 100644 --- a/src/main/frontend/src/code-quarkus/next-steps.tsx +++ b/src/main/frontend/src/code-quarkus/next-steps.tsx @@ -13,6 +13,7 @@ export function NextSteps(props: NextStepsProps) { const close = (reset?: boolean) => { props.onClose && props.onClose(reset); }; + const curlCmd = 'curl --output code-with-quarkus.zip ' + (props.downloadLink as string).replace(/&/g, '\\&'); return (

Your download should start shortly. If it doesn't, please use the direct link:

+ $ {curlCmd}

What's next?

Unzip the project and start playing with Quarkus by running: From 4fbb15a2266a21d119a2c11a26f30753e6b0e922 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Wed, 23 Oct 2019 14:17:30 +0200 Subject: [PATCH 02/14] fix: move bitly code to backend --- pom.xml | 6 +++ .../__tests__/launcher-quarkus.spec.tsx | 5 +-- .../frontend/src/code-quarkus/backend-api.ts | 17 ++------- .../src/code-quarkus/code-quarkus.tsx | 4 +- .../io/quarkus/code/CodeQuarkusResource.kt | 38 ++++++++++++++++++- .../io/quarkus/code/model/BitlyResponse.kt | 5 +++ src/main/resources/application.properties | 5 ++- 7 files changed, 58 insertions(+), 22 deletions(-) create mode 100644 src/main/kotlin/io/quarkus/code/model/BitlyResponse.kt diff --git a/pom.xml b/pom.xml index 8359889d9..811e0d099 100644 --- a/pom.xml +++ b/pom.xml @@ -7,6 +7,7 @@ 1.0.0 1.8 + 1.8 1.3.21 2.22.2 3.3.0 @@ -80,6 +81,11 @@ commons-compress ${version.commons-compress} + + com.squareup.okhttp3 + okhttp + 4.2.2 + io.rest-assured rest-assured diff --git a/src/main/frontend/src/code-quarkus/__tests__/launcher-quarkus.spec.tsx b/src/main/frontend/src/code-quarkus/__tests__/launcher-quarkus.spec.tsx index b07e659de..93e27637d 100644 --- a/src/main/frontend/src/code-quarkus/__tests__/launcher-quarkus.spec.tsx +++ b/src/main/frontend/src/code-quarkus/__tests__/launcher-quarkus.spec.tsx @@ -66,10 +66,7 @@ jest.mock('../backend-api', () => ({ }, ]), fetchConfig: () => { throw new Error("not used"); }, - shortenUrl: async (url: string) => Promise.resolve( - { - link: 'http://bit.ly/2Mzdn9Z' - }) + shortenUrl: async (params: string) => Promise.resolve('http://bit.ly/2Mzdn9Z') })); diff --git a/src/main/frontend/src/code-quarkus/backend-api.ts b/src/main/frontend/src/code-quarkus/backend-api.ts index 1c2379020..cfa3b8fa3 100644 --- a/src/main/frontend/src/code-quarkus/backend-api.ts +++ b/src/main/frontend/src/code-quarkus/backend-api.ts @@ -20,18 +20,7 @@ export async function fetchConfig() { } } -export async function shortenUrl(downloadLink: string) { - const response = await fetch('https://api-ssl.bitly.com/v4/shorten', { - method: 'POST', - headers: { - 'Accept': 'application/json', - 'Content-Type': 'application/json', - 'Authorization': 'Bearer a3badec1a128e46e42f36a138165cbbd21ed6cee' - }, - body: JSON.stringify({ - 'group_guid': 'Bjam8omLBjB', - 'long_url': downloadLink - }) - }); - return response.ok ? await response.json() : undefined +export async function shortenUrl(params: string) { + const response = await fetch(`${backendUrl}/api/shorten?${params}`); + return response.ok ? await response.text() : undefined } diff --git a/src/main/frontend/src/code-quarkus/code-quarkus.tsx b/src/main/frontend/src/code-quarkus/code-quarkus.tsx index ec46c0b1a..bd987ecb7 100644 --- a/src/main/frontend/src/code-quarkus/code-quarkus.tsx +++ b/src/main/frontend/src/code-quarkus/code-quarkus.tsx @@ -49,9 +49,9 @@ async function generateProject(project: QuarkusProject): Promise<{ downloadLink: const backendUrl = process.env.REACT_APP_BACKEND_URL || publicUrl; const downloadLink = `${backendUrl}/api/download?${stringify(params)}`; - const data = await shortenUrl(downloadLink); + const link = await shortenUrl(stringify(params)); window.open(downloadLink, '_blank'); - return { downloadLink: data ? data.link : downloadLink }; + return { downloadLink: link || downloadLink }; } const DEFAULT_PROJECT = { diff --git a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt index e4f9a5339..9d54329bb 100644 --- a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt +++ b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt @@ -11,14 +11,19 @@ import org.eclipse.microprofile.openapi.annotations.media.Content import org.eclipse.microprofile.openapi.annotations.media.Schema import org.eclipse.microprofile.openapi.annotations.responses.APIResponse import javax.inject.Inject +import javax.json.bind.JsonbBuilder import javax.validation.Valid import javax.ws.rs.BeanParam import javax.ws.rs.GET import javax.ws.rs.Path import javax.ws.rs.Produces import javax.ws.rs.core.MediaType.APPLICATION_JSON +import javax.ws.rs.core.MediaType.TEXT_PLAIN import javax.ws.rs.core.Response +import okhttp3.* +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.RequestBody.Companion.toRequestBody @Path("/") class CodeQuarkusResource { @@ -32,6 +37,8 @@ class CodeQuarkusResource { @Inject lateinit var projectCreator: QuarkusProjectCreator + private val httpClient = OkHttpClient() + @GET @Path("/config") @Produces(APPLICATION_JSON) @@ -57,6 +64,36 @@ class CodeQuarkusResource { return extensionCatalog.extensions } + @GET + @Path("/shorten") + @Produces(TEXT_PLAIN) + @Operation(summary = "Create a short url based on the parameters") + fun shorten(@Valid @BeanParam project: QuarkusProject): Response { + val JSON = "application/json; charset=utf-8".toMediaType() + val body = """ + { + "group_guid": "$bitlyGroupId", + "long_url": "https://code.quarkus.io/api/download?g=${project.groupId}&a=${project.artifactId}&v=${project.version}&c=${project.className}&e=${project.extensions}" + } + """.toRequestBody(JSON) + + val request = Request.Builder() + .url("https://api-ssl.bitly.com/v4/shorten") + .addHeader("Authorization", "Bearer $bitlyAccessToken") + .post(body) + .build() + + httpClient.newCall(request).execute().use { response -> + + if (!response.isSuccessful) throw IOException("Unexpected code $response") + + val jsonb = JsonbBuilder.create() + val obj = jsonb.fromJson(response.body!!.string(), BitlyResponse::class.java) + + return Response.ok(obj.link).build() + } + } + @GET @Path("/download") @Produces("application/zip") @@ -67,6 +104,5 @@ class CodeQuarkusResource { .type("application/zip") .header("Content-Disposition", "attachment; filename=\"${project.artifactId}.zip\"") .build() - } } \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/model/BitlyResponse.kt b/src/main/kotlin/io/quarkus/code/model/BitlyResponse.kt new file mode 100644 index 000000000..f503919c0 --- /dev/null +++ b/src/main/kotlin/io/quarkus/code/model/BitlyResponse.kt @@ -0,0 +1,5 @@ +package io.quarkus.code + +class BitlyResponse { + var link: String? = null +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e7cbfef54..4e7c1491b 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -3,4 +3,7 @@ quarkus.log.file.enable=false quarkus.http.cors=true io.quarkus.code.quarkus-version=${version.quarkus} io.quarkus.code.quarkus-platform-version=${version.quarkus-platform} -io.quarkus.code.git-commit-id=${git.commit.id.abbrev} \ No newline at end of file +io.quarkus.code.git-commit-id=${git.commit.id.abbrev} + +io.quarkus.code.bitly.generic-access-token=a3badec1a128e46e42f36a138165cbbd21ed6cee +io.quarkus.code.bitly.group-id=Bjam8omLBjB \ No newline at end of file From 09b1ee996ce62b68de7ff24764e7822e5051929f Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Thu, 7 Nov 2019 08:28:05 +0100 Subject: [PATCH 03/14] fix merge error --- src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt | 5 +++-- .../io/quarkus/code/services/CodeQuarkusConfigManager.kt | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt index 9d54329bb..a664807e7 100644 --- a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt +++ b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt @@ -24,6 +24,7 @@ import javax.ws.rs.core.Response import okhttp3.* import okhttp3.MediaType.Companion.toMediaType import okhttp3.RequestBody.Companion.toRequestBody +import java.io.IOException @Path("/") class CodeQuarkusResource { @@ -72,14 +73,14 @@ class CodeQuarkusResource { val JSON = "application/json; charset=utf-8".toMediaType() val body = """ { - "group_guid": "$bitlyGroupId", + "group_guid": "${configManager.bitlyGroupId}", "long_url": "https://code.quarkus.io/api/download?g=${project.groupId}&a=${project.artifactId}&v=${project.version}&c=${project.className}&e=${project.extensions}" } """.toRequestBody(JSON) val request = Request.Builder() .url("https://api-ssl.bitly.com/v4/shorten") - .addHeader("Authorization", "Bearer $bitlyAccessToken") + .addHeader("Authorization", "Bearer ${configManager.bitlyAccessToken}") .post(body) .build() diff --git a/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt b/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt index 0920afd35..85b66aa93 100644 --- a/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt +++ b/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt @@ -33,6 +33,14 @@ open class CodeQuarkusConfigManager { lateinit var sentryDSN: String private set + @ConfigProperty(name = "io.quarkus.code.bitly.generic-access-token") + lateinit var bitlyAccessToken: String + private set + + @ConfigProperty(name = "io.quarkus.code.bitly.group-id") + lateinit var bitlyGroupId: String + private set + fun getConfig(): Config { return Config( environment, From a5b427ca5c122817b7885a9174a04e6d51f4dea6 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Wed, 6 Nov 2019 15:46:08 +0100 Subject: [PATCH 04/14] changed now uses mongo instead of bitly --- pom.xml | 12 ++--- .../io/quarkus/code/CodeQuarkusResource.kt | 53 +++++++++---------- .../kotlin/io/quarkus/code/UrlRepository.kt | 42 +++++++++++++++ .../io/quarkus/code/model/BitlyResponse.kt | 5 -- .../kotlin/io/quarkus/code/model/ShortUrl.kt | 3 ++ src/main/resources/application.properties | 3 +- 6 files changed, 77 insertions(+), 41 deletions(-) create mode 100644 src/main/kotlin/io/quarkus/code/UrlRepository.kt delete mode 100644 src/main/kotlin/io/quarkus/code/model/BitlyResponse.kt create mode 100644 src/main/kotlin/io/quarkus/code/model/ShortUrl.kt diff --git a/pom.xml b/pom.xml index 811e0d099..e01e15d0d 100644 --- a/pom.xml +++ b/pom.xml @@ -7,9 +7,9 @@ 1.0.0 1.8 - 1.8 1.3.21 2.22.2 + 1.8 3.3.0 2.22.0 1.3.31 @@ -82,9 +82,8 @@ ${version.commons-compress} - com.squareup.okhttp3 - okhttp - 4.2.2 + io.quarkus + quarkus-mongodb-client io.rest-assured @@ -122,16 +121,15 @@ src/test/kotlin - src/main/resources true + src/main/resources application.properties - - src/main/resources false + src/main/resources application.properties diff --git a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt index a664807e7..8e2e34cdd 100644 --- a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt +++ b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt @@ -10,8 +10,10 @@ import org.eclipse.microprofile.openapi.annotations.Operation import org.eclipse.microprofile.openapi.annotations.media.Content import org.eclipse.microprofile.openapi.annotations.media.Schema import org.eclipse.microprofile.openapi.annotations.responses.APIResponse +import java.io.IOException +import java.net.URI +import java.util.* import javax.inject.Inject -import javax.json.bind.JsonbBuilder import javax.validation.Valid import javax.ws.rs.BeanParam import javax.ws.rs.GET @@ -21,11 +23,6 @@ import javax.ws.rs.core.MediaType.APPLICATION_JSON import javax.ws.rs.core.MediaType.TEXT_PLAIN import javax.ws.rs.core.Response -import okhttp3.* -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.RequestBody.Companion.toRequestBody -import java.io.IOException - @Path("/") class CodeQuarkusResource { @@ -38,7 +35,8 @@ class CodeQuarkusResource { @Inject lateinit var projectCreator: QuarkusProjectCreator - private val httpClient = OkHttpClient() + @Inject + lateinit var urlRepository: UrlRepository @GET @Path("/config") @@ -69,32 +67,33 @@ class CodeQuarkusResource { @Path("/shorten") @Produces(TEXT_PLAIN) @Operation(summary = "Create a short url based on the parameters") - fun shorten(@Valid @BeanParam project: QuarkusProject): Response { - val JSON = "application/json; charset=utf-8".toMediaType() - val body = """ - { - "group_guid": "${configManager.bitlyGroupId}", - "long_url": "https://code.quarkus.io/api/download?g=${project.groupId}&a=${project.artifactId}&v=${project.version}&c=${project.className}&e=${project.extensions}" + fun createShort(@Valid @BeanParam project: QuarkusProject): Response { + val url = "https://code.quarkus.io/api/download?g=${project.groupId}&a=${project.artifactId}&v=${project.version}&c=${project.className}&e=${project.extensions}" + val response = { id: String -> + Response.ok("https://code.quarkus.io/api/shorten/$id").build() } - """.toRequestBody(JSON) - - val request = Request.Builder() - .url("https://api-ssl.bitly.com/v4/shorten") - .addHeader("Authorization", "Bearer ${configManager.bitlyAccessToken}") - .post(body) - .build() - - httpClient.newCall(request).execute().use { response -> - - if (!response.isSuccessful) throw IOException("Unexpected code $response") + urlRepository.getByUrl(url)?.let { shortUrl -> + return response(shortUrl.id) + } + val id = UUID.randomUUID().toString() + val shortUrl = ShortUrl(id, url) + urlRepository.save(shortUrl) - val jsonb = JsonbBuilder.create() - val obj = jsonb.fromJson(response.body!!.string(), BitlyResponse::class.java) + return response(id) + } - return Response.ok(obj.link).build() + @GET + @Path("/shorten/{id}") + @Operation(summary = "Redirect user to download for this id") + fun getShort(@PathParam("id") id: String): Response { + val shortUrl = urlRepository.getById(id) + shortUrl?.url?.let { url -> + return Response.seeOther(URI(url)).build() } + return Response.serverError().build() } + @GET @Path("/download") @Produces("application/zip") diff --git a/src/main/kotlin/io/quarkus/code/UrlRepository.kt b/src/main/kotlin/io/quarkus/code/UrlRepository.kt new file mode 100644 index 000000000..95700e9f2 --- /dev/null +++ b/src/main/kotlin/io/quarkus/code/UrlRepository.kt @@ -0,0 +1,42 @@ +package io.quarkus.code + +import com.mongodb.client.MongoClient +import com.mongodb.client.MongoCollection +import io.quarkus.code.model.ShortUrl +import org.bson.Document +import javax.enterprise.context.ApplicationScoped +import javax.inject.Inject +import com.mongodb.client.model.Filters.eq + +@ApplicationScoped +open class UrlRepository { + @Inject + lateinit var mongoClient: MongoClient + + open fun getById(id: String): ShortUrl? { + return findByField("_id", id) + } + + open fun getByUrl(url: String): ShortUrl? { + return findByField("url", url) + } + + open fun save(shortUrl: ShortUrl) { + val document = Document() + .append("_id", shortUrl.id) + .append("url", shortUrl.url) + getCollection().insertOne(document) + } + + private fun getCollection(): MongoCollection { + return mongoClient.getDatabase("shorten").getCollection("urls") + } + + private fun findByField(field: String, value: String): ShortUrl? { + val document = getCollection().find(eq(field, value)).first() + document?.let { + return ShortUrl(document["_id"].toString(), document["url"] as String) + } + return null + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/model/BitlyResponse.kt b/src/main/kotlin/io/quarkus/code/model/BitlyResponse.kt deleted file mode 100644 index f503919c0..000000000 --- a/src/main/kotlin/io/quarkus/code/model/BitlyResponse.kt +++ /dev/null @@ -1,5 +0,0 @@ -package io.quarkus.code - -class BitlyResponse { - var link: String? = null -} \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/model/ShortUrl.kt b/src/main/kotlin/io/quarkus/code/model/ShortUrl.kt new file mode 100644 index 000000000..7e4526240 --- /dev/null +++ b/src/main/kotlin/io/quarkus/code/model/ShortUrl.kt @@ -0,0 +1,3 @@ +package io.quarkus.code.model + +data class ShortUrl(val id: String, val url: String) \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 4e7c1491b..7ff080852 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -5,5 +5,4 @@ io.quarkus.code.quarkus-version=${version.quarkus} io.quarkus.code.quarkus-platform-version=${version.quarkus-platform} io.quarkus.code.git-commit-id=${git.commit.id.abbrev} -io.quarkus.code.bitly.generic-access-token=a3badec1a128e46e42f36a138165cbbd21ed6cee -io.quarkus.code.bitly.group-id=Bjam8omLBjB \ No newline at end of file +quarkus.mongodb.connection-string=mongodb://localhost:27017 \ No newline at end of file From de1577c2e25b117540f93acae60b93b7ee412b40 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Wed, 6 Nov 2019 16:19:39 +0100 Subject: [PATCH 05/14] even shorter id --- pom.xml | 12 ++++++++++++ .../kotlin/io/quarkus/code/CodeQuarkusResource.kt | 14 ++++++-------- src/main/kotlin/io/quarkus/code/model/ShortUrl.kt | 4 +++- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index e01e15d0d..4cbdda736 100644 --- a/pom.xml +++ b/pom.xml @@ -25,6 +25,13 @@ 1.0.0.CR1 + + + shortid + shortid + http://dl.bintray.com/snimavat/maven + + @@ -85,6 +92,11 @@ io.quarkus quarkus-mongodb-client + + me.nimavat + shortid + 1.0.1.RC1 + io.rest-assured rest-assured diff --git a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt index 8e2e34cdd..962081f5f 100644 --- a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt +++ b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt @@ -3,6 +3,7 @@ package io.quarkus.code import io.quarkus.code.model.CodeQuarkusExtension import io.quarkus.code.model.Config import io.quarkus.code.model.QuarkusProject +import io.quarkus.code.model.ShortUrl import io.quarkus.code.services.CodeQuarkusConfigManager import io.quarkus.code.services.QuarkusExtensionCatalog import io.quarkus.code.services.QuarkusProjectCreator @@ -10,9 +11,7 @@ import org.eclipse.microprofile.openapi.annotations.Operation import org.eclipse.microprofile.openapi.annotations.media.Content import org.eclipse.microprofile.openapi.annotations.media.Schema import org.eclipse.microprofile.openapi.annotations.responses.APIResponse -import java.io.IOException import java.net.URI -import java.util.* import javax.inject.Inject import javax.validation.Valid import javax.ws.rs.BeanParam @@ -69,17 +68,16 @@ class CodeQuarkusResource { @Operation(summary = "Create a short url based on the parameters") fun createShort(@Valid @BeanParam project: QuarkusProject): Response { val url = "https://code.quarkus.io/api/download?g=${project.groupId}&a=${project.artifactId}&v=${project.version}&c=${project.className}&e=${project.extensions}" - val response = { id: String -> - Response.ok("https://code.quarkus.io/api/shorten/$id").build() + val response = { shortUrl: ShortUrl -> + Response.ok("https://code.quarkus.io/api/shorten/${shortUrl.id}").build() } urlRepository.getByUrl(url)?.let { shortUrl -> - return response(shortUrl.id) + return response(shortUrl) } - val id = UUID.randomUUID().toString() - val shortUrl = ShortUrl(id, url) + val shortUrl = ShortUrl(url = url) urlRepository.save(shortUrl) - return response(id) + return response(shortUrl) } @GET diff --git a/src/main/kotlin/io/quarkus/code/model/ShortUrl.kt b/src/main/kotlin/io/quarkus/code/model/ShortUrl.kt index 7e4526240..a4bceafd8 100644 --- a/src/main/kotlin/io/quarkus/code/model/ShortUrl.kt +++ b/src/main/kotlin/io/quarkus/code/model/ShortUrl.kt @@ -1,3 +1,5 @@ package io.quarkus.code.model -data class ShortUrl(val id: String, val url: String) \ No newline at end of file +import me.nimavat.shortid.ShortId + +data class ShortUrl(val id: String = ShortId.generate(), val url: String) \ No newline at end of file From 071f4297304ea1ce3de1cba276f98ac2dd7cf737 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Thu, 7 Nov 2019 08:13:23 +0100 Subject: [PATCH 06/14] even shorter path --- .../frontend/src/code-quarkus/backend-api.ts | 2 +- .../io/quarkus/code/CodeQuarkusResource.kt | 35 +------------- .../io/quarkus/code/ShortUrlResource.kt | 47 +++++++++++++++++++ src/main/resources/application.properties | 1 - 4 files changed, 49 insertions(+), 36 deletions(-) create mode 100644 src/main/kotlin/io/quarkus/code/ShortUrlResource.kt diff --git a/src/main/frontend/src/code-quarkus/backend-api.ts b/src/main/frontend/src/code-quarkus/backend-api.ts index cfa3b8fa3..1e3e6e7dc 100644 --- a/src/main/frontend/src/code-quarkus/backend-api.ts +++ b/src/main/frontend/src/code-quarkus/backend-api.ts @@ -21,6 +21,6 @@ export async function fetchConfig() { } export async function shortenUrl(params: string) { - const response = await fetch(`${backendUrl}/api/shorten?${params}`); + const response = await fetch(`${backendUrl}/s/?${params}`); return response.ok ? await response.text() : undefined } diff --git a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt index 962081f5f..2932b85ce 100644 --- a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt +++ b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt @@ -3,7 +3,6 @@ package io.quarkus.code import io.quarkus.code.model.CodeQuarkusExtension import io.quarkus.code.model.Config import io.quarkus.code.model.QuarkusProject -import io.quarkus.code.model.ShortUrl import io.quarkus.code.services.CodeQuarkusConfigManager import io.quarkus.code.services.QuarkusExtensionCatalog import io.quarkus.code.services.QuarkusProjectCreator @@ -11,7 +10,6 @@ import org.eclipse.microprofile.openapi.annotations.Operation import org.eclipse.microprofile.openapi.annotations.media.Content import org.eclipse.microprofile.openapi.annotations.media.Schema import org.eclipse.microprofile.openapi.annotations.responses.APIResponse -import java.net.URI import javax.inject.Inject import javax.validation.Valid import javax.ws.rs.BeanParam @@ -19,10 +17,9 @@ import javax.ws.rs.GET import javax.ws.rs.Path import javax.ws.rs.Produces import javax.ws.rs.core.MediaType.APPLICATION_JSON -import javax.ws.rs.core.MediaType.TEXT_PLAIN import javax.ws.rs.core.Response -@Path("/") +@Path("/api") class CodeQuarkusResource { @Inject @@ -62,36 +59,6 @@ class CodeQuarkusResource { return extensionCatalog.extensions } - @GET - @Path("/shorten") - @Produces(TEXT_PLAIN) - @Operation(summary = "Create a short url based on the parameters") - fun createShort(@Valid @BeanParam project: QuarkusProject): Response { - val url = "https://code.quarkus.io/api/download?g=${project.groupId}&a=${project.artifactId}&v=${project.version}&c=${project.className}&e=${project.extensions}" - val response = { shortUrl: ShortUrl -> - Response.ok("https://code.quarkus.io/api/shorten/${shortUrl.id}").build() - } - urlRepository.getByUrl(url)?.let { shortUrl -> - return response(shortUrl) - } - val shortUrl = ShortUrl(url = url) - urlRepository.save(shortUrl) - - return response(shortUrl) - } - - @GET - @Path("/shorten/{id}") - @Operation(summary = "Redirect user to download for this id") - fun getShort(@PathParam("id") id: String): Response { - val shortUrl = urlRepository.getById(id) - shortUrl?.url?.let { url -> - return Response.seeOther(URI(url)).build() - } - return Response.serverError().build() - } - - @GET @Path("/download") @Produces("application/zip") diff --git a/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt b/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt new file mode 100644 index 000000000..30f1d9ae9 --- /dev/null +++ b/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt @@ -0,0 +1,47 @@ +package io.quarkus.code + +import io.quarkus.code.model.QuarkusProject +import io.quarkus.code.model.ShortUrl +import org.eclipse.microprofile.openapi.annotations.Operation +import java.net.URI +import javax.inject.Inject +import javax.validation.Valid +import javax.ws.rs.* +import javax.ws.rs.core.MediaType +import javax.ws.rs.core.Response + +@Path("/s") +class ShortUrlResource { + @Inject + lateinit var urlRepository: UrlRepository + + @GET + @Path("/") + @Produces(MediaType.TEXT_PLAIN) + @Operation(summary = "Create a short url based on the parameters") + fun createShort(@Valid @BeanParam project: QuarkusProject): Response { + val url = "https://code.quarkus.io/api/download?g=${project.groupId}&a=${project.artifactId}&v=${project.version}&c=${project.className}&e=${project.extensions}" + val response = { shortUrl: ShortUrl -> + Response.ok("https://code.quarkus.io/s/${shortUrl.id}").build() + } + urlRepository.getByUrl(url)?.let { shortUrl -> + return response(shortUrl) + } + val shortUrl = ShortUrl(url = url) + urlRepository.save(shortUrl) + + return response(shortUrl) + } + + @GET + @Path("/{id}") + @Operation(summary = "Redirect user to download for this id") + fun getShort(@PathParam("id") id: String): Response { + val shortUrl = urlRepository.getById(id) + shortUrl?.url?.let { url -> + return Response.seeOther(URI(url)).build() + } + return Response.serverError().build() + } + +} \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 7ff080852..689039890 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,4 +1,3 @@ -quarkus.resteasy.path=/api quarkus.log.file.enable=false quarkus.http.cors=true io.quarkus.code.quarkus-version=${version.quarkus} From 208bfb88f4c51242eaff2c0d6f5ab48eb51f663a Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Thu, 7 Nov 2019 09:02:41 +0100 Subject: [PATCH 07/14] fix merge error --- .../io/quarkus/code/services/CodeQuarkusConfigManager.kt | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt b/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt index 85b66aa93..0920afd35 100644 --- a/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt +++ b/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt @@ -33,14 +33,6 @@ open class CodeQuarkusConfigManager { lateinit var sentryDSN: String private set - @ConfigProperty(name = "io.quarkus.code.bitly.generic-access-token") - lateinit var bitlyAccessToken: String - private set - - @ConfigProperty(name = "io.quarkus.code.bitly.group-id") - lateinit var bitlyGroupId: String - private set - fun getConfig(): Config { return Config( environment, From 352bdd63fa99e4f1a9f6d832fd601109c49eee83 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Thu, 7 Nov 2019 09:31:20 +0100 Subject: [PATCH 08/14] 404 instead of error --- src/main/kotlin/io/quarkus/code/ShortUrlResource.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt b/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt index 30f1d9ae9..cb19a5fed 100644 --- a/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt +++ b/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt @@ -41,7 +41,7 @@ class ShortUrlResource { shortUrl?.url?.let { url -> return Response.seeOther(URI(url)).build() } - return Response.serverError().build() + return Response.status(404).entity("This link has expired").build() } } \ No newline at end of file From b051472f04a2aaaee1a20b9918fa1f9acf325479 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Mon, 18 Nov 2019 12:15:49 +0100 Subject: [PATCH 09/14] changed from mongo to DynamoDB --- pom.xml | 48 +++++++-------- .../io/quarkus/code/AbstractRepository.kt | 60 +++++++++++++++++++ .../kotlin/io/quarkus/code/UrlRepository.kt | 44 +++++++------- src/main/resources/application.properties | 7 ++- 4 files changed, 114 insertions(+), 45 deletions(-) create mode 100644 src/main/kotlin/io/quarkus/code/AbstractRepository.kt diff --git a/pom.xml b/pom.xml index 4cbdda736..885d398fe 100644 --- a/pom.xml +++ b/pom.xml @@ -6,32 +6,21 @@ code.quarkus.io 1.0.0 - 1.8 - 1.3.21 - 2.22.2 - 1.8 - 3.3.0 - 2.22.0 - 1.3.31 - 3.8.0 3.2.0 + 1.0.0.CR1 + 2.22.0 1.8 1.7.6 + 3.8.0 + 1.3.21 + 3.3.0 1.19 - - - 1.0.0.CR1 - - + 1.8 + 2.22.2 1.0.0.CR1 + 1.8 + 1.3.31 - - - shortid - shortid - http://dl.bintray.com/snimavat/maven - - @@ -88,10 +77,6 @@ commons-compress ${version.commons-compress} - - io.quarkus - quarkus-mongodb-client - me.nimavat shortid @@ -127,7 +112,22 @@ io.quarkus quarkus-smallrye-metrics + + io.quarkus + quarkus-amazon-dynamodb + + + software.amazon.awssdk + url-connection-client + + + + shortid + shortid + http://dl.bintray.com/snimavat/maven + + src/main/kotlin src/test/kotlin diff --git a/src/main/kotlin/io/quarkus/code/AbstractRepository.kt b/src/main/kotlin/io/quarkus/code/AbstractRepository.kt new file mode 100644 index 000000000..378046827 --- /dev/null +++ b/src/main/kotlin/io/quarkus/code/AbstractRepository.kt @@ -0,0 +1,60 @@ +package io.quarkus.code + + +import io.quarkus.code.model.ShortUrl +import software.amazon.awssdk.services.dynamodb.model.* +import java.util.HashMap +import software.amazon.awssdk.services.dynamodb.model.AttributeValue + + +abstract class AbstractRepository { + + private val tableName: String + get() = "shortUrls" + + protected fun scanRequest(): ScanRequest { + return ScanRequest.builder().tableName(tableName) + .attributesToGet(ID_COL, URL_COL).build() + } + + protected fun putRequest(shortUrl: ShortUrl): PutItemRequest { + val item = HashMap() + item[ID_COL] = AttributeValue.builder().s(shortUrl.id).build() + item[URL_COL] = AttributeValue.builder().s(shortUrl.url).build() + + return PutItemRequest.builder() + .tableName(tableName) + .item(item) + .build() + } + + protected fun queryRequest(url: String): ScanRequest { + val values = HashMap() + values[":url"] = AttributeValue.builder().s(url).build() + val attributesNames = HashMap() + attributesNames["#$URL_COL"] = URL_COL + + return ScanRequest.builder() + .tableName(tableName) + .filterExpression("#$URL_COL = :url") + .expressionAttributeNames(attributesNames) + .expressionAttributeValues(values) + .build() + } + + protected fun getRequest(name: String): GetItemRequest { + val key = HashMap() + key[ID_COL] = AttributeValue.builder().s(name).build() + + return GetItemRequest.builder() + .tableName(tableName) + .key(key) + .attributesToGet(ID_COL, URL_COL) + .build() + } + + companion object { + const val ID_COL = "shortUrlId" + const val URL_COL = "shortUrlUrl" + } +} \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/UrlRepository.kt b/src/main/kotlin/io/quarkus/code/UrlRepository.kt index 95700e9f2..198c0ec30 100644 --- a/src/main/kotlin/io/quarkus/code/UrlRepository.kt +++ b/src/main/kotlin/io/quarkus/code/UrlRepository.kt @@ -1,42 +1,46 @@ package io.quarkus.code -import com.mongodb.client.MongoClient -import com.mongodb.client.MongoCollection import io.quarkus.code.model.ShortUrl -import org.bson.Document +import software.amazon.awssdk.services.dynamodb.DynamoDbClient +import software.amazon.awssdk.services.dynamodb.model.AttributeValue +import java.util.stream.Collectors import javax.enterprise.context.ApplicationScoped import javax.inject.Inject -import com.mongodb.client.model.Filters.eq + @ApplicationScoped -open class UrlRepository { +open class UrlRepository: AbstractRepository() { @Inject - lateinit var mongoClient: MongoClient + lateinit var dynamoDbClient: DynamoDbClient open fun getById(id: String): ShortUrl? { - return findByField("_id", id) + return from(dynamoDbClient.getItem(getRequest(id)).item()) } open fun getByUrl(url: String): ShortUrl? { - return findByField("url", url) + val items = dynamoDbClient.scan(queryRequest(url)).items() + return if (items.isNotEmpty()) { + from(items.first()) + } else { + null + } } - open fun save(shortUrl: ShortUrl) { - val document = Document() - .append("_id", shortUrl.id) - .append("url", shortUrl.url) - getCollection().insertOne(document) + open fun save(shortUrl: ShortUrl): List { + dynamoDbClient.putItem(putRequest(shortUrl)); + return findAll() } - private fun getCollection(): MongoCollection { - return mongoClient.getDatabase("shorten").getCollection("urls") + fun findAll(): List { + return dynamoDbClient.scanPaginator(scanRequest()).items().stream() + .map(({ from(it) })) + .collect(Collectors.toList()) } - private fun findByField(field: String, value: String): ShortUrl? { - val document = getCollection().find(eq(field, value)).first() - document?.let { - return ShortUrl(document["_id"].toString(), document["url"] as String) + fun from(item: Map?): ShortUrl { + if (item != null && item.isNotEmpty()) { + return ShortUrl((item[ID_COL] ?: error("invalid")).s(), (item[URL_COL] ?: error("invalid")).s()) } - return null + error("invalid") } } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 689039890..ffdba2438 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,4 +4,9 @@ io.quarkus.code.quarkus-version=${version.quarkus} io.quarkus.code.quarkus-platform-version=${version.quarkus-platform} io.quarkus.code.git-commit-id=${git.commit.id.abbrev} -quarkus.mongodb.connection-string=mongodb://localhost:27017 \ No newline at end of file +quarkus.dynamodb.endpoint-override=http://localhost:8000 + +quarkus.dynamodb.aws.region=eu-central-1 +quarkus.dynamodb.aws.credentials.type=static +quarkus.dynamodb.aws.credentials.static-provider.access-key-id=test-key +quarkus.dynamodb.aws.credentials.static-provider.secret-access-key=test-secret \ No newline at end of file From e9bfc327603476c740fab8bedcd16595b144db74 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Mon, 18 Nov 2019 16:21:33 +0100 Subject: [PATCH 10/14] removed common code and merged into one --- .../io/quarkus/code/AbstractRepository.kt | 60 ------------------- .../kotlin/io/quarkus/code/UrlRepository.kt | 57 +++++++++++++++++- 2 files changed, 55 insertions(+), 62 deletions(-) delete mode 100644 src/main/kotlin/io/quarkus/code/AbstractRepository.kt diff --git a/src/main/kotlin/io/quarkus/code/AbstractRepository.kt b/src/main/kotlin/io/quarkus/code/AbstractRepository.kt deleted file mode 100644 index 378046827..000000000 --- a/src/main/kotlin/io/quarkus/code/AbstractRepository.kt +++ /dev/null @@ -1,60 +0,0 @@ -package io.quarkus.code - - -import io.quarkus.code.model.ShortUrl -import software.amazon.awssdk.services.dynamodb.model.* -import java.util.HashMap -import software.amazon.awssdk.services.dynamodb.model.AttributeValue - - -abstract class AbstractRepository { - - private val tableName: String - get() = "shortUrls" - - protected fun scanRequest(): ScanRequest { - return ScanRequest.builder().tableName(tableName) - .attributesToGet(ID_COL, URL_COL).build() - } - - protected fun putRequest(shortUrl: ShortUrl): PutItemRequest { - val item = HashMap() - item[ID_COL] = AttributeValue.builder().s(shortUrl.id).build() - item[URL_COL] = AttributeValue.builder().s(shortUrl.url).build() - - return PutItemRequest.builder() - .tableName(tableName) - .item(item) - .build() - } - - protected fun queryRequest(url: String): ScanRequest { - val values = HashMap() - values[":url"] = AttributeValue.builder().s(url).build() - val attributesNames = HashMap() - attributesNames["#$URL_COL"] = URL_COL - - return ScanRequest.builder() - .tableName(tableName) - .filterExpression("#$URL_COL = :url") - .expressionAttributeNames(attributesNames) - .expressionAttributeValues(values) - .build() - } - - protected fun getRequest(name: String): GetItemRequest { - val key = HashMap() - key[ID_COL] = AttributeValue.builder().s(name).build() - - return GetItemRequest.builder() - .tableName(tableName) - .key(key) - .attributesToGet(ID_COL, URL_COL) - .build() - } - - companion object { - const val ID_COL = "shortUrlId" - const val URL_COL = "shortUrlUrl" - } -} \ No newline at end of file diff --git a/src/main/kotlin/io/quarkus/code/UrlRepository.kt b/src/main/kotlin/io/quarkus/code/UrlRepository.kt index 198c0ec30..95c691681 100644 --- a/src/main/kotlin/io/quarkus/code/UrlRepository.kt +++ b/src/main/kotlin/io/quarkus/code/UrlRepository.kt @@ -3,13 +3,17 @@ package io.quarkus.code import io.quarkus.code.model.ShortUrl import software.amazon.awssdk.services.dynamodb.DynamoDbClient import software.amazon.awssdk.services.dynamodb.model.AttributeValue +import software.amazon.awssdk.services.dynamodb.model.GetItemRequest +import software.amazon.awssdk.services.dynamodb.model.PutItemRequest +import software.amazon.awssdk.services.dynamodb.model.ScanRequest +import java.util.HashMap import java.util.stream.Collectors import javax.enterprise.context.ApplicationScoped import javax.inject.Inject @ApplicationScoped -open class UrlRepository: AbstractRepository() { +open class UrlRepository { @Inject lateinit var dynamoDbClient: DynamoDbClient @@ -27,7 +31,7 @@ open class UrlRepository: AbstractRepository() { } open fun save(shortUrl: ShortUrl): List { - dynamoDbClient.putItem(putRequest(shortUrl)); + dynamoDbClient.putItem(putRequest(shortUrl)) return findAll() } @@ -43,4 +47,53 @@ open class UrlRepository: AbstractRepository() { } error("invalid") } + + private fun scanRequest(): ScanRequest { + return ScanRequest.builder().tableName(tableName) + .attributesToGet(ID_COL, URL_COL).build() + } + + private fun putRequest(shortUrl: ShortUrl): PutItemRequest { + val item = HashMap() + item[ID_COL] = AttributeValue.builder().s(shortUrl.id).build() + item[URL_COL] = AttributeValue.builder().s(shortUrl.url).build() + + return PutItemRequest.builder() + .tableName(tableName) + .item(item) + .build() + } + + private fun queryRequest(url: String): ScanRequest { + val values = HashMap() + values[":url"] = AttributeValue.builder().s(url).build() + val attributesNames = HashMap() + attributesNames["#$URL_COL"] = URL_COL + + return ScanRequest.builder() + .tableName(tableName) + .filterExpression("#$URL_COL = :url") + .expressionAttributeNames(attributesNames) + .expressionAttributeValues(values) + .build() + } + + private fun getRequest(name: String): GetItemRequest { + val key = HashMap() + key[ID_COL] = AttributeValue.builder().s(name).build() + + return GetItemRequest.builder() + .tableName(tableName) + .key(key) + .attributesToGet(ID_COL, URL_COL) + .build() + } + + companion object { + const val ID_COL = "shortUrlId" + const val URL_COL = "shortUrlUrl" + } + + private val tableName: String + get() = "shortUrls" } \ No newline at end of file From 61e14d8d66d29f6abb14f5f184a2cd2785c06107 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Tue, 19 Nov 2019 07:57:41 +0100 Subject: [PATCH 11/14] fix added test for ShortUrlResource --- .../io/quarkus/code/CodeQuarkusResource.kt | 1 + .../io/quarkus/code/ShortUrlResource.kt | 1 + .../code/{ => services}/UrlRepository.kt | 2 +- src/main/resources/application.properties | 1 + .../io/quarkus/code/ShortUrlResourceTest.kt | 42 +++++++++++++++++++ .../code/services/UrlRepositoryMock.kt | 29 +++++++++++++ 6 files changed, 75 insertions(+), 1 deletion(-) rename src/main/kotlin/io/quarkus/code/{ => services}/UrlRepository.kt (98%) create mode 100644 src/test/kotlin/io/quarkus/code/ShortUrlResourceTest.kt create mode 100644 src/test/kotlin/io/quarkus/code/services/UrlRepositoryMock.kt diff --git a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt index 2932b85ce..a9771b6a6 100644 --- a/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt +++ b/src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt @@ -6,6 +6,7 @@ import io.quarkus.code.model.QuarkusProject import io.quarkus.code.services.CodeQuarkusConfigManager import io.quarkus.code.services.QuarkusExtensionCatalog import io.quarkus.code.services.QuarkusProjectCreator +import io.quarkus.code.services.UrlRepository import org.eclipse.microprofile.openapi.annotations.Operation import org.eclipse.microprofile.openapi.annotations.media.Content import org.eclipse.microprofile.openapi.annotations.media.Schema diff --git a/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt b/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt index cb19a5fed..0f074174e 100644 --- a/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt +++ b/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt @@ -2,6 +2,7 @@ package io.quarkus.code import io.quarkus.code.model.QuarkusProject import io.quarkus.code.model.ShortUrl +import io.quarkus.code.services.UrlRepository import org.eclipse.microprofile.openapi.annotations.Operation import java.net.URI import javax.inject.Inject diff --git a/src/main/kotlin/io/quarkus/code/UrlRepository.kt b/src/main/kotlin/io/quarkus/code/services/UrlRepository.kt similarity index 98% rename from src/main/kotlin/io/quarkus/code/UrlRepository.kt rename to src/main/kotlin/io/quarkus/code/services/UrlRepository.kt index 95c691681..d51baa686 100644 --- a/src/main/kotlin/io/quarkus/code/UrlRepository.kt +++ b/src/main/kotlin/io/quarkus/code/services/UrlRepository.kt @@ -1,4 +1,4 @@ -package io.quarkus.code +package io.quarkus.code.services import io.quarkus.code.model.ShortUrl import software.amazon.awssdk.services.dynamodb.DynamoDbClient diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index ffdba2438..b75a4c670 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,6 +4,7 @@ io.quarkus.code.quarkus-version=${version.quarkus} io.quarkus.code.quarkus-platform-version=${version.quarkus-platform} io.quarkus.code.git-commit-id=${git.commit.id.abbrev} +#see https://quarkus.io/guides/dynamodb#configuring-dynamodb-clients quarkus.dynamodb.endpoint-override=http://localhost:8000 quarkus.dynamodb.aws.region=eu-central-1 diff --git a/src/test/kotlin/io/quarkus/code/ShortUrlResourceTest.kt b/src/test/kotlin/io/quarkus/code/ShortUrlResourceTest.kt new file mode 100644 index 000000000..43cc3231a --- /dev/null +++ b/src/test/kotlin/io/quarkus/code/ShortUrlResourceTest.kt @@ -0,0 +1,42 @@ +package io.quarkus.code + +import io.quarkus.code.services.UrlRepositoryMock +import io.quarkus.test.junit.QuarkusTest +import io.restassured.RestAssured.given +import io.restassured.config.RedirectConfig.redirectConfig +import io.restassured.config.RestAssuredConfig.newConfig +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.`is` +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import javax.inject.Inject + +@QuarkusTest +class ShortUrlResourceTest { + + @Inject + lateinit var urlRepository: UrlRepositoryMock + + @Test + @DisplayName("Should return a short url for specified download parameters") + fun testShortDownloadUrl() { + given() + .queryParam("g", "ch.nerdin.blog") + .`when`().get("/s") + .then() + .statusCode(200) + assertThat(urlRepository.shortUrls.isEmpty(), `is`(false)) + assertThat(urlRepository.shortUrls[0].url, `is`("https://code.quarkus.io/api/download?g=ch.nerdin.blog&a=code-with-quarkus&v=1.0.0-SNAPSHOT&c=org.acme.ExampleResource&e=[]")) + } + + @Test + @DisplayName("Should return the shortUrl for a given id") + fun testShortUrlById() { + given() + .config(newConfig().redirect(redirectConfig().followRedirects(false))) + .`when`().get("/s/${UrlRepositoryMock.ID}") + .then() + .statusCode(303) + .header("location", `is`("http://blog.nerdin.ch/")) + } +} \ No newline at end of file diff --git a/src/test/kotlin/io/quarkus/code/services/UrlRepositoryMock.kt b/src/test/kotlin/io/quarkus/code/services/UrlRepositoryMock.kt new file mode 100644 index 000000000..e7b9a6e20 --- /dev/null +++ b/src/test/kotlin/io/quarkus/code/services/UrlRepositoryMock.kt @@ -0,0 +1,29 @@ +package io.quarkus.code.services + +import io.quarkus.code.model.ShortUrl +import io.quarkus.test.Mock +import javax.inject.Singleton + +@Mock +@Singleton +open class UrlRepositoryMock: UrlRepository() { + val shortUrls = ArrayList() + + companion object { + const val ID = "id123" + } + + override fun getById(id: String): ShortUrl? { + assert(id == ID) + return ShortUrl(url = "http://blog.nerdin.ch/") + } + + override fun getByUrl(url: String): ShortUrl? { + return null + } + + override fun save(shortUrl: ShortUrl): List { + this.shortUrls.add(shortUrl) + return emptyList() + } +} \ No newline at end of file From ff573160c03220bdc55bc4eb122bb29a4283a347 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Tue, 19 Nov 2019 08:37:57 +0100 Subject: [PATCH 12/14] updated test snapshots --- .../__tests__/__snapshots__/launcher-quarkus.spec.tsx.snap | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/frontend/src/code-quarkus/__tests__/__snapshots__/launcher-quarkus.spec.tsx.snap b/src/main/frontend/src/code-quarkus/__tests__/__snapshots__/launcher-quarkus.spec.tsx.snap index 154525d86..f3fc5edf3 100644 --- a/src/main/frontend/src/code-quarkus/__tests__/__snapshots__/launcher-quarkus.spec.tsx.snap +++ b/src/main/frontend/src/code-quarkus/__tests__/__snapshots__/launcher-quarkus.spec.tsx.snap @@ -1,5 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Let user Generate default application 1`] = `"/api/download?g=org.acme&a=code-with-quarkus&v=1.0.0-SNAPSHOT&b=MAVEN&c=org.acme.ExampleResource&"`; +exports[`Let user Generate default application 1`] = `"http://bit.ly/2Mzdn9Z"`; -exports[`Let user customize an Application and Generate it 1`] = `"/api/download?g=io.test.group&a=custom-test-app&v=1.0.0-TEST&b=MAVEN&c=io.test.pack.ExampleResource&e=io.quarkus%3Aquarkus-arc&e=io.quarkus%3Aquarkus-resteasy&e=io.quarkus%3Aquarkus-resteasy-jackson"`; +exports[`Let user customize an Application and Generate it 1`] = `"http://bit.ly/2Mzdn9Z"`; From a2bad66d695537bb0771ecf4af74a86622096802 Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Tue, 19 Nov 2019 10:30:55 +0100 Subject: [PATCH 13/14] added feature switch --- .../io/quarkus/code/ShortUrlResource.kt | 26 +++++++++++++------ .../code/services/CodeQuarkusConfigManager.kt | 4 +++ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt b/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt index 0f074174e..85a5415e2 100644 --- a/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt +++ b/src/main/kotlin/io/quarkus/code/ShortUrlResource.kt @@ -2,8 +2,10 @@ package io.quarkus.code import io.quarkus.code.model.QuarkusProject import io.quarkus.code.model.ShortUrl +import io.quarkus.code.services.CodeQuarkusConfigManager import io.quarkus.code.services.UrlRepository import org.eclipse.microprofile.openapi.annotations.Operation +import java.lang.Exception import java.net.URI import javax.inject.Inject import javax.validation.Valid @@ -13,6 +15,9 @@ import javax.ws.rs.core.Response @Path("/s") class ShortUrlResource { + @Inject + lateinit var configManager: CodeQuarkusConfigManager + @Inject lateinit var urlRepository: UrlRepository @@ -22,16 +27,21 @@ class ShortUrlResource { @Operation(summary = "Create a short url based on the parameters") fun createShort(@Valid @BeanParam project: QuarkusProject): Response { val url = "https://code.quarkus.io/api/download?g=${project.groupId}&a=${project.artifactId}&v=${project.version}&c=${project.className}&e=${project.extensions}" - val response = { shortUrl: ShortUrl -> - Response.ok("https://code.quarkus.io/s/${shortUrl.id}").build() - } - urlRepository.getByUrl(url)?.let { shortUrl -> + if (!configManager.dynamoDbEnabled) return Response.ok(url).build() + try { + val response = { shortUrl: ShortUrl -> + Response.ok("https://code.quarkus.io/s/${shortUrl.id}").build() + } + urlRepository.getByUrl(url)?.let { shortUrl -> + return response(shortUrl) + } + val shortUrl = ShortUrl(url = url) + urlRepository.save(shortUrl) + return response(shortUrl) + } catch (ex: Exception) { + return Response.ok(url).build() } - val shortUrl = ShortUrl(url = url) - urlRepository.save(shortUrl) - - return response(shortUrl) } @GET diff --git a/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt b/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt index 0920afd35..5b482b089 100644 --- a/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt +++ b/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt @@ -33,6 +33,10 @@ open class CodeQuarkusConfigManager { lateinit var sentryDSN: String private set + @ConfigProperty(name = "io.quarkus.code.dynamodb.enabled", defaultValue = "false") + var dynamoDbEnabled: Boolean = false + private set + fun getConfig(): Config { return Config( environment, From 9aae9d123723239042f0a4e9355064013e6f1bfc Mon Sep 17 00:00:00 2001 From: Erik Jan de Wit Date: Tue, 19 Nov 2019 10:40:29 +0100 Subject: [PATCH 14/14] enable dynamodb for tests --- .../quarkus/code/services/CodeQuarkusConfigManager.kt | 1 - .../kotlin/io/quarkus/code/ShortUrlResourceTest.kt | 10 ++++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt b/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt index 5b482b089..8f461737a 100644 --- a/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt +++ b/src/main/kotlin/io/quarkus/code/services/CodeQuarkusConfigManager.kt @@ -35,7 +35,6 @@ open class CodeQuarkusConfigManager { @ConfigProperty(name = "io.quarkus.code.dynamodb.enabled", defaultValue = "false") var dynamoDbEnabled: Boolean = false - private set fun getConfig(): Config { return Config( diff --git a/src/test/kotlin/io/quarkus/code/ShortUrlResourceTest.kt b/src/test/kotlin/io/quarkus/code/ShortUrlResourceTest.kt index 43cc3231a..89d193d81 100644 --- a/src/test/kotlin/io/quarkus/code/ShortUrlResourceTest.kt +++ b/src/test/kotlin/io/quarkus/code/ShortUrlResourceTest.kt @@ -1,5 +1,6 @@ package io.quarkus.code +import io.quarkus.code.services.CodeQuarkusConfigManager import io.quarkus.code.services.UrlRepositoryMock import io.quarkus.test.junit.QuarkusTest import io.restassured.RestAssured.given @@ -7,6 +8,7 @@ import io.restassured.config.RedirectConfig.redirectConfig import io.restassured.config.RestAssuredConfig.newConfig import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.`is` +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Test import javax.inject.Inject @@ -14,9 +16,17 @@ import javax.inject.Inject @QuarkusTest class ShortUrlResourceTest { + @Inject + lateinit var configManager: CodeQuarkusConfigManager + @Inject lateinit var urlRepository: UrlRepositoryMock + @BeforeEach + fun setup() { + configManager.dynamoDbEnabled = true + } + @Test @DisplayName("Should return a short url for specified download parameters") fun testShortDownloadUrl() {