Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] fix: send url to bitly to have cool curl command #178

Closed
wants to merge 14 commits into from
46 changes: 31 additions & 15 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,20 @@
<artifactId>code.quarkus.io</artifactId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<version.kotlin-stdlib>1.3.21</version.kotlin-stdlib>
<version.maven-failsafe-plugin>2.22.2</version.maven-failsafe-plugin>
<version.rest-assured>3.3.0</version.rest-assured>
<version.surefire-plugin>2.22.0</version.surefire-plugin>
<version.kotlin>1.3.31</version.kotlin>
<version.maven-compiler-plugin>3.8.0</version.maven-compiler-plugin>
<version.gatling>3.2.0</version.gatling>
<version.quarkus>1.0.0.CR1</version.quarkus>
<version.surefire-plugin>2.22.0</version.surefire-plugin>
<maven.compiler.target>1.8</maven.compiler.target>
<version.frontend-maven-plugin>1.7.6</version.frontend-maven-plugin>
<version.maven-compiler-plugin>3.8.0</version.maven-compiler-plugin>
<version.kotlin-stdlib>1.3.21</version.kotlin-stdlib>
<version.rest-assured>3.3.0</version.rest-assured>
<version.commons-compress>1.19</version.commons-compress>

<!-- Quarkus version is used for bundling Code Quarkus -->
<version.quarkus>1.0.0.CR1</version.quarkus>

<!-- Quarkus Platform version must be compatible with Quarkus version -->
<maven.compiler.source>1.8</maven.compiler.source>
<version.maven-failsafe-plugin>2.22.2</version.maven-failsafe-plugin>
<version.quarkus-platform>1.0.0.CR1</version.quarkus-platform>
<kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget>
<version.kotlin>1.3.31</version.kotlin>
</properties>
<dependencyManagement>
<dependencies>
Expand Down Expand Up @@ -80,6 +77,11 @@
<artifactId>commons-compress</artifactId>
<version>${version.commons-compress}</version>
</dependency>
<dependency>
<groupId>me.nimavat</groupId>
<artifactId>shortid</artifactId>
<version>1.0.1.RC1</version>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
Expand Down Expand Up @@ -110,22 +112,36 @@
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-metrics</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-amazon-dynamodb</artifactId>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>url-connection-client</artifactId>
</dependency>
</dependencies>
<repositories>
<repository>
<id>shortid</id>
<name>shortid</name>
<url>http://dl.bintray.com/snimavat/maven</url>
</repository>
</repositories>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<directory>src/main/resources</directory>
<includes>
<include>application.properties</include>
</includes>
</resource>

<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
<directory>src/main/resources</directory>
<excludes>
<exclude>application.properties</exclude>
</excludes>
Expand Down
Original file line number Diff line number Diff line change
@@ -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"`;
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ jest.mock('../backend-api', () => ({
"order": 6
},
]),
fetchConfig: () => { throw new Error("not used"); }
fetchConfig: () => { throw new Error("not used"); },
shortenUrl: async (params: string) => Promise.resolve('http://bit.ly/2Mzdn9Z')
}));


Expand Down
7 changes: 6 additions & 1 deletion src/main/frontend/src/code-quarkus/backend-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,9 @@ export async function fetchConfig() {
environment: 'dev'
}
}
}
}

export async function shortenUrl(params: string) {
const response = await fetch(`${backendUrl}/s/?${params}`);
return response.ok ? await response.text() : undefined
}
5 changes: 4 additions & 1 deletion src/main/frontend/src/code-quarkus/code-quarkus.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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 link = await shortenUrl(stringify(params));
window.open(downloadLink, '_blank');
return { downloadLink };
return { downloadLink: link || downloadLink };
}

const DEFAULT_PROJECT = {
Expand Down
2 changes: 2 additions & 0 deletions src/main/frontend/src/code-quarkus/next-steps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<Modal
title="Your Quarkus Application is Ready!"
Expand All @@ -33,6 +34,7 @@ export function NextSteps(props: NextStepsProps) {
<TextContent>
<p>Your download should start shortly. If it doesn't, please use the direct link:</p>
<Button component="a" href={props.downloadLink as string} aria-label="Download link" className="download-button">Download the zip</Button>
<code>$ {curlCmd} <CopyToClipboard eventId="Download-via-curl-Command" content={curlCmd} /></code>
<h1>What's next?</h1>
<div>
Unzip the project and start playing with Quarkus by running:
Expand Down
8 changes: 5 additions & 3 deletions src/main/kotlin/io/quarkus/code/CodeQuarkusResource.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -19,8 +20,7 @@ import javax.ws.rs.Produces
import javax.ws.rs.core.MediaType.APPLICATION_JSON
import javax.ws.rs.core.Response


@Path("/")
@Path("/api")
class CodeQuarkusResource {

@Inject
Expand All @@ -32,6 +32,9 @@ class CodeQuarkusResource {
@Inject
lateinit var projectCreator: QuarkusProjectCreator

@Inject
lateinit var urlRepository: UrlRepository

@GET
@Path("/config")
@Produces(APPLICATION_JSON)
Expand Down Expand Up @@ -67,6 +70,5 @@ class CodeQuarkusResource {
.type("application/zip")
.header("Content-Disposition", "attachment; filename=\"${project.artifactId}.zip\"")
.build()

}
}
58 changes: 58 additions & 0 deletions src/main/kotlin/io/quarkus/code/ShortUrlResource.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
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
import javax.ws.rs.*
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response

@Path("/s")
class ShortUrlResource {
@Inject
lateinit var configManager: CodeQuarkusConfigManager

@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}"
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()
}
}

@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.status(404).entity("This link has expired").build()
}

}
5 changes: 5 additions & 0 deletions src/main/kotlin/io/quarkus/code/model/ShortUrl.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.quarkus.code.model

import me.nimavat.shortid.ShortId

data class ShortUrl(val id: String = ShortId.generate(), val url: String)
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ open class CodeQuarkusConfigManager {
lateinit var sentryDSN: String
private set

@ConfigProperty(name = "io.quarkus.code.dynamodb.enabled", defaultValue = "false")
var dynamoDbEnabled: Boolean = false

fun getConfig(): Config {
return Config(
environment,
Expand Down
99 changes: 99 additions & 0 deletions src/main/kotlin/io/quarkus/code/services/UrlRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package io.quarkus.code.services

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 {
@Inject
lateinit var dynamoDbClient: DynamoDbClient

open fun getById(id: String): ShortUrl? {
return from(dynamoDbClient.getItem(getRequest(id)).item())
}

open fun getByUrl(url: String): ShortUrl? {
val items = dynamoDbClient.scan(queryRequest(url)).items()
return if (items.isNotEmpty()) {
from(items.first())
} else {
null
}
}

open fun save(shortUrl: ShortUrl): List<ShortUrl> {
dynamoDbClient.putItem(putRequest(shortUrl))
return findAll()
}

fun findAll(): List<ShortUrl> {
return dynamoDbClient.scanPaginator(scanRequest()).items().stream()
.map(({ from(it) }))
.collect(Collectors.toList())
}

fun from(item: Map<String, AttributeValue>?): ShortUrl {
if (item != null && item.isNotEmpty()) {
return ShortUrl((item[ID_COL] ?: error("invalid")).s(), (item[URL_COL] ?: error("invalid")).s())
}
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<String, AttributeValue>()
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<String, AttributeValue>()
values[":url"] = AttributeValue.builder().s(url).build()
val attributesNames = HashMap<String, String>()
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<String, AttributeValue>()
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"
}
11 changes: 9 additions & 2 deletions src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
quarkus.resteasy.path=/api
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}
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
ia3andy marked this conversation as resolved.
Show resolved Hide resolved
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
Loading