Skip to content

Instantly share code, notes, and snippets.

@mayankchoubey
Created October 25, 2023 06:05
Show Gist options
  • Save mayankchoubey/9cda029f66fcb1b61c7934ca6686513a to your computer and use it in GitHub Desktop.
Save mayankchoubey/9cda029f66fcb1b61c7934ca6686513a to your computer and use it in GitHub Desktop.
Quarkus - URL shortener service in PostgreSQL
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=${dbUser}
quarkus.datasource.password=${dbUserPass}
quarkus.datasource.reactive.url=postgresql://localhost:5432/${dbName}
quarkus.datasource.reactive.max-size=10
quarkus.http.port=3000
package com.example;
import java.time.LocalDateTime;
import io.vertx.mutiny.sqlclient.Row;
public record ShortenedUrl(
String id,
String srcurl,
LocalDateTime created,
LocalDateTime lastaccessed) {
public static ShortenedUrl of(String id, String srcurl, LocalDateTime created, LocalDateTime lastaccessed) {
return new ShortenedUrl(id, srcurl, created, lastaccessed);
}
public static ShortenedUrl from(Row row) {
return new ShortenedUrl(
row.getString("id"),
row.getString("srcurl"),
row.getLocalDateTime("created"),
row.getLocalDateTime("lastaccessed"));
}
}
package com.example;
public class UrlShortenerError {
private String errMsg;
public UrlShortenerError() {
}
public UrlShortenerError(String errMsg) {
this.errMsg = errMsg;
}
public String getErrMsg() {
return this.errMsg;
}
public void setErrMsg(String errMsg) {
this.errMsg = errMsg;
}
}
package com.example;
import io.smallrye.mutiny.Multi;
import io.smallrye.mutiny.Uni;
import io.vertx.mutiny.pgclient.PgPool;
import io.vertx.mutiny.sqlclient.Row;
import io.vertx.mutiny.sqlclient.RowSet;
import io.vertx.mutiny.sqlclient.Tuple;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.StreamSupport;
import com.example.ShortenedUrl;
@ApplicationScoped
public class UrlShortenerRepository {
@Inject
private PgPool client;
public UrlShortenerRepository(PgPool _client) {
this.client = _client;
}
public Uni<ShortenedUrl> save(ShortenedUrl data) {
return this.client
.preparedQuery("INSERT INTO shortenedurls (id, srcurl, created, lastaccessed) VALUES ($1, $2, $3, $4) RETURNING *")
.execute(Tuple.of(data.id(), data.srcurl(), data.created(), data.lastaccessed()))
.onItem().transform(RowSet::iterator)
.onItem().transform(iterator -> iterator.hasNext() ? ShortenedUrl.from(iterator.next()) : null);
}
}
package com.example;
public class UrlShortenerRequest {
private String srcUrl;
public String getSrcUrl() {
return this.srcUrl;
}
public void setSrcUrl(String srcUrl) {
this.srcUrl = srcUrl;
}
}
package com.example;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.RequestScoped;
import jakarta.inject.Inject;
import jakarta.validation.Valid;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import java.net.URI;
import java.util.UUID;
import java.util.logging.Logger;
import static jakarta.ws.rs.core.Response.*;
import io.vertx.mutiny.pgclient.PgPool;
import com.example.UrlShortenerRequest;
import com.example.UrlShortenerResponse;
import com.example.UrlShortenerError;
import java.time.LocalDateTime;
import com.aventrix.jnanoid.jnanoid.*;
@Path("/shorten")
@RequestScoped
public class UrlShortenerResource {
final String baseUrl = "http://test.short/";
@Inject
PgPool client;
@Inject
UrlShortenerRepository shortenerRepository;
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Uni<Response> handleRequest(@Valid UrlShortenerRequest shortenRequest) {
if(shortenRequest.getSrcUrl() == null) {
return Uni.createFrom()
.item(
Response.status(400)
.type(MediaType.APPLICATION_JSON)
.entity(new UrlShortenerError("Parameter 'srcUrl' is missing"))
.build());
}
String srcUrl = shortenRequest.getSrcUrl();
if(srcUrl.length()>250) {
return Uni.createFrom()
.item(
Response.status(400)
.type(MediaType.APPLICATION_JSON)
.entity(new UrlShortenerError("Parameter 'srcUrl' must not be more than 250 characters"))
.build());
}
if(!(srcUrl.startsWith("http://") || srcUrl.startsWith("https://"))) {
return Uni.createFrom()
.item(
Response.status(400)
.type(MediaType.APPLICATION_JSON)
.entity(new UrlShortenerError("Parameter 'srcUrl' must start with http:// or https://"))
.build());
}
String urlId = NanoIdUtils.randomNanoId(
NanoIdUtils.DEFAULT_NUMBER_GENERATOR,
NanoIdUtils.DEFAULT_ALPHABET,
10
);
String shortUrl = baseUrl + urlId;
ShortenedUrl shortenedUrl = new ShortenedUrl(
urlId,
srcUrl,
LocalDateTime.now(),
LocalDateTime.now());
Uni<ShortenedUrl> shortUrlData = this.shortenerRepository.save(shortenedUrl);
return shortUrlData.map(shortenedUrlData -> {
if(shortenedUrlData.id() == null) {
return Response.status(500)
.type(MediaType.APPLICATION_JSON)
.entity(new UrlShortenerError("Failed to shorten"))
.build();
}
UrlShortenerResponse resp = new UrlShortenerResponse(srcUrl, shortUrl);
return Response.ok()
.type(MediaType.APPLICATION_JSON)
.entity(resp)
.build();
});
}
}
package com.example;
public class UrlShortenerResponse {
private String srcUrl;
private String shortenedUrl;
public UrlShortenerResponse() {
}
public UrlShortenerResponse(String srcUrl, String shortenedUrl) {
this.srcUrl = srcUrl;
this.shortenedUrl = shortenedUrl;
}
public String getSrcUrl() {
return this.srcUrl;
}
public void setSrcUrl(String srcUrl) {
this.srcUrl = srcUrl;
}
public String getShortenedUrl() {
return this.shortenedUrl;
}
public void setShortenedUrl(String shortenedUrl) {
this.shortenedUrl = shortenedUrl;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment