Rohan Sircar
3 years ago
44 changed files with 1683 additions and 186 deletions
-
29.dockerignore
-
18.github/workflows/build.yaml
-
3.gitignore
-
41build.sbt
-
23build.sh
-
4captain-definition
-
BINlib/monix-bio_2.13.jar
-
6modules/flyway/src/main/resources/db/migration/default/V1__create_users_table.sql
-
36modules/flyway/src/main/resources/db/migration/default/V1__library_schema.sql
-
6modules/flyway/src/main/resources/db/migration/default/V2__add_user.sql
-
69modules/flyway/src/main/resources/db/migration/default/V2__sample_data.sql
-
6modules/flyway/src/main/resources/db/migration/default/V3__create_cars_table.sql
-
6modules/flyway/src/main/resources/db/migration/default/V4__add_car.sql
-
12modules/flyway/src/main/resources/db/migration/default/V5__authors_books_table.sql
-
14modules/flyway/src/main/resources/db/migration/default/V6__insert_books_and_authors.sql
-
11native
-
2project/plugins.sbt
-
5scripts/.env
-
46scripts/app.Dockerfile
-
7scripts/app.sh
-
7scripts/curl
-
4scripts/db.Dockerfile
-
6scripts/db.sh
-
41scripts/docker-compose.yml
-
4scripts/test.Dockerfile
-
22src/main/resources/META-INF/native-image/wow/doge/http4sdemo/jni-config.json
-
309src/main/resources/META-INF/native-image/wow/doge/http4sdemo/reflect-config.json
-
19src/main/resources/META-INF/native-image/wow/doge/http4sdemo/resource-config.json
-
2src/main/resources/META-INF/native-image/wow/doge/http4sdemo/serialization-config.json
-
18src/main/resources/application.conf
-
97src/main/scala/wow/doge/http4sdemo/Http4sdemoRoutes.scala
-
7src/main/scala/wow/doge/http4sdemo/Http4sdemoServer.scala
-
3src/main/scala/wow/doge/http4sdemo/Main.scala
-
66src/main/scala/wow/doge/http4sdemo/dto/Library.scala
-
7src/main/scala/wow/doge/http4sdemo/implicits/package.scala
-
208src/main/scala/wow/doge/http4sdemo/services/LibraryService.scala
-
111src/test/scala/wow/doge/http4sdemo/DatabaseIntegrationTestBase.scala
-
25src/test/scala/wow/doge/http4sdemo/HelloWorldSpec.scala
-
166src/test/scala/wow/doge/http4sdemo/LibraryControllerSpec.scala
-
123src/test/scala/wow/doge/http4sdemo/LibraryServiceSpec.scala
-
51src/test/scala/wow/doge/http4sdemo/LibrarySpec2.scala
-
12src/test/scala/wow/doge/http4sdemo/LoggerFixtureSpec.scala
-
35src/test/scala/wow/doge/http4sdemo/MonixBioSuite.scala
-
182wait-for-it.sh
@ -0,0 +1,29 @@ |
|||||
|
*.class |
||||
|
*.log |
||||
|
|
||||
|
# sbt specific |
||||
|
.cache/ |
||||
|
.history/ |
||||
|
.lib/ |
||||
|
dist/* |
||||
|
target/ |
||||
|
lib_managed/ |
||||
|
src_managed/ |
||||
|
project/boot/ |
||||
|
project/plugins/project/ |
||||
|
metals.sbt |
||||
|
.metals |
||||
|
.bloop |
||||
|
.ammonite |
||||
|
.bsp |
||||
|
|
||||
|
# Scala-IDE specific |
||||
|
.scala_dependencies |
||||
|
.worksheet |
||||
|
|
||||
|
.idea/ |
||||
|
.vscode |
||||
|
assets/ |
||||
|
.attach_pid* |
||||
|
hs_err_pid* |
||||
|
*.db |
@ -0,0 +1,18 @@ |
|||||
|
name: Build |
||||
|
on: push |
||||
|
|
||||
|
jobs: |
||||
|
build: |
||||
|
runs-on: ubuntu-latest |
||||
|
steps: |
||||
|
- name: Checkout |
||||
|
uses: actions/checkout@v2 |
||||
|
# - name: Coursier cache |
||||
|
# uses: coursier/cache-action@v6 |
||||
|
- name: Setup |
||||
|
uses: olafurpg/setup-scala@v10 |
||||
|
with: |
||||
|
java-version: adopt@1.11 |
||||
|
# - run: sbt compile |
||||
|
# - run: sbt test |
||||
|
- run: ./build.sh |
@ -0,0 +1,23 @@ |
|||||
|
# export POSTGRES_DB=codegen_db |
||||
|
export CODEGEN_DB_HOST=localhost |
||||
|
export CODEGEN_DB_NAME=codegen_db |
||||
|
export CODEGEN_DB_USER=codegen_user |
||||
|
export CODEGEN_DB_PASSWORD=password |
||||
|
export CODEGEN_DB_PORT=5435 |
||||
|
|
||||
|
cid=$(docker run \ |
||||
|
-e POSTGRES_DB=$CODEGEN_DB_NAME \ |
||||
|
-e POSTGRES_USER=$CODEGEN_DB_USER \ |
||||
|
-e POSTGRES_PASSWORD=$CODEGEN_DB_PASSWORD \ |
||||
|
-p $CODEGEN_DB_PORT:5432 \ |
||||
|
-d postgres:12) |
||||
|
|
||||
|
echo "Container id is $cid" |
||||
|
sleep 5s |
||||
|
# ./wait-for-it.sh localhost:5434 -s -t 300 -- echo "db started" |
||||
|
sbtn flyway/flywayMigrate |
||||
|
sbtn docker:publishLocal |
||||
|
sbtn shutdown |
||||
|
|
||||
|
docker stop $cid |
||||
|
docker rm $cid |
@ -0,0 +1,4 @@ |
|||||
|
{ |
||||
|
"schemaVersion": 2, |
||||
|
"image": "rohansircar/http4s-demo:0.0.1" |
||||
|
} |
@ -1,6 +0,0 @@ |
|||||
create table "users" ( |
|
||||
"id" VARCHAR(255) PRIMARY KEY NOT NULL, |
|
||||
"email" VARCHAR(1024) NOT NULL, |
|
||||
created_at TIMESTAMP NOT NULL, |
|
||||
updated_at TIMESTAMP NULL |
|
||||
); |
|
@ -0,0 +1,36 @@ |
|||||
|
create table authors ( |
||||
|
author_id SERIAL PRIMARY KEY, |
||||
|
author_name VARCHAR(30) NOT NULL |
||||
|
); |
||||
|
|
||||
|
CREATE TABLE books ( |
||||
|
book_id SERIAL PRIMARY KEY, |
||||
|
isbn VARCHAR(50) UNIQUE NOT NULL, |
||||
|
book_title VARCHAR(30) NOT NULL, |
||||
|
author_id INTEGER REFERENCES authors(author_id) NOT NULL, |
||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL |
||||
|
); |
||||
|
|
||||
|
create table books_store ( |
||||
|
books_store_id SERIAL PRIMARY KEY, |
||||
|
book_id INTEGER REFERENCES books(book_id) NOT NULL, |
||||
|
quantity INTEGER NOT NULL |
||||
|
); |
||||
|
|
||||
|
create table book_expiry ( |
||||
|
book_expiry_id SERIAL PRIMARY KEY, |
||||
|
book_id INTEGER REFERENCES books(book_id) NOT NULL, |
||||
|
discontinued BOOLEAN NOT NULL |
||||
|
); |
||||
|
|
||||
|
create table users ( |
||||
|
user_id SERIAL PRIMARY KEY NOT NULL, |
||||
|
user_name VARCHAR(30) NOT NULL |
||||
|
); |
||||
|
|
||||
|
create table checkouts ( |
||||
|
checkout_id SERIAL PRIMARY KEY, |
||||
|
book_id INTEGER REFERENCES books(book_id) NOT NULL, |
||||
|
taken_by INTEGER REFERENCES users(user_id) NOT NULL, |
||||
|
return_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP |
||||
|
); |
@ -1,6 +0,0 @@ |
|||||
INSERT INTO "users" VALUES ( |
|
||||
'd074bce8-a8ca-49ec-9225-a50ffe83dc2f', |
|
||||
'myuser@example.com', |
|
||||
(TIMESTAMP '2013-03-26T17:50:06Z'), |
|
||||
(TIMESTAMP '2013-03-26T17:50:06Z') |
|
||||
); |
|
@ -0,0 +1,69 @@ |
|||||
|
insert into |
||||
|
authors (author_name) |
||||
|
values |
||||
|
('Author1'); |
||||
|
|
||||
|
insert into |
||||
|
authors (author_name) |
||||
|
values |
||||
|
('Author2'); |
||||
|
|
||||
|
insert into |
||||
|
authors (author_name) |
||||
|
values |
||||
|
('Author3'); |
||||
|
|
||||
|
insert into |
||||
|
books (isbn, book_title, author_id) |
||||
|
values |
||||
|
('aebwegbwe', 'book1', 3); |
||||
|
|
||||
|
insert into |
||||
|
books (isbn, book_title, author_id) |
||||
|
values |
||||
|
('abeqegbqeg', 'book2', 2); |
||||
|
|
||||
|
insert into |
||||
|
books (isbn, book_title, author_id) |
||||
|
values |
||||
|
('aebhqeqegq', 'book3', 1); |
||||
|
|
||||
|
insert into |
||||
|
books_store (book_id, quantity) |
||||
|
values |
||||
|
(1, 5); |
||||
|
|
||||
|
insert into |
||||
|
books_store (book_id, quantity) |
||||
|
values |
||||
|
(2, 3); |
||||
|
|
||||
|
insert into |
||||
|
books_store (book_id, quantity) |
||||
|
values |
||||
|
(3, 8); |
||||
|
|
||||
|
insert into |
||||
|
book_expiry (book_id, discontinued) |
||||
|
values |
||||
|
(1, false); |
||||
|
|
||||
|
insert into |
||||
|
book_expiry (book_id, discontinued) |
||||
|
values |
||||
|
(2, false); |
||||
|
|
||||
|
insert into |
||||
|
book_expiry (book_id, discontinued) |
||||
|
values |
||||
|
(3, false); |
||||
|
|
||||
|
insert into |
||||
|
users (user_name) |
||||
|
values |
||||
|
('user1'); |
||||
|
|
||||
|
insert into |
||||
|
users (user_name) |
||||
|
values |
||||
|
('user2'); |
@ -1,6 +0,0 @@ |
|||||
create table "cars" ( |
|
||||
"id" VARCHAR(255) PRIMARY KEY NOT NULL, |
|
||||
"model" VARCHAR(1024) NOT NULL, |
|
||||
created_at TIMESTAMP NOT NULL, |
|
||||
updated_at TIMESTAMP NULL |
|
||||
); |
|
@ -1,6 +0,0 @@ |
|||||
INSERT INTO "cars" VALUES ( |
|
||||
'd074bce8-a8ca-49ec-9225-a50ffe83dc2f', |
|
||||
'gxxer', |
|
||||
(TIMESTAMP '2013-03-26T17:50:06Z'), |
|
||||
(TIMESTAMP '2013-03-26T17:50:06Z') |
|
||||
); |
|
@ -1,12 +0,0 @@ |
|||||
create table authors ( |
|
||||
id SERIAL PRIMARY KEY, |
|
||||
name VARCHAR(15) NOT NULL |
|
||||
); |
|
||||
|
|
||||
create table books ( |
|
||||
id SERIAL PRIMARY KEY, |
|
||||
title VARCHAR(50) NOT NULL, |
|
||||
author_id INTEGER NOT NULL, |
|
||||
FOREIGN KEY(author_id) REFERENCES authors(id), |
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL |
|
||||
); |
|
@ -1,14 +0,0 @@ |
|||||
-- create table authors ( |
|
||||
-- id INTEGER PRIMARY KEY NOT NULL, |
|
||||
-- name VARCHAR(15) |
|
||||
-- ); |
|
||||
|
|
||||
-- create table books ( |
|
||||
-- id INTEGER PRIMARY KEY NOT NULL, |
|
||||
-- title VARCHAR(15) NOT NULL, |
|
||||
-- author_id INTEGER NOT NULL, |
|
||||
-- FOREIGN KEY(author_id) REFERENCES authors(id) |
|
||||
-- ); |
|
||||
|
|
||||
INSERT INTO authors (name) VALUES ('Jane Austen'); |
|
||||
INSERT INTO books (title, author_id) VALUES ('Pride and Prejudice', 1); |
|
@ -0,0 +1,11 @@ |
|||||
|
native-image --trace-class-initialization --static -H:+ReportExceptionStackTraces -H:+AddAllCharsets --allow-incomplete-classpath --no-fallback --initialize-at-build-time --enable-http --enable-https --enable-all-security-services --initialize-at-run-time=org.flywaydb.core.internal.scanner.cloud.s3.AwsS3Scanner \ |
||||
|
--initialize-at-run-time=org.flywaydb.core.internal.scanner.classpath.jboss.JBossVFSv3ClassPathLocationScanner \ |
||||
|
--initialize-at-run-time=org.postgresql.sspi.SSPIClient \ |
||||
|
--initialize-at-build-time=scala.runtime.Statics$VM \ |
||||
|
--initialize-at-run-time=scala.tools.nsc.profile.ExtendedThreadMxBean \ |
||||
|
--verbose -jar "./target/scala-2.13/http4s-demo-assembly-0.0.1-SNAPSHOT.jar" http4s-demoBinaryImage |
||||
|
|
||||
|
|
||||
|
|
||||
|
--initialize-at-run-time=scala.tools.nsc.profile.ExtendedThreadMxBean \ |
||||
|
--initialize-at-build-time=scala.tools.nsc.profile.SunThreadMxBean \ |
@ -0,0 +1,5 @@ |
|||||
|
export POSTGRES_DB=codegen_db |
||||
|
export CODEGEN_DB_HOST=localhost |
||||
|
export CODEGEN_DB_NAME=codegen_db |
||||
|
export CODEGEN_DB_USER=codegen_user |
||||
|
export CODEGEN_DB_PASSWORD=password |
@ -0,0 +1,46 @@ |
|||||
|
FROM scala/coursier-sbt:0.0.2 |
||||
|
|
||||
|
ARG DOCKER_TAG |
||||
|
|
||||
|
# RUN apt-get update |
||||
|
# RUN apt-get -y install git |
||||
|
# RUN apt-get -y install curl |
||||
|
# RUN sh -c '(echo "#!/usr/bin/env sh" && curl -fLo cs https://git.io/coursier-cli-"$(uname | tr LD ld)") && chmod +x cs' |
||||
|
# RUN ./cs install cs |
||||
|
# ENV PATH=${PATH}:/root/.local/share/coursier/bin |
||||
|
# RUN export PATH="$PATH:/root/.local/share/coursier/bin" |
||||
|
# RUN rm ./cs |
||||
|
|
||||
|
# ENV PATH=${PATH}:/root/.local/share/coursier/bin |
||||
|
# RUN export PATH="$PATH:/root/.local/share/coursier/bin" |
||||
|
# RUN mkdir -p /root/.local/share/coursier |
||||
|
# COPY coursier/bin /root/.local/share/coursier/bin |
||||
|
# RUN echo $PATH |
||||
|
# RUN cs install sbt |
||||
|
|
||||
|
RUN mkdir -p /usr/src/app/bin |
||||
|
WORKDIR /usr/src/app |
||||
|
COPY ./ /usr/src/app |
||||
|
|
||||
|
# RUN cat /etc/hosts |
||||
|
|
||||
|
# COPY wait-for-it.sh wait-for-it.sh |
||||
|
# RUN chmod +x wait-for-it.sh |
||||
|
# ENTRYPOINT [ "/bin/bash", "-c" ] |
||||
|
# CMD ["./wait-for-it.sh" , "project_db:5432" , "--strict" , "--timeout=30000" , "--" , "echo 'db has started'"] |
||||
|
# RUN bash ./wait-for-it.sh project_db:5432 --timeout=3000 --strict -- echo "db is up" |
||||
|
|
||||
|
# RUN cat /etc/hosts |
||||
|
# CMD [ "sbt" , "flyway/flywayMigrate" ] |
||||
|
# CMD ["sbtn","universal:packageBin"] |
||||
|
# CMD sh sbtn flyway/flywayMigrate; sbtn universal:packageBin |
||||
|
# RUN sbt flyway/flywayMigrate |
||||
|
# RUN sbt docker:stage |
||||
|
|
||||
|
CMD sh Docker/app.sh |
||||
|
|
||||
|
# CMD ["coursier", "--help"] |
||||
|
|
||||
|
# RUN coursier install sbt |
||||
|
# RUN sbt docker:stage |
||||
|
# RUN |
@ -0,0 +1,7 @@ |
|||||
|
sbtn flyway/flywayMigrate |
||||
|
sbtn universal:packageZipTarball |
||||
|
tar -xf target/universal/http4s-demo-0.0.1-SNAPSHOT.tgz -C bin |
||||
|
# ./http4s-demo-0.0.1-SNAPSHOT/bin/http4s-demo |
||||
|
# sbtn docker:stage |
||||
|
# mv targer/docker/** bin |
||||
|
rm -r target |
@ -0,0 +1,7 @@ |
|||||
|
curl -X POST -H "content-type: application/json" http://localhost:8081/api/post/book --data '{"aege":"aaegqE"}' |
||||
|
curl http://localhost:8081/api/get/books |
||||
|
curl http://localhost:8081/api/get/book/1 |
||||
|
curl -X POST -H "content-type: application/json" http://localhost:8081/api/post/book --data '{"title":"aaegqE", "authorId": 1}' |
||||
|
curl -X PATCH -H "content-type: application/json" http://localhost:8081/api/update/book/2 --data '{"title":"abwbewe"}' |
||||
|
|
||||
|
|
@ -0,0 +1,4 @@ |
|||||
|
FROM postgres:12 |
||||
|
ENV POSTGRES_USER test_user |
||||
|
ENV POSTGRES_PASSWORD password |
||||
|
ENV POSTGRES_DB test_db |
@ -0,0 +1,6 @@ |
|||||
|
docker run \ |
||||
|
-e POSTGRES_DB=test_db \ |
||||
|
-e POSTGRES_USER=test_user \ |
||||
|
-e POSTGRES_PASSWORD=password \ |
||||
|
-p 5433:5432 \ |
||||
|
postgres:12 |
@ -0,0 +1,41 @@ |
|||||
|
version: "3.3" |
||||
|
services: |
||||
|
|
||||
|
|
||||
|
db: |
||||
|
container_name: project_db |
||||
|
image: postgres:12 |
||||
|
# build: |
||||
|
# context: ./Docker |
||||
|
# dockerfile: db.Dockerfile |
||||
|
environment: |
||||
|
POSTGRES_DB: 'codegen_db' |
||||
|
POSTGRES_USER: 'codegen_user' |
||||
|
POSTGRES_PASSWORD: 'password' |
||||
|
# volumes: |
||||
|
# - ./var/pgdata:/var/lib/postgresql/data |
||||
|
ports: |
||||
|
- "5432:5433" |
||||
|
# network_mode: host |
||||
|
backend: |
||||
|
container_name: project_backend |
||||
|
build: |
||||
|
context: . |
||||
|
dockerfile: app.Dockerfile |
||||
|
# ports: |
||||
|
# - "9000:9001" |
||||
|
environment: |
||||
|
POSTGRES_DB: 'codegen_db' |
||||
|
CODEGEN_DB_HOST: 'project_db' |
||||
|
CODEGEN_DB_NAME: 'codegen_db' |
||||
|
CODEGEN_DB_USER: 'codegen_user' |
||||
|
CODEGEN_DB_PASSWORD: 'password' |
||||
|
volumes: |
||||
|
- ./app:/usr/src/app/bin |
||||
|
# links: |
||||
|
# - db |
||||
|
# # command: ["./wait-for-it.sh", "project_db:5432", "--strict" , "--timeout=30000" , "--" , "echo 'db has started'"] |
||||
|
# depends_on: |
||||
|
# - db |
||||
|
# # condition: service_healthy |
||||
|
|
@ -0,0 +1,4 @@ |
|||||
|
FROM scala/coursier/sbt:v0.0.1 |
||||
|
# RUN apt search docker |
||||
|
RUN apt install -y docker.io |
||||
|
RUN docker --help |
@ -0,0 +1,22 @@ |
|||||
|
[ |
||||
|
{ |
||||
|
"name":"java.lang.ClassLoader", |
||||
|
"methods":[{"name":"getPlatformClassLoader","parameterTypes":[] }] |
||||
|
}, |
||||
|
{ |
||||
|
"name":"java.lang.NoSuchMethodError" |
||||
|
}, |
||||
|
{ |
||||
|
"name":"sun.management.VMManagementImpl", |
||||
|
"fields":[ |
||||
|
{"name":"compTimeMonitoringSupport"}, |
||||
|
{"name":"currentThreadCpuTimeSupport"}, |
||||
|
{"name":"objectMonitorUsageSupport"}, |
||||
|
{"name":"otherThreadCpuTimeSupport"}, |
||||
|
{"name":"remoteDiagnosticCommandsSupport"}, |
||||
|
{"name":"synchronizerUsageSupport"}, |
||||
|
{"name":"threadAllocatedMemorySupport"}, |
||||
|
{"name":"threadContentionMonitoringSupport"} |
||||
|
] |
||||
|
} |
||||
|
] |
@ -0,0 +1,19 @@ |
|||||
|
{ |
||||
|
"resources":{ |
||||
|
"includes":[ |
||||
|
{"pattern":"\\QMETA-INF/services/java.sql.Driver\\E"}, |
||||
|
{"pattern":"\\Qapplication.conf\\E"}, |
||||
|
{"pattern":"\\Qdb/migration/default/V1__create_users_table.sql\\E"}, |
||||
|
{"pattern":"\\Qdb/migration/default/V2__add_user.sql\\E"}, |
||||
|
{"pattern":"\\Qdb/migration/default/V3__create_cars_table.sql\\E"}, |
||||
|
{"pattern":"\\Qdb/migration/default/V4__add_car.sql\\E"}, |
||||
|
{"pattern":"\\Qdb/migration/default/V5__authors_books_table.sql\\E"}, |
||||
|
{"pattern":"\\Qdb/migration/default/V6__insert_books_and_authors.sql\\E"}, |
||||
|
{"pattern":"\\Qdb/migration/default\\E"}, |
||||
|
{"pattern":"\\Qlogback.xml\\E"}, |
||||
|
{"pattern":"\\Qorg/flywaydb/core/internal/version.txt\\E"}, |
||||
|
{"pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E"}, |
||||
|
{"pattern":"\\Qreference.conf\\E"} |
||||
|
]}, |
||||
|
"bundles":[] |
||||
|
} |
@ -0,0 +1,2 @@ |
|||||
|
[ |
||||
|
] |
@ -0,0 +1,111 @@ |
|||||
|
package wow.doge.http4sdemo |
||||
|
|
||||
|
import com.dimafeng.testcontainers.ContainerDef |
||||
|
import com.dimafeng.testcontainers.PostgreSQLContainer |
||||
|
import com.dimafeng.testcontainers.munit.TestContainerForAll |
||||
|
import com.typesafe.config.ConfigFactory |
||||
|
import monix.bio.IO |
||||
|
import monix.bio.Task |
||||
|
import monix.bio.UIO |
||||
|
import monix.execution.Scheduler |
||||
|
import org.testcontainers.utility.DockerImageName |
||||
|
import slick.jdbc.JdbcBackend |
||||
|
import slick.jdbc.PostgresProfile |
||||
|
|
||||
|
trait DatabaseIntegrationTestBase |
||||
|
extends MonixBioSuite |
||||
|
with TestContainerForAll { |
||||
|
def databaseName = "testcontainer-scala" |
||||
|
def username = "scala" |
||||
|
def password = "scala" |
||||
|
|
||||
|
override val containerDef: ContainerDef = PostgreSQLContainer.Def( |
||||
|
dockerImageName = DockerImageName.parse("postgres:13.2"), |
||||
|
databaseName = databaseName, |
||||
|
username = username, |
||||
|
password = password |
||||
|
) |
||||
|
|
||||
|
lazy val profile = PostgresProfile |
||||
|
|
||||
|
def config(url: String) = ConfigFactory.parseString(s"""| |
||||
|
|testDatabase = { |
||||
|
| url = "$url" |
||||
|
| driver = org.postgresql.Driver |
||||
|
| user = $username |
||||
|
| password = $password |
||||
|
| |
||||
|
| numThreads = 2 |
||||
|
| |
||||
|
| queueSize = 10 |
||||
|
| |
||||
|
| maxThreads = 2 |
||||
|
| |
||||
|
| maxConnections = 2 |
||||
|
| |
||||
|
}""".stripMargin) |
||||
|
|
||||
|
def withDb[T](url: String)(f: JdbcBackend.DatabaseDef => Task[T]) = Task( |
||||
|
// JdbcBackend.Database.forURL( |
||||
|
// url, |
||||
|
// // user = username, |
||||
|
// // password = password, |
||||
|
// // driver = "org.postgresql.Driver", |
||||
|
// prop = Map( |
||||
|
// "driver" -> "org.postgresql.Driver", |
||||
|
// "user" -> username, |
||||
|
// "password" -> password, |
||||
|
// "numThreads" -> "16", |
||||
|
// "maxThreads" -> "36", |
||||
|
// "queueSize" -> "10", |
||||
|
// "maxConnections" -> "36" |
||||
|
// ) |
||||
|
// ) |
||||
|
JdbcBackend.Database.forConfig("testDatabase", config(url)) |
||||
|
).bracket(f)(db => UIO(db.close())) |
||||
|
|
||||
|
def createSchema(containers: Containers) = { |
||||
|
implicit val s = Scheduler.global |
||||
|
containers match { |
||||
|
case container: PostgreSQLContainer => |
||||
|
val config = JdbcDatabaseConfig( |
||||
|
container.jdbcUrl, |
||||
|
"org.postgresql.Driver", |
||||
|
Some(username), |
||||
|
Some(password), |
||||
|
"flyway_schema_history", |
||||
|
List("classpath:db/migration/default") |
||||
|
) |
||||
|
// (UIO(println("creating db")) >> dbBracket(container.jdbcUrl)( |
||||
|
// // _.runL(Tables.schema.create) |
||||
|
// _ => DBMigrations.migrate[Task](config) |
||||
|
// )) |
||||
|
DBMigrations.migrate[Task](config).runSyncUnsafe(munitTimeout) |
||||
|
case _ => () |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// val fixture = ResourceFixture( |
||||
|
// Resource.make( |
||||
|
// Task( |
||||
|
// JdbcBackend.Database.forURL( |
||||
|
// "jdbc:postgresql://localhost:49162/testcontainer-scala?", |
||||
|
// user = username, |
||||
|
// password = password, |
||||
|
// driver = "org.postgresql.Driver" |
||||
|
// ) |
||||
|
// ) |
||||
|
// )(db => Task(db.close())) |
||||
|
// ) |
||||
|
|
||||
|
def withContainersIO[A](pf: PartialFunction[Containers, Task[A]]): Task[A] = { |
||||
|
withContainers { containers => |
||||
|
pf.applyOrElse( |
||||
|
containers, |
||||
|
(c: Containers) => |
||||
|
IO.terminate(new Exception(s"Unknown container: ${c.toString}")) |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
@ -1,25 +0,0 @@ |
|||||
package wow.doge.http4sdemo |
|
||||
|
|
||||
import cats.effect.IO |
|
||||
import org.http4s._ |
|
||||
import org.http4s.implicits._ |
|
||||
import munit.CatsEffectSuite |
|
||||
class HelloWorldSpec extends CatsEffectSuite { |
|
||||
|
|
||||
// test("HelloWorld returns status code 200") { |
|
||||
// assertIO(retHelloWorld.map(_.status), Status.Ok) |
|
||||
// } |
|
||||
|
|
||||
// test("HelloWorld returns hello world message") { |
|
||||
// assertIO( |
|
||||
// retHelloWorld.flatMap(_.as[String]), |
|
||||
// "{\"message\":\"Hello, world\"}" |
|
||||
// ) |
|
||||
// } |
|
||||
|
|
||||
// private[this] val retHelloWorld: IO[Response[IO]] = { |
|
||||
// val getHW = Request[IO](Method.GET, uri"/hello/world") |
|
||||
// val helloWorld = HelloWorld.impl[IO] |
|
||||
// Http4sdemoRoutes.helloWorldRoutes(helloWorld).orNotFound(getHW) |
|
||||
// } |
|
||||
} |
|
@ -0,0 +1,166 @@ |
|||||
|
package wow.doge.http4sdemo |
||||
|
|
||||
|
import java.time.LocalDateTime |
||||
|
|
||||
|
import cats.syntax.all._ |
||||
|
import monix.bio.IO |
||||
|
import monix.bio.Task |
||||
|
import monix.bio.UIO |
||||
|
import monix.reactive.Observable |
||||
|
import org.http4s.Method |
||||
|
import org.http4s.Request |
||||
|
import org.http4s.Uri |
||||
|
import org.http4s.implicits._ |
||||
|
import wow.doge.http4sdemo.dto.Book |
||||
|
import wow.doge.http4sdemo.dto.BookSearchMode |
||||
|
import wow.doge.http4sdemo.dto.BookUpdate |
||||
|
import wow.doge.http4sdemo.services.LibraryService |
||||
|
import wow.doge.http4sdemo.services.NoopLibraryService |
||||
|
|
||||
|
class LibraryControllerSpec extends MonixBioSuite { |
||||
|
|
||||
|
// "libraryControllerSpec" |
||||
|
// val fixture = loggerFixture() |
||||
|
// ResourceFixture |
||||
|
|
||||
|
// override def munitFixtures = List(myFixture) |
||||
|
// override def munitFixtures: Seq[Fixture[_]] = ??? |
||||
|
|
||||
|
val date = LocalDateTime.now() |
||||
|
|
||||
|
|
||||
|
// val logger = consoleLogger[Task]() |
||||
|
|
||||
|
val Root = Uri(path = "") |
||||
|
|
||||
|
test("get books success") { |
||||
|
import org.http4s.circe.CirceEntityCodec._ |
||||
|
val book = Book(1, "book1", "adsgq342dsdc", 1, date) |
||||
|
val service = new NoopLibraryService { |
||||
|
|
||||
|
override def getBooks: Observable[Book] = |
||||
|
Observable.fromIterable(book :: Nil) |
||||
|
|
||||
|
override def getBookById(id: Int): Task[Option[Book]] = |
||||
|
Task.some(book) |
||||
|
|
||||
|
} |
||||
|
for { |
||||
|
_ <- UIO.unit |
||||
|
routes = Http4sdemoRoutes.libraryRoutes(service) |
||||
|
res <- routes |
||||
|
.run(Request[Task](Method.GET, uri"/api/get/books")) |
||||
|
.value |
||||
|
.hideErrors |
||||
|
body <- res.map(_.as[List[Book]]).sequence |
||||
|
_ <- UIO(assertEquals(body, Some(List(book)))) |
||||
|
// _ <- logger2.debug(body.toString).hideErrors |
||||
|
} yield () |
||||
|
} |
||||
|
|
||||
|
test("update book error") { |
||||
|
import org.http4s.circe.CirceEntityCodec._ |
||||
|
val service = new NoopLibraryService { |
||||
|
override def updateBook(id: Int, updateData: BookUpdate) = |
||||
|
IO.raiseError( |
||||
|
LibraryService.EntityDoesNotExist(s"Book with id=$id does not exist") |
||||
|
) |
||||
|
} |
||||
|
|
||||
|
for { |
||||
|
_ <- UIO.unit |
||||
|
reqBody = BookUpdate(Some("blah"), None) |
||||
|
routes = Http4sdemoRoutes.libraryRoutes(service) |
||||
|
res <- routes |
||||
|
.run( |
||||
|
Request[Task](Method.PATCH, Root / "api" / "update" / "book" / "1") |
||||
|
.withEntity(reqBody) |
||||
|
) |
||||
|
.value |
||||
|
.hideErrors |
||||
|
body <- res.map(_.as[LibraryService.Error]).sequence |
||||
|
_ <- UIO( |
||||
|
assertEquals( |
||||
|
body, |
||||
|
Some( |
||||
|
LibraryService.EntityDoesNotExist("Book with id=1 does not exist") |
||||
|
) |
||||
|
) |
||||
|
) |
||||
|
// _ <- logger.debug(res.toString).hideErrors |
||||
|
// _ <- logger.debug(body.toString).hideErrors |
||||
|
} yield () |
||||
|
} |
||||
|
|
||||
|
test("get books by author name") { |
||||
|
import org.http4s.circe.CirceEntityCodec._ |
||||
|
val value = "blah" |
||||
|
val books = |
||||
|
List(Book(1, "book1", value, 1, date), Book(2, "book1", value, 1, date)) |
||||
|
val service = new NoopLibraryService { |
||||
|
override def searchBook(mode: BookSearchMode, value: String) = |
||||
|
mode match { |
||||
|
case BookSearchMode.BookTitle => |
||||
|
Observable.fromIterable(books) |
||||
|
case BookSearchMode.AuthorName => |
||||
|
Observable.fromIterable(books) |
||||
|
} |
||||
|
} |
||||
|
for { |
||||
|
_ <- UIO.unit |
||||
|
// logger2 = logger.withConstContext( |
||||
|
// Map("Test" -> "get books by author name") |
||||
|
// ) |
||||
|
routes = Http4sdemoRoutes.libraryRoutes(service) |
||||
|
request = Request[Task]( |
||||
|
Method.GET, |
||||
|
Root / "api" / "get" / "book" |
||||
|
withQueryParams Map( |
||||
|
"mode" -> BookSearchMode.AuthorName.entryName, |
||||
|
"value" -> "blah" |
||||
|
) |
||||
|
) |
||||
|
// _ <- logger2.info(s"Request -> $request") |
||||
|
res <- routes.run(request).value.hideErrors |
||||
|
body <- res.map(_.as[List[Book]]).sequence |
||||
|
_ <- UIO.pure(body).assertEquals(Some(books)) |
||||
|
// _ <- logger2.debug(s"Response body -> $body").hideErrors |
||||
|
} yield () |
||||
|
} |
||||
|
|
||||
|
test("get books by book title") { |
||||
|
import org.http4s.circe.CirceEntityCodec._ |
||||
|
val value = "blah" |
||||
|
val books = |
||||
|
List(Book(1, "book1", value, 1, date), Book(2, "book1", value, 1, date)) |
||||
|
val service = new NoopLibraryService { |
||||
|
override def searchBook(mode: BookSearchMode, value: String) = |
||||
|
mode match { |
||||
|
case BookSearchMode.BookTitle => |
||||
|
Observable.fromIterable(books) |
||||
|
case BookSearchMode.AuthorName => |
||||
|
Observable.fromIterable(books) |
||||
|
} |
||||
|
} |
||||
|
for { |
||||
|
_ <- UIO.unit |
||||
|
// logger2 = logger.withConstContext( |
||||
|
// Map("Test" -> "get books by book title") |
||||
|
// ) |
||||
|
routes = Http4sdemoRoutes.libraryRoutes(service) |
||||
|
request = Request[Task]( |
||||
|
Method.GET, |
||||
|
Root / "api" / "get" / "book" |
||||
|
withQueryParams Map( |
||||
|
"mode" -> BookSearchMode.BookTitle.entryName, |
||||
|
"value" -> "blah" |
||||
|
) |
||||
|
) |
||||
|
// _ <- logger2.info(s"Request -> $request") |
||||
|
res <- routes.run(request).value.hideErrors |
||||
|
body <- res.map(_.as[List[Book]]).sequence |
||||
|
_ <- UIO.pure(body).assertEquals(Some(books)) |
||||
|
// _ <- logger2.debug(s"Response body -> $body").hideErrors |
||||
|
} yield () |
||||
|
} |
||||
|
} |
@ -0,0 +1,123 @@ |
|||||
|
package wow.doge.http4sdemo |
||||
|
|
||||
|
import cats.syntax.all._ |
||||
|
import com.dimafeng.testcontainers.PostgreSQLContainer |
||||
|
import monix.bio.UIO |
||||
|
import wow.doge.http4sdemo.dto.BookSearchMode |
||||
|
import wow.doge.http4sdemo.dto.NewAuthor |
||||
|
import wow.doge.http4sdemo.dto.NewBook |
||||
|
import wow.doge.http4sdemo.implicits._ |
||||
|
import wow.doge.http4sdemo.services.LibraryDbio |
||||
|
import wow.doge.http4sdemo.services.LibraryService |
||||
|
import wow.doge.http4sdemo.services.LibraryServiceImpl |
||||
|
|
||||
|
class LibraryServiceSpec extends DatabaseIntegrationTestBase { |
||||
|
|
||||
|
override def afterContainersStart(containers: Containers): Unit = { |
||||
|
super.afterContainersStart(containers) |
||||
|
createSchema(containers) |
||||
|
} |
||||
|
|
||||
|
test("insert and retrieve book") { |
||||
|
withContainersIO { case container: PostgreSQLContainer => |
||||
|
val io = |
||||
|
withDb(container.jdbcUrl)(db => |
||||
|
for { |
||||
|
_ <- UIO.unit |
||||
|
service: LibraryService = new LibraryServiceImpl( |
||||
|
profile, |
||||
|
new LibraryDbio(profile), |
||||
|
db |
||||
|
) |
||||
|
id <- service.insertAuthor(NewAuthor("author1")) |
||||
|
book <- service.insertBook(NewBook("blah", "Segehwe", id)) |
||||
|
_ <- service |
||||
|
.getBookById(book.bookId) |
||||
|
.flatTap(r => UIO(println(r))) |
||||
|
.assertEquals(Some(book)) |
||||
|
} yield () |
||||
|
) |
||||
|
io |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
test("author does not exist error on book insertion") { |
||||
|
withContainersIO { case container: PostgreSQLContainer => |
||||
|
val io = |
||||
|
withDb(container.jdbcUrl)(db => |
||||
|
for { |
||||
|
_ <- UIO.unit |
||||
|
service: LibraryService = new LibraryServiceImpl( |
||||
|
profile, |
||||
|
new LibraryDbio(profile), |
||||
|
db |
||||
|
) |
||||
|
_ <- service |
||||
|
.insertBook(NewBook("blah2", "agege", 23)) |
||||
|
.attempt |
||||
|
.assertEquals( |
||||
|
Left( |
||||
|
LibraryService |
||||
|
.EntityDoesNotExist("Author with id=23 does not exist") |
||||
|
) |
||||
|
) |
||||
|
} yield () |
||||
|
) |
||||
|
io |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
test("books with isbn already exists error on book insertion") { |
||||
|
withContainersIO { case container: PostgreSQLContainer => |
||||
|
val io = |
||||
|
withDb(container.jdbcUrl)(db => |
||||
|
for { |
||||
|
_ <- UIO.unit |
||||
|
service: LibraryService = new LibraryServiceImpl( |
||||
|
profile, |
||||
|
new LibraryDbio(profile), |
||||
|
db |
||||
|
) |
||||
|
_ <- service.insertBook(NewBook("blah2", "agege", 1)) |
||||
|
_ <- service |
||||
|
.insertBook(NewBook("blah3", "agege", 1)) |
||||
|
.attempt |
||||
|
.assertEquals( |
||||
|
Left( |
||||
|
LibraryService |
||||
|
.EntityAlreadyExists("Book with isbn=agege already exists") |
||||
|
) |
||||
|
) |
||||
|
} yield () |
||||
|
) |
||||
|
io |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
test("search books by author id") { |
||||
|
withContainersIO { case container: PostgreSQLContainer => |
||||
|
val io = |
||||
|
withDb(container.jdbcUrl)(db => |
||||
|
for { |
||||
|
_ <- UIO.unit |
||||
|
service: LibraryService = new LibraryServiceImpl( |
||||
|
profile, |
||||
|
new LibraryDbio(profile), |
||||
|
db |
||||
|
) |
||||
|
id <- service.insertAuthor(NewAuthor("bar")) |
||||
|
book1 <- service.insertBook(NewBook("blah3", "aeaega", id)) |
||||
|
book2 <- service.insertBook(NewBook("blah4", "afgegg", id)) |
||||
|
_ <- service |
||||
|
.searchBook(BookSearchMode.AuthorName, id.toString) |
||||
|
.toListL |
||||
|
.toIO |
||||
|
.attempt |
||||
|
.assertEquals(Right(List(book1, book2))) |
||||
|
} yield () |
||||
|
) |
||||
|
io |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,51 @@ |
|||||
|
package wow.doge.http4sdemo |
||||
|
|
||||
|
import com.dimafeng.testcontainers.PostgreSQLContainer |
||||
|
import monix.bio.IO |
||||
|
import monix.bio.UIO |
||||
|
import wow.doge.http4sdemo.services.LibraryDbio |
||||
|
import wow.doge.http4sdemo.services.LibraryServiceImpl |
||||
|
|
||||
|
class LibrarySpec2 extends DatabaseIntegrationTestBase { |
||||
|
|
||||
|
override def afterContainersStart(containers: Containers): Unit = { |
||||
|
createSchema(containers) |
||||
|
} |
||||
|
|
||||
|
test("blah") { |
||||
|
withContainers { |
||||
|
case postgresContainer: PostgreSQLContainer => |
||||
|
val io = |
||||
|
withDb(postgresContainer.jdbcUrl)(db => |
||||
|
for { |
||||
|
// _ <- db.runL(Tables.schema.create) |
||||
|
_ <- UIO.unit |
||||
|
service = new LibraryServiceImpl( |
||||
|
profile, |
||||
|
new LibraryDbio(profile), |
||||
|
db |
||||
|
) |
||||
|
_ <- service |
||||
|
.getBookById(1) |
||||
|
.hideErrors |
||||
|
.flatMap(r => UIO(println(r))) |
||||
|
} yield () |
||||
|
) |
||||
|
io |
||||
|
case other => |
||||
|
IO.terminate(new Exception(s"Invalid container ${other.toString}")) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// override val container: PostgreSQLContainer = PostgreSQLContainer() |
||||
|
|
||||
|
// "PostgreSQL container" should "be started" in { |
||||
|
// Class.forName(container.driverClassName) |
||||
|
// val connection = DriverManager.getConnection( |
||||
|
// container.jdbcUrl, |
||||
|
// container.username, |
||||
|
// container.password |
||||
|
// ) |
||||
|
// // ... |
||||
|
// } |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
package wow.doge.http4sdemo |
||||
|
|
||||
|
// import sourcecode.File |
||||
|
|
||||
|
class LoggerFixtureSpec extends MonixBioSuite { |
||||
|
// "LoggerFixtureSpec" |
||||
|
val fixture = loggerFixture() |
||||
|
|
||||
|
loggerFixture().test("blah blah") { logger => |
||||
|
logger.debug("blah blah blah") |
||||
|
} |
||||
|
} |
@ -0,0 +1,35 @@ |
|||||
|
package wow.doge.http4sdemo |
||||
|
|
||||
|
import cats.syntax.all._ |
||||
|
import io.odin.consoleLogger |
||||
|
import io.odin.fileLogger |
||||
|
import io.odin.syntax._ |
||||
|
import monix.bio.Task |
||||
|
import monix.execution.Scheduler |
||||
|
|
||||
|
import scala.concurrent.Future |
||||
|
import munit.TestOptions |
||||
|
import cats.effect.Resource |
||||
|
import io.odin.Logger |
||||
|
|
||||
|
trait MonixBioSuite extends munit.TaglessFinalSuite[Task] { |
||||
|
override protected def toFuture[A](f: Task[A]): Future[A] = { |
||||
|
implicit val s = Scheduler.global |
||||
|
f.runToFuture |
||||
|
} |
||||
|
|
||||
|
def loggerFixture(fileName: Option[String] = None)(implicit |
||||
|
enc: sourcecode.Enclosing |
||||
|
) = |
||||
|
ResourceFixture( |
||||
|
consoleLogger[Task]().withAsync() |+| fileLogger[Task]( |
||||
|
fileName.getOrElse(enc.value.split("#").head + ".log") |
||||
|
), |
||||
|
( |
||||
|
options: TestOptions, |
||||
|
value: Logger[Task] |
||||
|
) => Task(options.name), |
||||
|
(_: Logger[Task]) => Task.unit |
||||
|
) |
||||
|
|
||||
|
} |
@ -0,0 +1,182 @@ |
|||||
|
#!/usr/bin/env bash |
||||
|
# Use this script to test if a given TCP host/port are available |
||||
|
|
||||
|
WAITFORIT_cmdname=${0##*/} |
||||
|
|
||||
|
echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } |
||||
|
|
||||
|
usage() |
||||
|
{ |
||||
|
cat << USAGE >&2 |
||||
|
Usage: |
||||
|
$WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args] |
||||
|
-h HOST | --host=HOST Host or IP under test |
||||
|
-p PORT | --port=PORT TCP port under test |
||||
|
Alternatively, you specify the host and port as host:port |
||||
|
-s | --strict Only execute subcommand if the test succeeds |
||||
|
-q | --quiet Don't output any status messages |
||||
|
-t TIMEOUT | --timeout=TIMEOUT |
||||
|
Timeout in seconds, zero for no timeout |
||||
|
-- COMMAND ARGS Execute command with args after the test finishes |
||||
|
USAGE |
||||
|
exit 1 |
||||
|
} |
||||
|
|
||||
|
wait_for() |
||||
|
{ |
||||
|
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then |
||||
|
echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" |
||||
|
else |
||||
|
echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout" |
||||
|
fi |
||||
|
WAITFORIT_start_ts=$(date +%s) |
||||
|
while : |
||||
|
do |
||||
|
if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then |
||||
|
nc -z $WAITFORIT_HOST $WAITFORIT_PORT |
||||
|
WAITFORIT_result=$? |
||||
|
else |
||||
|
(echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1 |
||||
|
WAITFORIT_result=$? |
||||
|
fi |
||||
|
if [[ $WAITFORIT_result -eq 0 ]]; then |
||||
|
WAITFORIT_end_ts=$(date +%s) |
||||
|
echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds" |
||||
|
break |
||||
|
fi |
||||
|
sleep 1 |
||||
|
done |
||||
|
return $WAITFORIT_result |
||||
|
} |
||||
|
|
||||
|
wait_for_wrapper() |
||||
|
{ |
||||
|
# In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 |
||||
|
if [[ $WAITFORIT_QUIET -eq 1 ]]; then |
||||
|
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & |
||||
|
else |
||||
|
timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT & |
||||
|
fi |
||||
|
WAITFORIT_PID=$! |
||||
|
trap "kill -INT -$WAITFORIT_PID" INT |
||||
|
wait $WAITFORIT_PID |
||||
|
WAITFORIT_RESULT=$? |
||||
|
if [[ $WAITFORIT_RESULT -ne 0 ]]; then |
||||
|
echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT" |
||||
|
fi |
||||
|
return $WAITFORIT_RESULT |
||||
|
} |
||||
|
|
||||
|
# process arguments |
||||
|
while [[ $# -gt 0 ]] |
||||
|
do |
||||
|
case "$1" in |
||||
|
*:* ) |
||||
|
WAITFORIT_hostport=(${1//:/ }) |
||||
|
WAITFORIT_HOST=${WAITFORIT_hostport[0]} |
||||
|
WAITFORIT_PORT=${WAITFORIT_hostport[1]} |
||||
|
shift 1 |
||||
|
;; |
||||
|
--child) |
||||
|
WAITFORIT_CHILD=1 |
||||
|
shift 1 |
||||
|
;; |
||||
|
-q | --quiet) |
||||
|
WAITFORIT_QUIET=1 |
||||
|
shift 1 |
||||
|
;; |
||||
|
-s | --strict) |
||||
|
WAITFORIT_STRICT=1 |
||||
|
shift 1 |
||||
|
;; |
||||
|
-h) |
||||
|
WAITFORIT_HOST="$2" |
||||
|
if [[ $WAITFORIT_HOST == "" ]]; then break; fi |
||||
|
shift 2 |
||||
|
;; |
||||
|
--host=*) |
||||
|
WAITFORIT_HOST="${1#*=}" |
||||
|
shift 1 |
||||
|
;; |
||||
|
-p) |
||||
|
WAITFORIT_PORT="$2" |
||||
|
if [[ $WAITFORIT_PORT == "" ]]; then break; fi |
||||
|
shift 2 |
||||
|
;; |
||||
|
--port=*) |
||||
|
WAITFORIT_PORT="${1#*=}" |
||||
|
shift 1 |
||||
|
;; |
||||
|
-t) |
||||
|
WAITFORIT_TIMEOUT="$2" |
||||
|
if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi |
||||
|
shift 2 |
||||
|
;; |
||||
|
--timeout=*) |
||||
|
WAITFORIT_TIMEOUT="${1#*=}" |
||||
|
shift 1 |
||||
|
;; |
||||
|
--) |
||||
|
shift |
||||
|
WAITFORIT_CLI=("$@") |
||||
|
break |
||||
|
;; |
||||
|
--help) |
||||
|
usage |
||||
|
;; |
||||
|
*) |
||||
|
echoerr "Unknown argument: $1" |
||||
|
usage |
||||
|
;; |
||||
|
esac |
||||
|
done |
||||
|
|
||||
|
if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then |
||||
|
echoerr "Error: you need to provide a host and port to test." |
||||
|
usage |
||||
|
fi |
||||
|
|
||||
|
WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15} |
||||
|
WAITFORIT_STRICT=${WAITFORIT_STRICT:-0} |
||||
|
WAITFORIT_CHILD=${WAITFORIT_CHILD:-0} |
||||
|
WAITFORIT_QUIET=${WAITFORIT_QUIET:-0} |
||||
|
|
||||
|
# Check to see if timeout is from busybox? |
||||
|
WAITFORIT_TIMEOUT_PATH=$(type -p timeout) |
||||
|
WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH) |
||||
|
|
||||
|
WAITFORIT_BUSYTIMEFLAG="" |
||||
|
if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then |
||||
|
WAITFORIT_ISBUSY=1 |
||||
|
# Check if busybox timeout uses -t flag |
||||
|
# (recent Alpine versions don't support -t anymore) |
||||
|
if timeout &>/dev/stdout | grep -q -e '-t '; then |
||||
|
WAITFORIT_BUSYTIMEFLAG="-t" |
||||
|
fi |
||||
|
else |
||||
|
WAITFORIT_ISBUSY=0 |
||||
|
fi |
||||
|
|
||||
|
if [[ $WAITFORIT_CHILD -gt 0 ]]; then |
||||
|
wait_for |
||||
|
WAITFORIT_RESULT=$? |
||||
|
exit $WAITFORIT_RESULT |
||||
|
else |
||||
|
if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then |
||||
|
wait_for_wrapper |
||||
|
WAITFORIT_RESULT=$? |
||||
|
else |
||||
|
wait_for |
||||
|
WAITFORIT_RESULT=$? |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
if [[ $WAITFORIT_CLI != "" ]]; then |
||||
|
if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then |
||||
|
echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess" |
||||
|
exit $WAITFORIT_RESULT |
||||
|
fi |
||||
|
exec "${WAITFORIT_CLI[@]}" |
||||
|
else |
||||
|
exit $WAITFORIT_RESULT |
||||
|
fi |
Write
Preview
Loading…
Cancel
Save
Reference in new issue