diff --git a/.editorconfig b/.editorconfig index a99bec4..fbdc8fc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,17 +3,17 @@ root = true [*] charset = utf-8 end_of_line = lf -indent_size = 4 +indent_size = 2 indent_style = space insert_final_newline = false max_line_length = 120 -tab_width = 4 -ij_continuation_indent_size = 8 +tab_width = 2 +ij_continuation_indent_size = 4 ij_formatter_off_tag = @formatter:off ij_formatter_on_tag = @formatter:on ij_formatter_tags_enabled = true ij_smart_tabs = false -ij_visual_guides = +ij_visual_guides = ij_wrap_on_typing = false [*.css] @@ -30,7 +30,7 @@ ij_css_hex_color_upper_case = false ij_css_keep_blank_lines_in_code = 2 ij_css_keep_indents_on_empty_lines = false ij_css_keep_single_line_blocks = false -ij_css_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_css_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow ij_css_space_after_colon = true ij_css_space_before_opening_brace = true ij_css_use_double_quotes = true @@ -60,7 +60,7 @@ ij_sass_keep_indents_on_empty_lines = false ij_sass_keep_single_line_blocks = false ij_sass_line_comment_add_space = false ij_sass_line_comment_at_first_column = false -ij_sass_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_sass_properties_order = font, font-family, font-size, font-weight, font-style, font-variant, font-size-adjust, font-stretch, line-height, position, z-index, top, right, bottom, left, display, visibility, float, clear, overflow, overflow-x, overflow-y, clip, zoom, align-content, align-items, align-self, flex, flex-flow, flex-basis, flex-direction, flex-grow, flex-shrink, flex-wrap, justify-content, order, box-sizing, width, min-width, max-width, height, min-height, max-height, margin, margin-top, margin-right, margin-bottom, margin-left, padding, padding-top, padding-right, padding-bottom, padding-left, table-layout, empty-cells, caption-side, border-spacing, border-collapse, list-style, list-style-position, list-style-type, list-style-image, content, quotes, counter-reset, counter-increment, resize, cursor, user-select, nav-index, nav-up, nav-right, nav-down, nav-left, transition, transition-delay, transition-timing-function, transition-duration, transition-property, transform, transform-origin, animation, animation-name, animation-duration, animation-play-state, animation-timing-function, animation-delay, animation-iteration-count, animation-direction, text-align, text-align-last, vertical-align, white-space, text-decoration, text-emphasis, text-emphasis-color, text-emphasis-style, text-emphasis-position, text-indent, text-justify, letter-spacing, word-spacing, text-outline, text-transform, text-wrap, text-overflow, text-overflow-ellipsis, text-overflow-mode, word-wrap, word-break, tab-size, hyphens, pointer-events, opacity, color, border, border-width, border-style, border-color, border-top, border-top-width, border-top-style, border-top-color, border-right, border-right-width, border-right-style, border-right-color, border-bottom, border-bottom-width, border-bottom-style, border-bottom-color, border-left, border-left-width, border-left-style, border-left-color, border-radius, border-top-left-radius, border-top-right-radius, border-bottom-right-radius, border-bottom-left-radius, border-image, border-image-source, border-image-slice, border-image-width, border-image-outset, border-image-repeat, outline, outline-width, outline-style, outline-color, outline-offset, background, background-color, background-image, background-repeat, background-attachment, background-position, background-position-x, background-position-y, background-clip, background-origin, background-size, box-decoration-break, box-shadow, text-shadow ij_sass_space_after_colon = true ij_sass_space_before_opening_brace = true ij_sass_use_double_quotes = true @@ -114,7 +114,7 @@ ij_typescript_array_initializer_wrap = off ij_typescript_assignment_wrap = off ij_typescript_binary_operation_sign_on_next_line = false ij_typescript_binary_operation_wrap = off -ij_typescript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** +ij_typescript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/** ij_typescript_blank_lines_after_imports = 1 ij_typescript_blank_lines_around_class = 1 ij_typescript_blank_lines_around_field = 0 @@ -183,7 +183,7 @@ ij_typescript_prefer_explicit_types_function_expression_returns = false ij_typescript_prefer_explicit_types_function_returns = false ij_typescript_prefer_explicit_types_vars_fields = false ij_typescript_prefer_parameters_wrap = false -ij_typescript_property_prefix = +ij_typescript_property_prefix = ij_typescript_reformat_c_style_comments = false ij_typescript_space_after_colon = true ij_typescript_space_after_comma = true @@ -297,7 +297,7 @@ ij_javascript_array_initializer_wrap = off ij_javascript_assignment_wrap = off ij_javascript_binary_operation_sign_on_next_line = false ij_javascript_binary_operation_wrap = off -ij_javascript_blacklist_imports = rxjs/Rx,node_modules/**,**/node_modules/**,@angular/material,@angular/material/typings/** +ij_javascript_blacklist_imports = rxjs/Rx, node_modules/**, **/node_modules/**, @angular/material, @angular/material/typings/** ij_javascript_blank_lines_after_imports = 1 ij_javascript_blank_lines_around_class = 1 ij_javascript_blank_lines_around_field = 0 @@ -362,7 +362,7 @@ ij_javascript_prefer_explicit_types_function_expression_returns = false ij_javascript_prefer_explicit_types_function_returns = false ij_javascript_prefer_explicit_types_vars_fields = false ij_javascript_prefer_parameters_wrap = false -ij_javascript_property_prefix = +ij_javascript_property_prefix = ij_javascript_reformat_c_style_comments = false ij_javascript_space_after_colon = true ij_javascript_space_after_comma = true @@ -460,7 +460,7 @@ ij_go_group_stdlib_imports = true ij_go_import_sorting = gofmt ij_go_keep_indents_on_empty_lines = false ij_go_local_group_mode = project -ij_go_local_package_prefixes = +ij_go_local_package_prefixes = ij_go_move_all_imports_in_one_declaration = true ij_go_move_all_stdlib_imports_in_one_group = true ij_go_remove_redundant_import_aliases = false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f23807..911526b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: push: - branches: ["master"] + branches: [ "master" ] jobs: api-build: @@ -29,7 +29,7 @@ jobs: volumes: - minio-data:/data redis: - image: redis:7.0.2-bookworm + image: redis:7.0.12-bookworm ports: - 6379:6379 defaults: diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 5bb4cf8..1b7841a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -2,7 +2,7 @@ name: PR on: pull_request: - branches: ["*"] + branches: [ "*" ] jobs: client-build: @@ -18,7 +18,7 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - - name: Installling dependencies + - name: Install dependencies run: npm install - name: Lint @@ -66,7 +66,7 @@ jobs: volumes: - minio-data:/data redis: - image: redis:7.0.2-bookworm + image: redis:7.0.12-bookworm ports: - 6379:6379 defaults: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d6248fb..530784a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,75 +1,87 @@ -# Contributing Guide - -First of all. Thank you for considering to contribute on Jokes Bapak2 API project. I hope this project will get better and we will become more bapak2 than ever. - -This project is a monorepo, meaning the backend, frontend, and Github CI are all in one place (one repository). Before you do anything, if you're going to do some breaking change or you'll write (or remove) large numbers of LOC (line of codes), please open an issue first and let us know about it. So that our work won't bother you and you'll have a breeze on developing this. - -## Project Prerequisites && Setup - -### Front End (`./client`) - -You'll have to install: -* Node.js LTS (preferably with [fnm](https://github.com/Schniz/fnm) or [nvm](https://github.com/nvm-sh/nvm)) -* Yarn v1 - -See the [README](./client/README.md) on client for detailed project setup. - -### Back End (`./api`) - -You'll have to install: -* Go v1.16.x -* (Optional) [Fiber CLI](https://github.com/gofiber/cli) for ease of development - -See the [README](./api/README.md) on client for detailed project setup. - -### With Docker Compose - -If you're just developing the front end and too lazy installing Go and such (or the other way around), you can use `docker-compose` file specified on the main page. - -You'll have to install: -* Docker (preferably with Docker Desktop if you're on Windows or Mac) -* Docker Compose - -```bash -# Create a docker container but don't start it yet. -$ docker-compose up --no-start - -# Or if you want to create the docker container and start it right away -$ docker-compose up - -# If you want to have it running in the background -$ docker-compose up --detach - -# Start existing container -$ docker-compose start - -# Stop running container -$ docker-compose stop - -# Destroy current container -$ docker-compose down -``` - -## Before submitting PR - -### Front End (`./client`) - -Please run these: -* `yarn lint` -* `yarn format` -* `yarn build` - -If those command didn't pass, please fix the problem first. Please recheck your changes, make sure NOT to leave any secret token/keys behind. - -### Back End (`./api`) - -Please run these: -* `go fmt` -* `go build main.go` -* `go test -v -race -coverprofile=coverage.out -covermode=atomic ./...` - -If those command didn't pass, please fix the problem first. Please recheck your changes, make sure NOT to leave any secret token/keys behind. - -## One more thing.. - +# Contributing Guide + +First of all. Thank you for considering to contribute on Jokes Bapak2 API project. I hope this project will get better +and we will become more bapak2 than ever. + +This project is a monorepo, meaning the backend, frontend, and Github CI are all in one place (one repository). Before +you do anything, if you're going to do some breaking change or you'll write (or remove) large numbers of LOC (line of +codes), please open an issue first and let us know about it. So that our work won't bother you and you'll have a breeze +on developing this. + +## Project Prerequisites && Setup + +### Front End (`./client`) + +You'll have to install: + +* Node.js LTS (preferably with [fnm](https://github.com/Schniz/fnm) or [nvm](https://github.com/nvm-sh/nvm)) +* Yarn v1 + +See the [README](./client/README.md) on client for detailed project setup. + +### Back End (`./api`) + +You'll have to install: + +* Go v1.16.x +* (Optional) [Fiber CLI](https://github.com/gofiber/cli) for ease of development + +See the [README](./api/README.md) on client for detailed project setup. + +### With Docker Compose + +If you're just developing the front end and too lazy installing Go and such (or the other way around), you can +use `docker-compose` file specified on the main page. + +You'll have to install: + +* Docker (preferably with Docker Desktop if you're on Windows or Mac) +* Docker Compose + +```bash +# Create a docker container but don't start it yet. +$ docker-compose up --no-start + +# Or if you want to create the docker container and start it right away +$ docker-compose up + +# If you want to have it running in the background +$ docker-compose up --detach + +# Start existing container +$ docker-compose start + +# Stop running container +$ docker-compose stop + +# Destroy current container +$ docker-compose down +``` + +## Before submitting PR + +### Front End (`./client`) + +Please run these: + +* `yarn lint` +* `yarn format` +* `yarn build` + +If those command didn't pass, please fix the problem first. Please recheck your changes, make sure NOT to leave any +secret token/keys behind. + +### Back End (`./api`) + +Please run these: + +* `go fmt` +* `go build main.go` +* `go test -v -race -coverprofile=coverage.out -covermode=atomic ./...` + +If those command didn't pass, please fix the problem first. Please recheck your changes, make sure NOT to leave any +secret token/keys behind. + +## One more thing.. + Oh my God, thank you so much!!! Working on an open source project is interesting right?? 😆 \ No newline at end of file diff --git a/README.md b/README.md index f44ddbb..b3023e9 100644 --- a/README.md +++ b/README.md @@ -7,15 +7,19 @@
-👋 Hey there! Always a work in progress, if you'd like to contribute this while this repo is still growing, that would be so great! +👋 Hey there! Always a work in progress, if you'd like to contribute this while this repo is still growing, that would be +so great! ou can access the front facing web on [jokesbapak2.reinaldyrafli.com](http://jokesbapak2.reinaldyrafli.com/). ## Brief explanation of what is this -Jokes Bapak2 is an image API that you can use for free! I've been seeing lots and lots of Indonesian dad jokes on Twitter, Facebook and Instagram on early 2020. In a month, I made a Discord bot that provides the jokes. But I thought, why not make it as an API? +Jokes Bapak2 is an image API that you can use for free! I've been seeing lots and lots of Indonesian dad jokes on +Twitter, Facebook and Instagram on early 2020. In a month, I made a Discord bot that provides the jokes. But I thought, +why not make it as an API? -This is some kind of [icanhazdadjokes](https://icanhazdadjoke.com/) but it's Indonesian and it's not text, it's images. Dad jokes in Indonesia is somewhat a bit different than in US/UK because, I guess, here, it's a lot dumber. +This is some kind of [icanhazdadjokes](https://icanhazdadjoke.com/) but it's Indonesian and it's not text, it's images. +Dad jokes in Indonesia is somewhat a bit different than in US/UK because, I guess, here, it's a lot dumber. ## Project Directories @@ -24,29 +28,31 @@ This is some kind of [icanhazdadjokes](https://icanhazdadjoke.com/) but it's Ind You can consume this API via a website (linked in the front facing web) with a few endpoints: - * `/` - Random jokes bapak2 - * `/id/{number}` - Jokes bapak2 based on ID - * `/today` - Jokes bapak2 of the day - * `/total` - Total available jokes bapak2 +* `/` - Random jokes bapak2 +* `/id/{number}` - Jokes bapak2 based on ID +* `/today` - Jokes bapak2 of the day +* `/total` - Total available jokes bapak2 Currently I'm (still) searching for an alternative for AWS S3 that I can use for free. ## Tech stacks - * Go (for `api` / back end) - * Node.js (for `client` / front end) - * Postgres - * Redis +* Go (for `api` / back end) +* Node.js (for `client` / front end) +* Postgres +* Redis That's it. ## Development Two ways of doing this: - 1. Install all the tech stack on your local machine - 2. Using docker-compose -See [CONTRIBUTING](./CONTRIBUTING.md) or README files on each project directory for further instruction on how to run the development environment. +1. Install all the tech stack on your local machine +2. Using docker-compose + +See [CONTRIBUTING](./CONTRIBUTING.md) or README files on each project directory for further instruction on how to run +the development environment. ## Thanks to diff --git a/api/README.md b/api/README.md index bbb53fc..a3b7f65 100644 --- a/api/README.md +++ b/api/README.md @@ -1,78 +1,79 @@ -# Jokes Bapak2 API - -Still work in progress - -## Development - -```bash -# Install modules -$ go mod download -# or -$ go mod vendor - -# run the local server -$ go run main.go - -# build everything -$ go build main.go -``` - -There is a placeholder data ready for you to query it manually in `/platform/database/placeholder.sql`. Have a good time developing! - -## Used packages - -| Name | Version | Type | -| --- | --- | --- | -| [gofiber/fiber](https://github.com/gofiber/fiber) | `v2.21.0` | Framework | -| [jackc/pgx](https://github.com/jackc/pgx) | `v4.13.0` | Database | -| [go-redis/redis](https://github.com/go-redis/redis) | `v8.11.4` | Cache | -| [allegro/bigcache](https://github.com/allegro/bigcache) | `v3.0.1` | Cache | -| [joho/godotenv](https://github.com/joho/godotenv) | `v1.4.0` | Config | -| [getsentry/sentry-go](https://github.com/getsentry/sentry-go) | `v0.11.0` | Logging | -| [aldy505/phc-crypto](https://github.com/aldy505/phc-crypto) | `v1.1.0` | Utils | -| [Masterminds/squirrel](https://github.com/Masterminds/squirrel ) | `v1.5.1` | Utils | -| [aldy505/bob](https://github.com/aldy505/bob) | `v0.0.4` | Utils | -| [gojek/heimdall](https://github.com/gojek/heimdall) | `v7.0.2` | Utils | -| [georgysavva/scany](https://github.com/georgysavva/scany) | `v0.2.9` | Utils | -| [pquerna/ffjson](https://github.com/pquerna/ffjson) | `v0.0.0-20190930134022-aa0246cd15f7` | Utils | - -## Directory structure - -``` -. -├── core - Pure business logic -│ ├── administrator -│ ├── joke -│ ├── schema -│ ├── submit -│ └── validator -├── Dockerfile - Docker image for API -├── documentation.json - Swagger documentation -├── documentation.yaml - Swagger documentation -├── favicon.png -├── go.mod - Module declaration -├── go.sum - Checksum for modules -├── handler - Route handlers -│ ├── health -│ ├── joke -│ └── submit -├── main.go - Application entry point -├── middleware - Route middlewares -├── platform - Third party packages -│ └── database -├── README.md - You are here -├── routes - Route definitions -└── utils - Utility functions -``` - -## `.env` configuration - -```ini -ENV=development -PORT=5000 - -DATABASE_URL=postgres://postgres:password@localhost:5432/jokesbapak2 -REDIS_URL=redis://@localhost:6379 - -SENTRY_DSN= +# Jokes Bapak2 API + +Still work in progress + +## Development + +```bash +# Install modules +$ go mod download +# or +$ go mod vendor + +# run the local server +$ go run main.go + +# build everything +$ go build main.go +``` + +There is a placeholder data ready for you to query it manually in `/platform/database/placeholder.sql`. Have a good time +developing! + +## Used packages + +| Name | Version | Type | +|------------------------------------------------------------------|--------------------------------------|-----------| +| [gofiber/fiber](https://github.com/gofiber/fiber) | `v2.21.0` | Framework | +| [jackc/pgx](https://github.com/jackc/pgx) | `v4.13.0` | Database | +| [go-redis/redis](https://github.com/go-redis/redis) | `v8.11.4` | Cache | +| [allegro/bigcache](https://github.com/allegro/bigcache) | `v3.0.1` | Cache | +| [joho/godotenv](https://github.com/joho/godotenv) | `v1.4.0` | Config | +| [getsentry/sentry-go](https://github.com/getsentry/sentry-go) | `v0.11.0` | Logging | +| [aldy505/phc-crypto](https://github.com/aldy505/phc-crypto) | `v1.1.0` | Utils | +| [Masterminds/squirrel](https://github.com/Masterminds/squirrel ) | `v1.5.1` | Utils | +| [aldy505/bob](https://github.com/aldy505/bob) | `v0.0.4` | Utils | +| [gojek/heimdall](https://github.com/gojek/heimdall) | `v7.0.2` | Utils | +| [georgysavva/scany](https://github.com/georgysavva/scany) | `v0.2.9` | Utils | +| [pquerna/ffjson](https://github.com/pquerna/ffjson) | `v0.0.0-20190930134022-aa0246cd15f7` | Utils | + +## Directory structure + +``` +. +├── core - Pure business logic +│ ├── administrator +│ ├── joke +│ ├── schema +│ ├── submit +│ └── validator +├── Dockerfile - Docker image for API +├── documentation.json - Swagger documentation +├── documentation.yaml - Swagger documentation +├── favicon.png +├── go.mod - Module declaration +├── go.sum - Checksum for modules +├── handler - Route handlers +│ ├── health +│ ├── joke +│ └── submit +├── main.go - Application entry point +├── middleware - Route middlewares +├── platform - Third party packages +│ └── database +├── README.md - You are here +├── routes - Route definitions +└── utils - Utility functions +``` + +## `.env` configuration + +```ini +ENV=development +PORT=5000 + +DATABASE_URL=postgres://postgres:password@localhost:5432/jokesbapak2 +REDIS_URL=redis://@localhost:6379 + +SENTRY_DSN= ``` \ No newline at end of file diff --git a/api/core/joke/getter_test.go b/api/core/joke/getter_test.go index 792c75d..01283d4 100644 --- a/api/core/joke/getter_test.go +++ b/api/core/joke/getter_test.go @@ -2,9 +2,10 @@ package joke_test import ( "context" - "jokes-bapak2-api/core/joke" "testing" "time" + + "jokes-bapak2-api/core/joke" ) func TestGetRandomJoke(t *testing.T) { diff --git a/api/core/joke/list_test.go b/api/core/joke/list_test.go index ae8a083..5d3a9ab 100644 --- a/api/core/joke/list_test.go +++ b/api/core/joke/list_test.go @@ -2,9 +2,10 @@ package joke_test import ( "context" - "jokes-bapak2-api/core/joke" "testing" "time" + + "jokes-bapak2-api/core/joke" ) func TestListJokeFromBucket(t *testing.T) { diff --git a/api/core/joke/today_test.go b/api/core/joke/today_test.go index 20ecafc..025498f 100644 --- a/api/core/joke/today_test.go +++ b/api/core/joke/today_test.go @@ -2,9 +2,10 @@ package joke_test import ( "context" - "jokes-bapak2-api/core/joke" "testing" "time" + + "jokes-bapak2-api/core/joke" ) func TestGetTodaysJoke(t *testing.T) { diff --git a/api/core/joke/total_test.go b/api/core/joke/total_test.go index cd62d30..3862746 100644 --- a/api/core/joke/total_test.go +++ b/api/core/joke/total_test.go @@ -2,9 +2,10 @@ package joke_test import ( "context" - "jokes-bapak2-api/core/joke" "testing" "time" + + "jokes-bapak2-api/core/joke" ) func TestGetTotalJoke(t *testing.T) { diff --git a/api/documentation.yaml b/api/documentation.yaml index f577d7e..eccdb2c 100644 --- a/api/documentation.yaml +++ b/api/documentation.yaml @@ -1,321 +1,321 @@ -openapi: 3.0.0 -info: - title: Jokesbapak2 Image API - description: > - Jokes Bapak2 is an image API that you can use for free! I've been seeing lots and lots of Indonesian dad jokes on Twitter, - Facebook and Instagram on early 2020. In a month, I made a Discord bot that provides the jokes. - But I thought, why not make it as an API? - version: 0.0.1 - contact: - name: Reinaldy Rafli - url: https://github.com/aldy505 - email: aldy505@tutanota.com - license: - name: GNU General Public License v3.0 - url: https://github.com/aldy505/jokes-bapak2/blob/master/LICENSE -servers: - - url: "https://jokesbapak2.reinaldyrafli.com/api/v1" - description: Production - - url: "https://jokesbapak2.reinaldyrafli.com/api" - description: Production - - url: "http://localhost:5000" - description: Development -paths: - /: - get: - tags: - - Jokes - summary: Get random Jokes Bapak2 image - description: Returns a different image (PNG, JPG, or GIF) for every call. - responses: - 200: - description: Image data - content: - "image/gif": {} - "image/png": {} - "image/jpeg": {} - put: - summary: Add a new joke into database - description: asd - tags: - - Jokes - requestBody: - description: asds - required: true - content: - application/json: - schema: - allOf: - - $ref: "#/components/schemas/request.auth" - - $ref: "#/components/schemas/request.joke" - responses: - 201: - description: Image has been added - content: - application/json: - schema: - $ref: "#/components/schemas/request.joke" - example: - link: https://link.to/image.jpg - 400: - description: Bad request - content: - application/json: - schema: - $ref: "#/components/schemas/response.error" - example: - error: URL provided is not a valid image - 403: - description: Must be authenticated to submit a joke - content: - application/json: - schema: - $ref: "#/components/schemas/response.error" - /id/{id}: - parameters: - - in: path - name: id - schema: - type: number - required: true - description: A number that represents image's ID - get: - summary: Get random Jokes Bapak2 image by ID - description: Returns consistent image for every call. - tags: - - Jokes - responses: - 200: - description: Image data - content: - "image/jpeg": {} - "image/png": {} - "image/gif": {} - 404: - description: Provided image ID was not found - content: - text/plain: - schema: - type: string - example: Requested ID was not found. - patch: - summary: Update a Joke with certain image ID - description: Returns consistent image for every call. - tags: - - Jokes - responses: - 200: - description: Sucessfully updated an image item - content: - application/json: - schema: - allOf: - - $ref: "#/components/schemas/response.confirmation" - - $ref: "#/components/schemas/request.joke" - 400: - description: Link provided is not a valid image - content: - application/json: - schema: - $ref: "#/components/schemas/response.error" - 403: - description: Must be authenticated to submit a joke - content: - application/json: - schema: - $ref: "#/components/schemas/response.error" - 406: - description: If the Joke ID does not exists - content: - application/json: - schema: - $ref: "#/components/schemas/response.error" - delete: - summary: Delete a Joke with certain image ID - description: hi - tags: - - Jokes - responses: - 200: - description: Sucessfully deleted an image item - content: - application/json: - schema: - $ref: "#/components/schemas/response.confirmation" - 403: - description: Must be authenticated to submit a joke - content: - application/json: - schema: - $ref: "#/components/schemas/response.error" - 406: - description: If the Joke ID does not exists - content: - application/json: - schema: - $ref: "#/components/schemas/response.error" - /today: - get: - summary: Get the joke of the day - description: A joke a day makes more of a dad out of you. - tags: - - Jokes - responses: - 200: - description: Image data - content: - "image/jpeg": {} - "image/png": {} - "image/gif": {} - /total: - get: - summary: Get total amount of jokes in database - tags: - - Jokes - responses: - 200: - description: Total jokes - content: - application/json: - schema: - $ref: "#/components/schemas/response.confirmation" - example: - message: "154" - /submit: - get: - summary: Get submitted Jokes - tags: - - Submission - parameters: - - name: author - in: query - required: false - description: Author to be queried - schema: - type: string - - name: approved - in: query - required: false - description: Whether query just approved jokes or not - schema: - type: boolean - - name: limit - in: query - required: false - schema: - type: number - - name: page - in: query - required: false - schema: - type: number - responses: - 200: - description: asd - content: - application/json: - schema: - type: object - properties: - count: - type: number - jokes: - type: array - items: - $ref: "#/components/schemas/response.submission" - post: - summary: Submit a joke - description: > - Must be in multipart/form-data format. - Author must be in the format of "Name <email>". - tags: - - Submission - requestBody: - content: - multipart/form-data: - schema: - properties: - link: - description: Image link - type: string - image: - description: Image data - type: string - author: - description: Person who submitted this - type: string - required: - - author - - image - - link - responses: - 201: - description: Joke successfully submitted - content: - application/json: - schema: - allOf: - - $ref: "#/components/schemas/response.confirmation" - - type: object - properties: - data: - $ref: "#/components/schemas/response.submission" - 400: - description: Invalid data was sent - content: - application/json: - schema: - $ref: "#/components/schemas/response.error" - /health: - get: - summary: Health check - description: Ping the databases to make sure everything's alright - tags: - - Health - responses: - 200: - description: Everything is okay - 403: - description: Something is not okay - content: - application/json: - schema: - $ref: "#/components/schemas/response.error" - -components: - schemas: - request.auth: - type: object - properties: - key: - type: string - token: - type: string - request.joke: - type: object - properties: - link: - type: string - response.confirmation: - type: object - properties: - message: - type: string - response.error: - type: object - properties: - error: - type: string - response.submission: - type: object - properties: - id: - type: number - link: - type: string - created_at: - type: string - author: - type: string - status: - type: number +openapi: 3.0.0 +info: + title: Jokesbapak2 Image API + description: > + Jokes Bapak2 is an image API that you can use for free! I've been seeing lots and lots of Indonesian dad jokes on Twitter, + Facebook and Instagram on early 2020. In a month, I made a Discord bot that provides the jokes. + But I thought, why not make it as an API? + version: 0.0.1 + contact: + name: Reinaldy Rafli + url: https://github.com/aldy505 + email: aldy505@tutanota.com + license: + name: GNU General Public License v3.0 + url: https://github.com/aldy505/jokes-bapak2/blob/master/LICENSE +servers: + - url: "https://jokesbapak2.reinaldyrafli.com/api/v1" + description: Production + - url: "https://jokesbapak2.reinaldyrafli.com/api" + description: Production + - url: "http://localhost:5000" + description: Development +paths: + /: + get: + tags: + - Jokes + summary: Get random Jokes Bapak2 image + description: Returns a different image (PNG, JPG, or GIF) for every call. + responses: + 200: + description: Image data + content: + "image/gif": { } + "image/png": { } + "image/jpeg": { } + put: + summary: Add a new joke into database + description: asd + tags: + - Jokes + requestBody: + description: asds + required: true + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/request.auth" + - $ref: "#/components/schemas/request.joke" + responses: + 201: + description: Image has been added + content: + application/json: + schema: + $ref: "#/components/schemas/request.joke" + example: + link: https://link.to/image.jpg + 400: + description: Bad request + content: + application/json: + schema: + $ref: "#/components/schemas/response.error" + example: + error: URL provided is not a valid image + 403: + description: Must be authenticated to submit a joke + content: + application/json: + schema: + $ref: "#/components/schemas/response.error" + /id/{id}: + parameters: + - in: path + name: id + schema: + type: number + required: true + description: A number that represents image's ID + get: + summary: Get random Jokes Bapak2 image by ID + description: Returns consistent image for every call. + tags: + - Jokes + responses: + 200: + description: Image data + content: + "image/jpeg": { } + "image/png": { } + "image/gif": { } + 404: + description: Provided image ID was not found + content: + text/plain: + schema: + type: string + example: Requested ID was not found. + patch: + summary: Update a Joke with certain image ID + description: Returns consistent image for every call. + tags: + - Jokes + responses: + 200: + description: Sucessfully updated an image item + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/response.confirmation" + - $ref: "#/components/schemas/request.joke" + 400: + description: Link provided is not a valid image + content: + application/json: + schema: + $ref: "#/components/schemas/response.error" + 403: + description: Must be authenticated to submit a joke + content: + application/json: + schema: + $ref: "#/components/schemas/response.error" + 406: + description: If the Joke ID does not exists + content: + application/json: + schema: + $ref: "#/components/schemas/response.error" + delete: + summary: Delete a Joke with certain image ID + description: hi + tags: + - Jokes + responses: + 200: + description: Sucessfully deleted an image item + content: + application/json: + schema: + $ref: "#/components/schemas/response.confirmation" + 403: + description: Must be authenticated to submit a joke + content: + application/json: + schema: + $ref: "#/components/schemas/response.error" + 406: + description: If the Joke ID does not exists + content: + application/json: + schema: + $ref: "#/components/schemas/response.error" + /today: + get: + summary: Get the joke of the day + description: A joke a day makes more of a dad out of you. + tags: + - Jokes + responses: + 200: + description: Image data + content: + "image/jpeg": { } + "image/png": { } + "image/gif": { } + /total: + get: + summary: Get total amount of jokes in database + tags: + - Jokes + responses: + 200: + description: Total jokes + content: + application/json: + schema: + $ref: "#/components/schemas/response.confirmation" + example: + message: "154" + /submit: + get: + summary: Get submitted Jokes + tags: + - Submission + parameters: + - name: author + in: query + required: false + description: Author to be queried + schema: + type: string + - name: approved + in: query + required: false + description: Whether query just approved jokes or not + schema: + type: boolean + - name: limit + in: query + required: false + schema: + type: number + - name: page + in: query + required: false + schema: + type: number + responses: + 200: + description: asd + content: + application/json: + schema: + type: object + properties: + count: + type: number + jokes: + type: array + items: + $ref: "#/components/schemas/response.submission" + post: + summary: Submit a joke + description: > + Must be in multipart/form-data format. + Author must be in the format of "Name <email>". + tags: + - Submission + requestBody: + content: + multipart/form-data: + schema: + properties: + link: + description: Image link + type: string + image: + description: Image data + type: string + author: + description: Person who submitted this + type: string + required: + - author + - image + - link + responses: + 201: + description: Joke successfully submitted + content: + application/json: + schema: + allOf: + - $ref: "#/components/schemas/response.confirmation" + - type: object + properties: + data: + $ref: "#/components/schemas/response.submission" + 400: + description: Invalid data was sent + content: + application/json: + schema: + $ref: "#/components/schemas/response.error" + /health: + get: + summary: Health check + description: Ping the databases to make sure everything's alright + tags: + - Health + responses: + 200: + description: Everything is okay + 403: + description: Something is not okay + content: + application/json: + schema: + $ref: "#/components/schemas/response.error" + +components: + schemas: + request.auth: + type: object + properties: + key: + type: string + token: + type: string + request.joke: + type: object + properties: + link: + type: string + response.confirmation: + type: object + properties: + message: + type: string + response.error: + type: object + properties: + error: + type: string + response.submission: + type: object + properties: + id: + type: number + link: + type: string + created_at: + type: string + author: + type: string + status: + type: number diff --git a/api/handler/joke/get.go b/api/handler/joke/get.go index fb9edea..29ecf7b 100644 --- a/api/handler/joke/get.go +++ b/api/handler/joke/get.go @@ -1,10 +1,11 @@ package joke import ( - core "jokes-bapak2-api/core/joke" "net/http" "strconv" + core "jokes-bapak2-api/core/joke" + "github.com/go-chi/chi/v5" ) diff --git a/api/handler/joke/total.go b/api/handler/joke/total.go index bc250f5..c6e41ca 100644 --- a/api/handler/joke/total.go +++ b/api/handler/joke/total.go @@ -1,9 +1,10 @@ package joke import ( - core "jokes-bapak2-api/core/joke" "net/http" "strconv" + + core "jokes-bapak2-api/core/joke" ) // TotalJokes provides a HTTP handler for acquiring total jokes diff --git a/api/main.go b/api/main.go index 44167b1..8c7cd19 100644 --- a/api/main.go +++ b/api/main.go @@ -1,182 +1,181 @@ -package main - -import ( - "errors" - "log" - "net" - "net/http" - "os" - "os/signal" - - "context" - "jokes-bapak2-api/core/joke" - "jokes-bapak2-api/routes" - - "github.com/go-redis/redis/v8" - "github.com/minio/minio-go/v7" - "github.com/minio/minio-go/v7/pkg/credentials" - - "time" - - "github.com/allegro/bigcache/v3" - "github.com/getsentry/sentry-go" - "github.com/go-chi/chi/v5" - "github.com/rs/cors" -) - -func main() { - redisURL, ok := os.LookupEnv("REDIS_URL") - if !ok { - redisURL = "redis://@localhost:6379" - } - - minioHost, ok := os.LookupEnv("MINIO_HOST") - if !ok { - minioHost = "localhost:9000" - } - - minioRegion, ok := os.LookupEnv("MINIO_REGION") - if !ok { - minioRegion = "" - } - - minioSecure, ok := os.LookupEnv("MINIO_SECURE") - if !ok { - minioSecure = "false" - } - - minioID, ok := os.LookupEnv("MINIO_ACCESS_ID") - if !ok { - minioID = "minio" - } - - minioSecret, ok := os.LookupEnv("MINIO_SECRET_KEY") - if !ok { - minioSecret = "password" - } - - minioToken, ok := os.LookupEnv("MINIO_TOKEN") - if !ok { - minioToken = "" - } - - sentryDsn, ok := os.LookupEnv("SENTRY_DSN") - if !ok { - sentryDsn = "" - } - - port, ok := os.LookupEnv("PORT") - if !ok { - port = "5000" - } - - hostname, ok := os.LookupEnv("HOSTNAME") - if !ok { - hostname = "127.0.0.1" - } - - environment, ok := os.LookupEnv("ENVIRONMENT") - if !ok { - environment = "development" - } - - // Setup In Memory - memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(10 * time.Minute)) - if err != nil { - log.Panicln(err) - } - defer memory.Close() - - // Setup MinIO - minioClient, err := minio.New(minioHost, &minio.Options{ - Creds: credentials.NewStaticV4(minioID, minioSecret, minioToken), - Region: minioRegion, - Secure: minioSecure == "true", - }) - if err != nil { - log.Fatalf("setting up minio client: %s", err.Error()) - return - } - - parsedRedisURL, err := redis.ParseURL(redisURL) - if err != nil { - log.Fatalf("parsing redis url: %s", err.Error()) - return - } - - redisClient := redis.NewClient(parsedRedisURL) - defer func() { - err := redisClient.Close() - if err != nil { - log.Printf("closing redis client: %s", err.Error()) - } - }() - - // Setup Sentry - err = sentry.Init(sentry.ClientOptions{ - Dsn: sentryDsn, - Environment: environment, - AttachStacktrace: true, - // Enable printing of SDK debug messages. - // Useful when getting started or trying to figure something out. - Debug: environment != "production", - }) - if err != nil { - log.Fatalf("setting up sentry: %s", err.Error()) - return - } - defer sentry.Flush(2 * time.Second) - - setupCtx, setupCancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute*4)) - defer setupCancel() - - _, _, err = joke.GetTodaysJoke(setupCtx, minioClient, redisClient, memory) - if err != nil { - log.Fatalf("getting initial joke data: %s", err.Error()) - return - } - - healthRouter := routes.Health(minioClient, redisClient) - jokeRouter := routes.Joke(minioClient, redisClient, memory) - - router := chi.NewRouter() - - router.Use(cors.New(cors.Options{ - AllowedMethods: []string{http.MethodGet}, - AllowCredentials: false, - MaxAge: int(60 * 60 * 24 * 365), - Debug: false, - }).Handler) - - router.Mount("/health", healthRouter) - router.Mount("/", jokeRouter) - - server := &http.Server{ - Handler: router, - Addr: net.JoinHostPort(hostname, port), - ReadTimeout: time.Minute, - WriteTimeout: time.Minute, - IdleTimeout: time.Second * 30, - ReadHeaderTimeout: time.Minute, - } - - exitSignal := make(chan os.Signal, 1) - signal.Notify(exitSignal, os.Interrupt) - - go func() { - err := server.ListenAndServe() - if err != nil && !errors.Is(err, http.ErrServerClosed) { - log.Fatalf("listening http server: %v", err) - } - }() - - <-exitSignal - - shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), time.Second*30) - defer shutdownCancel() - - err = server.Shutdown(shutdownCtx) - if err != nil { - log.Printf("shutting down http server: %v", err) - } -} +package main + +import ( + "context" + "errors" + "log" + "net" + "net/http" + "os" + "os/signal" + "time" + + "jokes-bapak2-api/core/joke" + "jokes-bapak2-api/routes" + + "github.com/go-redis/redis/v8" + "github.com/minio/minio-go/v7" + "github.com/minio/minio-go/v7/pkg/credentials" + + "github.com/allegro/bigcache/v3" + "github.com/getsentry/sentry-go" + "github.com/go-chi/chi/v5" + "github.com/rs/cors" +) + +func main() { + redisURL, ok := os.LookupEnv("REDIS_URL") + if !ok { + redisURL = "redis://@localhost:6379" + } + + minioHost, ok := os.LookupEnv("MINIO_HOST") + if !ok { + minioHost = "localhost:9000" + } + + minioRegion, ok := os.LookupEnv("MINIO_REGION") + if !ok { + minioRegion = "" + } + + minioSecure, ok := os.LookupEnv("MINIO_SECURE") + if !ok { + minioSecure = "false" + } + + minioID, ok := os.LookupEnv("MINIO_ACCESS_ID") + if !ok { + minioID = "minio" + } + + minioSecret, ok := os.LookupEnv("MINIO_SECRET_KEY") + if !ok { + minioSecret = "password" + } + + minioToken, ok := os.LookupEnv("MINIO_TOKEN") + if !ok { + minioToken = "" + } + + sentryDsn, ok := os.LookupEnv("SENTRY_DSN") + if !ok { + sentryDsn = "" + } + + port, ok := os.LookupEnv("PORT") + if !ok { + port = "5000" + } + + hostname, ok := os.LookupEnv("HOSTNAME") + if !ok { + hostname = "127.0.0.1" + } + + environment, ok := os.LookupEnv("ENVIRONMENT") + if !ok { + environment = "development" + } + + // Setup In Memory + memory, err := bigcache.NewBigCache(bigcache.DefaultConfig(10 * time.Minute)) + if err != nil { + log.Panicln(err) + } + defer memory.Close() + + // Setup MinIO + minioClient, err := minio.New(minioHost, &minio.Options{ + Creds: credentials.NewStaticV4(minioID, minioSecret, minioToken), + Region: minioRegion, + Secure: minioSecure == "true", + }) + if err != nil { + log.Fatalf("setting up minio client: %s", err.Error()) + return + } + + parsedRedisURL, err := redis.ParseURL(redisURL) + if err != nil { + log.Fatalf("parsing redis url: %s", err.Error()) + return + } + + redisClient := redis.NewClient(parsedRedisURL) + defer func() { + err := redisClient.Close() + if err != nil { + log.Printf("closing redis client: %s", err.Error()) + } + }() + + // Setup Sentry + err = sentry.Init(sentry.ClientOptions{ + Dsn: sentryDsn, + Environment: environment, + AttachStacktrace: true, + // Enable printing of SDK debug messages. + // Useful when getting started or trying to figure something out. + Debug: environment != "production", + }) + if err != nil { + log.Fatalf("setting up sentry: %s", err.Error()) + return + } + defer sentry.Flush(2 * time.Second) + + setupCtx, setupCancel := context.WithDeadline(context.Background(), time.Now().Add(time.Minute*4)) + defer setupCancel() + + _, _, err = joke.GetTodaysJoke(setupCtx, minioClient, redisClient, memory) + if err != nil { + log.Fatalf("getting initial joke data: %s", err.Error()) + return + } + + healthRouter := routes.Health(minioClient, redisClient) + jokeRouter := routes.Joke(minioClient, redisClient, memory) + + router := chi.NewRouter() + + router.Use(cors.New(cors.Options{ + AllowedMethods: []string{http.MethodGet}, + AllowCredentials: false, + MaxAge: int(60 * 60 * 24 * 365), + Debug: false, + }).Handler) + + router.Mount("/health", healthRouter) + router.Mount("/", jokeRouter) + + server := &http.Server{ + Handler: router, + Addr: net.JoinHostPort(hostname, port), + ReadTimeout: time.Minute, + WriteTimeout: time.Minute, + IdleTimeout: time.Second * 30, + ReadHeaderTimeout: time.Minute, + } + + exitSignal := make(chan os.Signal, 1) + signal.Notify(exitSignal, os.Interrupt) + + go func() { + err := server.ListenAndServe() + if err != nil && !errors.Is(err, http.ErrServerClosed) { + log.Fatalf("listening http server: %v", err) + } + }() + + <-exitSignal + + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), time.Second*30) + defer shutdownCancel() + + err = server.Shutdown(shutdownCtx) + if err != nil { + log.Printf("shutting down http server: %v", err) + } +} diff --git a/api/utils/array_test.go b/api/utils/array_test.go index 74c36af..51b1897 100644 --- a/api/utils/array_test.go +++ b/api/utils/array_test.go @@ -1,8 +1,9 @@ package utils_test import ( - "jokes-bapak2-api/utils" "testing" + + "jokes-bapak2-api/utils" ) func TestIsIn_True(t *testing.T) { diff --git a/api/utils/date_test.go b/api/utils/date_test.go index de59927..86306c2 100644 --- a/api/utils/date_test.go +++ b/api/utils/date_test.go @@ -1,9 +1,10 @@ package utils_test import ( - "jokes-bapak2-api/utils" "testing" "time" + + "jokes-bapak2-api/utils" ) func TestIsToday_Today(t *testing.T) { diff --git a/api/utils/parse_test.go b/api/utils/parse_test.go index 4388182..99191fc 100644 --- a/api/utils/parse_test.go +++ b/api/utils/parse_test.go @@ -1,9 +1,10 @@ package utils_test import ( - "jokes-bapak2-api/utils" "strings" "testing" + + "jokes-bapak2-api/utils" ) func TestParseToJSONBody(t *testing.T) { diff --git a/api/utils/random_test.go b/api/utils/random_test.go index c891510..54fedd8 100644 --- a/api/utils/random_test.go +++ b/api/utils/random_test.go @@ -1,8 +1,9 @@ package utils_test import ( - "jokes-bapak2-api/utils" "testing" + + "jokes-bapak2-api/utils" ) func TestRandomString_Valid(t *testing.T) { diff --git a/api/utils/request_test.go b/api/utils/request_test.go index 95750bb..97a2af5 100644 --- a/api/utils/request_test.go +++ b/api/utils/request_test.go @@ -1,9 +1,10 @@ package utils_test import ( - "jokes-bapak2-api/utils" "net/http" "testing" + + "jokes-bapak2-api/utils" ) func TestRequest_Get(t *testing.T) { diff --git a/benchmark/README.md b/benchmark/README.md index 4b43349..0ad0856 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -14,6 +14,6 @@ $ yarn start ## Used package -| Name | Version | Type | -| --- | --- | --- | +| Name | Version | Type | +|---------------------------------------------------------------|----------|-------------------| | [mcollina/autocannon](https://github.com/mcollina/autocannon) | `v7.4.0` | Benchmarking Tool | \ No newline at end of file