Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@ lint:
make -C app lint
build:
make -C app build
install:
make -C app install
run:
make -C app run
run-dist:
make -C app run-dist
test:
make -C app test
report:
make -C app report
check-deps:
make -C app check-deps
update-deps:
make -C app update-deps

build-run: build run
12 changes: 12 additions & 0 deletions app/Makefile
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
.PHONY: build
.DEFAULT_GOAL := build-run


setup:
./gradlew wrapper --gradle-version 8.7
clean:
./gradlew clean
lint:
./gradlew checkstyleMain
build:
./gradlew clean build
install:
./gradlew clean install
run-dist:
./build/install/app/bin/app
run:
./gradlew run
test:
./gradlew test
report:
./gradlew jacocoTestReport
check-deps:
./gradlew dependencyUpdates -Drevision=release
update-deps:
./gradlew useLatestVersions

build-run: build run
18 changes: 12 additions & 6 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ group = "hexlet.code"
version = "1.0-SNAPSHOT"

application {
mainClass = "hexlet.code.App"
mainClass = "hexlet.code.App"
}

repositories {
Expand All @@ -35,13 +35,12 @@ dependencies {
implementation("org.slf4j:slf4j-simple:2.1.0-alpha1")



// LOMBOK
compileOnly("org.projectlombok:lombok:1.18.38")
annotationProcessor("org.projectlombok:lombok:1.18.38")
annotationProcessor("org.projectlombok:lombok:1.18.38")

testCompileOnly("org.projectlombok:lombok:1.18.38")
testAnnotationProcessor("org.projectlombok:lombok:1.18.38")
testCompileOnly("org.projectlombok:lombok:1.18.38")
testAnnotationProcessor("org.projectlombok:lombok:1.18.38")

// CheckStyle
implementation("com.puppycrawl.tools:checkstyle:10.26.0")
Expand Down Expand Up @@ -70,6 +69,13 @@ jacoco {
toolVersion = "0.8.12"
}


// Solution for warning "Recompile with -Xlint:unchecked for details"
tasks.withType<JavaCompile> {
options.compilerArgs.addAll(listOf("-Xlint:unchecked", "-Xlint:deprecation")) // Add other desired flags
}


tasks.jacocoTestReport {
reports {
xml.required = true
Expand All @@ -85,5 +91,5 @@ sonar {
property("sonar.projectKey", "VictorGotsenko_java-project-72")
property("sonar.organization", "victorgotsenko")
property("sonar.host.url", "https://sonarcloud.io")
}
}
}
28 changes: 13 additions & 15 deletions app/src/main/java/hexlet/code/App.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package hexlet.code;

import hexlet.code.repository.BaseRepository;
import hexlet.code.controller.UrlController;
import hexlet.code.util.NamedRoutes;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
Expand All @@ -21,17 +26,12 @@
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import hexlet.code.repository.BaseRepository;


@Slf4j
public class App {

private static TemplateEngine createTemplateEngine() {
ClassLoader classLoader = App.class.getClassLoader();
ResourceCodeResolver codeResolver = new ResourceCodeResolver("templates", classLoader);
TemplateEngine templateEngine = TemplateEngine.create(codeResolver, ContentType.Html);
return templateEngine;
return TemplateEngine.create(codeResolver, ContentType.Html);
}

private static String getDatabaseUrl() {
Expand All @@ -40,9 +40,8 @@ private static String getDatabaseUrl() {
return System.getenv().getOrDefault("JDBC_DATABASE_URL", "jdbc:h2:mem:project;DB_CLOSE_DELAY=-1;");
}


private static String readResourceFile(String fileName) throws IOException {
var inputStream = App.class.getClassLoader().getResourceAsStream(fileName);
InputStream inputStream = App.class.getClassLoader().getResourceAsStream(fileName);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
return reader.lines().collect(Collectors.joining("\n"));
}
Expand All @@ -62,23 +61,22 @@ public static Javalin getApp() throws IOException, SQLException { //
}
BaseRepository.dataSource = dataSource;


Javalin app = Javalin.create(config -> {
config.bundledPlugins.enableDevLogging();
config.fileRenderer(new JavalinJte(createTemplateEngine()));
});

// root path
app.get("/", ctx -> {
// ctx.result("Hello World");
ctx.render("index_s.jte");

});
app.get(NamedRoutes.mainPage(), ctx -> ctx.render("index.jte"));
// other
app.post(NamedRoutes.urlsPath(), UrlController::create);
app.get(NamedRoutes.buildPath(), UrlController::build);
app.get(NamedRoutes.urlsPath(), UrlController::index);
app.get(NamedRoutes.urlPath("{id}"), UrlController::show);

return app;
}


public static void main(String[] args) throws IOException, SQLException { //throws SQLException
Javalin app = getApp();
app.start(7070);
Expand Down
93 changes: 93 additions & 0 deletions app/src/main/java/hexlet/code/controller/UrlController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package hexlet.code.controller;

import hexlet.code.dto.BasePage;
import hexlet.code.dto.urls.UrlPage;
import hexlet.code.dto.urls.UrlsPage;
import hexlet.code.model.Url;
import hexlet.code.repository.UrlRepository;
import hexlet.code.util.NamedRoutes;

import io.javalin.http.Context;
import io.javalin.http.NotFoundResponse;

import static io.javalin.rendering.template.TemplateUtil.model;

import java.net.URL;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.MalformedURLException;

import java.sql.SQLException;
import java.time.LocalDateTime;
import java.util.List;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.utils.URIBuilder;

@Slf4j
public class UrlController {
public static void build(Context ctx) {
BasePage page = new BasePage();
page.setFlash(ctx.consumeSessionAttribute("flash"));
page.setFlashType(ctx.consumeSessionAttribute("flash-type"));
ctx.render("urls/build.jte", model("page", page));
}

public static void create(Context ctx) throws SQLException, URISyntaxException, MalformedURLException {
String name = ctx.formParamAsClass("url", String.class).get();
URL unifmResourceId = null;
try {
unifmResourceId = new URI(name).toURL();
} catch (Exception e) {
ctx.sessionAttribute("flash", "Некорректный URL");
ctx.sessionAttribute("flash-type", "danger");
ctx.redirect(NamedRoutes.buildPath());
return;
}

if (name == null || name.isEmpty()) {
ctx.sessionAttribute("flash", "Поле URL не должно быть пустым");
ctx.sessionAttribute("flash-type", "warning");
ctx.redirect(NamedRoutes.buildPath());
return;
}

String protocol = unifmResourceId.getProtocol();
int port = unifmResourceId.getPort();
String host = unifmResourceId.getHost();

URL url = new URIBuilder().setScheme(protocol).setHost(host).setPort(port).build().toURL();

if (UrlRepository.findByName(String.valueOf(url)).isEmpty()) {
Url newUrl = new Url(String.valueOf(url), LocalDateTime.now());
UrlRepository.save(newUrl);
} else {
ctx.sessionAttribute("flash", "Страница уже существует");
ctx.sessionAttribute("flash-type", "info");
ctx.redirect(NamedRoutes.urlsPath());
return;
}

ctx.sessionAttribute("flash", "Страница успешно добавлена");
ctx.sessionAttribute("flash-type", "success");
ctx.redirect(NamedRoutes.urlsPath());
}

public static void index(Context ctx) throws SQLException {
List<Url> urls = UrlRepository.getEntities();
UrlsPage page = new UrlsPage(urls);
page.setFlash(ctx.consumeSessionAttribute("flash"));
page.setFlashType(ctx.consumeSessionAttribute("flash-type"));
ctx.render("urls/indexList.jte", model("page", page));
}

public static void show(Context ctx) throws SQLException {
Long id = ctx.pathParamAsClass("id", Long.class).get();
Url url = UrlRepository.find(id)
.orElseThrow(() -> new NotFoundResponse("Entity with id = " + id + " not found"));
UrlPage page = new UrlPage(url);
page.setFlash(ctx.consumeSessionAttribute("flash"));
page.setFlashType(ctx.consumeSessionAttribute("flash-type"));
ctx.render("urls/show.jte", model("page", page));
}
}
14 changes: 14 additions & 0 deletions app/src/main/java/hexlet/code/dto/BasePage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package hexlet.code.dto;

import lombok.Getter;
import lombok.Setter;

import java.time.format.DateTimeFormatter;

@Getter
@Setter
public class BasePage {
private String flash;
private String flashType;
private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm");
}
13 changes: 13 additions & 0 deletions app/src/main/java/hexlet/code/dto/urls/UrlPage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package hexlet.code.dto.urls;

import hexlet.code.dto.BasePage;
import hexlet.code.model.Url;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public class UrlPage extends BasePage {
private Url url;
}
15 changes: 15 additions & 0 deletions app/src/main/java/hexlet/code/dto/urls/UrlsPage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package hexlet.code.dto.urls;

import hexlet.code.dto.BasePage;
import hexlet.code.model.Url;

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public class UrlsPage extends BasePage {
private List<Url> urls;
}
91 changes: 91 additions & 0 deletions app/src/main/java/hexlet/code/repository/UrlRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package hexlet.code.repository;

import hexlet.code.model.Url;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class UrlRepository extends BaseRepository {
public static void save(Url url) throws SQLException {
String sql = "INSERT INTO urls (name, created_at) VALUES (?, ?)";
try (Connection conn = dataSource.getConnection();
PreparedStatement preparedStatement = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
preparedStatement.setString(1, url.getName());
LocalDateTime createdAt = LocalDateTime.now();
preparedStatement.setTimestamp(2, Timestamp.valueOf(createdAt));
preparedStatement.executeUpdate();
var generatedKeys = preparedStatement.getGeneratedKeys();
if (generatedKeys.next()) {
url.setId(generatedKeys.getLong(1));
url.setCreatedAt(createdAt);
} else {
throw new SQLException("DB have not returned an id after saving an entity");
}
}
}

public static Optional<Url> find(Long id) throws SQLException {
String sql = "SELECT * FROM urls WHERE id = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
preparedStatement.setLong(1, id);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
String name = resultSet.getString("name");
LocalDateTime createAt = resultSet.getTimestamp("created_at").toLocalDateTime();
Url url = new Url(name, createAt);
url.setCreatedAt(createAt);
url.setId(id);
return Optional.of(url);
}
return Optional.empty();
}
}

public static Optional<Url> findByName(String name) throws SQLException {
String sql = "SELECT * FROM urls WHERE name = ?";
try (Connection connection = dataSource.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(sql)) {
preparedStatement.setString(1, name);
ResultSet resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
LocalDateTime createdAt = resultSet.getTimestamp("created_at").toLocalDateTime();
Long id = resultSet.getLong("id");
Url url = new Url(name, createdAt);
url.setId(id);
return Optional.of(url);
}
return Optional.empty();
}
}

public static List<Url> getEntities() throws SQLException {
String sql = "SELECT * FROM urls";
try (Connection conn = dataSource.getConnection();
PreparedStatement preparedStatement = conn.prepareStatement(sql)) {
ResultSet resultSet = preparedStatement.executeQuery();
List<Url> result = new ArrayList<Url>();
while (resultSet.next()) {
Long id = resultSet.getLong("id");
String name = resultSet.getString("name");
LocalDateTime createdAt = resultSet.getTimestamp("created_at").toLocalDateTime();

Url url = new Url(name, createdAt);
url.setId(id);
result.add(url);
}
return result;
}
}
}
Loading
Loading