Commit cb2f92ba authored by Andre Blanke's avatar Andre Blanke
Browse files

Implement support for custom ids

parent 6852ff0c
import { Component } from "@angular/core";
import {HttpClient, HttpHeaders, HttpParams} from "@angular/common/http";
import { HttpClient, HttpHeaders, HttpParams } from "@angular/common/http";
class ShortenerFormModel {
url: string = ""
id: string = ""
}
@Component({
selector: 'shortener-form',
template: `
<form id="shortener-form" (ngSubmit)="submit()" method="post">
<input id="long-url-input" name="url" type="text" placeholder="Enter a long URL" [(ngModel)]="model.url">
<button type="submit">Shorten URL</button>
<ul>
<li>
<input id="long-url-input" name="url" type="text" placeholder="Enter a long URL"
title="The URL to be shortened" [(ngModel)]="model.url">
</li>
<li>
<p>and optionally specify a custom id:</p>
</li>
<li>
<input id="short-url-id-input" name="id" type="text" placeholder="optionally enter a custom id"
pattern="[a-zA-Z0-9]+" title="Custom id for the short URL" [(ngModel)]="model.id">
<button type="submit">Shorten URL</button>
</li>
</ul>
</form>
<div id="shortener-form-output">
<label id="short-url-label" for="short-url">Your short URL:</label>
<label id="short-url-label" for="short-url">Click to copy your short URL:</label>
<input id="short-url-output" name="short-url" (click)="copyContent($event)" readonly/>
</div>
`
......@@ -29,9 +42,10 @@ export class ShortenerFormComponent {
const headers =
new HttpHeaders()
.set("Content-Type", "application/x-www-form-urlencoded")
const body =
new HttpParams()
.append("url", encodeURIComponent(this.model.url));
let body = new HttpParams().append("url", encodeURIComponent(this.model.url));
if (this.model.id !== "")
body = body.append("id", this.model.id.toLowerCase());
this.http
.post("http://upbshrt.xyz/", body, { headers: headers, responseType: "text" })
......@@ -43,7 +57,10 @@ export class ShortenerFormComponent {
(document.getElementById("short-url-output") as HTMLInputElement)
.value = decodeURIComponent(data);
},
error => console.log(error));
error => {
console.log(error);
alert(error.message);
});
}
copyContent(event: MouseEvent): void {
......
......@@ -23,12 +23,22 @@ html {
justify-content: center;
}
#long-url-input {
#shortener-form > ul {
list-style: none;
padding: 0;
margin: 0;
}
#short-url-id-input {
margin-right: 10px;
}
#short-url-id-input:invalid {
color: red;
}
#shortener-form-output {
margin-top: 10px;
margin-top: 50px;
visibility: hidden;
}
......
......@@ -4,6 +4,7 @@ import java.net.URI
import java.net.URLDecoder
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import java.math.BigInteger
import javax.validation.constraints.Pattern
......@@ -68,7 +69,8 @@ class UrlShorteningController @Autowired constructor(private val repository: Url
)
}
val numericId: Int? = id?.toInt(UrlMapping.ID_RADIX)
val numericId: BigInteger? =
if (id != null) BigInteger(id, UrlMapping.ID_RADIX) else null
if ((numericId != null) && repository.existsById(numericId))
throw ResponseStatusException(
HttpStatus.UNPROCESSABLE_ENTITY,
......@@ -95,8 +97,8 @@ class UrlShorteningController @Autowired constructor(private val repository: Url
*/
@GetMapping("/{id:^[a-z0-9]+$}")
fun resolveUrl(@PathVariable id: String): ResponseEntity<Nothing> {
val numericId: Int = try {
id.toInt(UrlMapping.ID_RADIX)
val numericId: BigInteger = try {
BigInteger(id, UrlMapping.ID_RADIX)
} catch (exception: NumberFormatException) {
handleMalformedId(id)
}
......
package xyz.upbshrt.shortener.entity
import java.math.BigInteger
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.Id
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.SequenceGenerator
import org.hibernate.annotations.GenericGenerator
import org.hibernate.annotations.Parameter
import org.hibernate.id.enhanced.SequenceStyleGenerator
@Entity
class UrlMapping(id: Int?, url: String) {
class UrlMapping(id: BigInteger?, url: String) {
@Id
@Suppress("CanBePrimaryConstructorProperty")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "UrlMappingIdSequence")
@SequenceGenerator(
name = "UrlMappingIdSequence",
sequenceName = "url_mapping_id_sequence",
allocationSize = 1
@GenericGenerator(
name = "UrlMappingIdSequence",
strategy = "xyz.upbshrt.shortener.hibernate.SequenceStyleGenerator",
parameters = [
Parameter(name = SequenceStyleGenerator.DEF_SEQUENCE_NAME, value = "url_mapping_id_sequence"),
Parameter(name = SequenceStyleGenerator.INCREMENT_PARAM, value = "1")
]
)
private var id: Int? = id
private var id: BigInteger? = id
@Column(length = 2048, nullable = false)
var url: String = url
......
package xyz.upbshrt.shortener.hibernate
import java.io.Serializable
import java.util.Properties
import org.hibernate.MappingException
import org.hibernate.engine.spi.SharedSessionContractImplementor
import org.hibernate.service.ServiceRegistry
import org.hibernate.type.Type
/**
* Overrides Hibernate's default [org.hibernate.id.enhanced.SequenceStyleGenerator] to add support for manually
* assigned ids.
*/
class SequenceStyleGenerator : org.hibernate.id.enhanced.SequenceStyleGenerator() {
lateinit var entityName: String
override fun configure(type: Type?, params: Properties, serviceRegistry: ServiceRegistry?) {
entityName = params.getProperty(ENTITY_NAME) ?: throw MappingException("no entity name")
super.configure(type, params, serviceRegistry)
}
override fun generate(session: SharedSessionContractImplementor, `object`: Any?): Serializable =
session
.getEntityPersister("", `object`)
.getIdentifier(`object`, session)
?: super.generate(session, `object`)
}
package xyz.upbshrt.shortener.repository
import java.math.BigInteger
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import xyz.upbshrt.shortener.entity.UrlMapping
@Repository
interface UrlMappingRepository : JpaRepository<UrlMapping, Int>
interface UrlMappingRepository : JpaRepository<UrlMapping, BigInteger>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment