├── chatter ├── .gitignore ├── model │ ├── client.go │ ├── team_signup.go │ ├── session.go │ ├── user.go │ ├── team.go │ └── utils.go ├── app │ ├── team.go │ ├── options.go │ ├── app.go │ ├── authentication.go │ ├── session.go │ ├── server.go │ ├── user.go │ └── login.go ├── go.mod ├── web │ ├── context.go │ └── handlers.go ├── cmd │ └── chatter │ │ ├── main.go │ │ └── commands │ │ ├── root.go │ │ └── server.go ├── store │ ├── errors.go │ ├── inmemory │ │ ├── userstore.go │ │ └── sessionstore.go │ └── store.go └── api │ ├── handlers.go │ ├── team.go │ ├── api.go │ └── user.go ├── bookmyshow ├── settings.gradle ├── gradle.properties ├── application │ └── src │ │ ├── main │ │ └── java │ │ │ └── org │ │ │ └── npathai │ │ │ ├── cinemahall │ │ │ ├── Address.java │ │ │ ├── Rating.java │ │ │ ├── seat │ │ │ │ ├── SeatType.java │ │ │ │ ├── SeatNo.java │ │ │ │ ├── SeatId.java │ │ │ │ └── Seat.java │ │ │ ├── CinemaHallDao.java │ │ │ ├── CinemaHallId.java │ │ │ ├── auditorium │ │ │ │ ├── AuditoriumId.java │ │ │ │ └── Auditorium.java │ │ │ ├── show │ │ │ │ ├── ShowDao.java │ │ │ │ ├── ShowingSeatDao.java │ │ │ │ ├── ShowingSeat.java │ │ │ │ └── Show.java │ │ │ └── CinemaHall.java │ │ │ ├── movie │ │ │ ├── MovieDao.java │ │ │ ├── Genre.java │ │ │ ├── MovieId.java │ │ │ ├── MovieRepository.java │ │ │ └── Movie.java │ │ │ ├── usecases │ │ │ ├── BookingStatus.java │ │ │ ├── GetCities.java │ │ │ ├── Booking.java │ │ │ ├── GetMoviesByCityId.java │ │ │ ├── BookSeat.java │ │ │ ├── Shows.java │ │ │ └── GetShowsByMovie.java │ │ │ ├── Application.java │ │ │ ├── city │ │ │ ├── CityId.java │ │ │ ├── CityDao.java │ │ │ └── City.java │ │ │ └── common │ │ │ └── ValueObject.java │ │ └── test │ │ └── java │ │ └── org │ │ └── npathai │ │ ├── usecases │ │ ├── GetCitiesTest.java │ │ └── GetMoviesByCityIdTest.java │ │ ├── cinemahall │ │ ├── show │ │ │ └── ShowBuilder.java │ │ └── CinemaHallBuilder.java │ │ └── movie │ │ └── MovieBuilder.java ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── .gitignore └── gradlew.bat ├── discourse ├── settings.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── .gitignore ├── application │ └── src │ │ ├── main │ │ ├── java │ │ │ └── org │ │ │ │ └── npathai │ │ │ │ └── discourse │ │ │ │ └── application │ │ │ │ ├── domain │ │ │ │ ├── users │ │ │ │ │ ├── UsernameAlreadyExistsException.java │ │ │ │ │ ├── UserRepository.java │ │ │ │ │ ├── MySqlUserRepository.java │ │ │ │ │ ├── UserService.java │ │ │ │ │ ├── RegistrationData.java │ │ │ │ │ ├── InMemoryUserRepository.java │ │ │ │ │ └── User.java │ │ │ │ └── topics │ │ │ │ │ ├── TopicService.java │ │ │ │ │ └── Topic.java │ │ │ │ ├── common │ │ │ │ └── IdGenerator.java │ │ │ │ ├── Application.java │ │ │ │ ├── controllers │ │ │ │ ├── topics │ │ │ │ │ └── TopicController.java │ │ │ │ └── users │ │ │ │ │ └── UserController.java │ │ │ │ └── DomainBeanFactory.java │ │ └── resources │ │ │ └── log4j2.xml │ │ └── test │ │ └── java │ │ └── org │ │ └── npathai │ │ └── discourse │ │ └── application │ │ ├── controller │ │ ├── topics │ │ │ ├── TopicControllerClient.java │ │ │ └── TopicsControllerTest.java │ │ └── users │ │ │ └── UserControllerClient.java │ │ └── domain │ │ └── users │ │ ├── UserRepositoryStub.java │ │ ├── InMemoryUserRepositoryTest.java │ │ └── UserServiceTest.java └── gradlew.bat ├── tiny-url-generator ├── gradle.properties ├── .travis.yml ├── id-gen-service │ ├── build.gradle │ ├── src │ │ ├── main │ │ │ ├── java │ │ │ │ └── org │ │ │ │ │ └── npathai │ │ │ │ │ ├── domain │ │ │ │ │ ├── IdExhaustedException.java │ │ │ │ │ ├── Batch.java │ │ │ │ │ └── BatchedIdGenerator.java │ │ │ │ │ ├── config │ │ │ │ │ └── ZookeeperConfiguration.java │ │ │ │ │ ├── Application.java │ │ │ │ │ ├── metrics │ │ │ │ │ └── ServiceTags.java │ │ │ │ │ ├── DomainBeanFactory.java │ │ │ │ │ └── api │ │ │ │ │ └── IdGeneratorAPI.java │ │ │ └── resources │ │ │ │ ├── application.yml │ │ │ │ └── log4j2.xml │ │ └── test │ │ │ ├── resources │ │ │ └── application-test.yml │ │ │ └── java │ │ │ └── org │ │ │ └── npathai │ │ │ ├── api │ │ │ ├── IdGeneratorClient.java │ │ │ ├── DirectExecutorJobService.java │ │ │ └── IdGeneratorAPITest.java │ │ │ └── domain │ │ │ └── BatchedIdGeneratorTest.java │ └── Dockerfile ├── app │ ├── public │ │ ├── robots.txt │ │ ├── favicon.ico │ │ ├── manifest.json │ │ └── index.html │ ├── src │ │ ├── reducers │ │ │ ├── index.js │ │ │ ├── error.js │ │ │ ├── redirection.js │ │ │ └── auth.js │ │ ├── store │ │ │ └── store.js │ │ ├── components │ │ │ ├── login │ │ │ │ └── SignIn.css │ │ │ ├── navbar │ │ │ │ └── NavBar.js │ │ │ ├── home │ │ │ │ └── Home.js │ │ │ └── redirection-history │ │ │ │ ├── Redirection.js │ │ │ │ └── RedirectionHistory.js │ │ ├── constants.js │ │ ├── index.js │ │ └── index.css │ ├── .gitignore │ └── package.json ├── settings.gradle ├── .gitignore ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── analytics-service │ ├── build.gradle │ ├── src │ │ ├── test │ │ │ └── resources │ │ │ │ ├── application-test.yml │ │ │ │ └── log4j2.xml │ │ └── main │ │ │ ├── java │ │ │ └── org │ │ │ │ └── npathai │ │ │ │ ├── dao │ │ │ │ ├── DataAccessException.java │ │ │ │ ├── AnalyticsDao.java │ │ │ │ └── InMemoryAnalyticsDao.java │ │ │ │ ├── Application.java │ │ │ │ ├── DomainBeanFactory.java │ │ │ │ ├── model │ │ │ │ ├── AnalyticsInfo.java │ │ │ │ └── UserInfo.java │ │ │ │ ├── domain │ │ │ │ └── AnalyticsService.java │ │ │ │ └── api │ │ │ │ └── AnalyticsAPI.java │ │ │ └── resources │ │ │ ├── log4j2.xml │ │ │ └── application.yml │ └── Dockerfile ├── common-goodies │ ├── src │ │ ├── main │ │ │ ├── java │ │ │ │ └── org │ │ │ │ │ └── npathai │ │ │ │ │ ├── util │ │ │ │ │ ├── Stoppable.java │ │ │ │ │ ├── ThrowingConsumer.java │ │ │ │ │ ├── thread │ │ │ │ │ │ ├── ScheduledJobService.java │ │ │ │ │ │ ├── ThrowingRunnable.java │ │ │ │ │ │ ├── SwallowingRunnable.java │ │ │ │ │ │ └── DefaultScheduledJobService.java │ │ │ │ │ ├── NullSafe.java │ │ │ │ │ ├── time │ │ │ │ │ │ └── MutableClock.java │ │ │ │ │ └── jwt │ │ │ │ │ │ └── JWTCreator.java │ │ │ │ │ ├── discovery │ │ │ │ │ ├── Instance.java │ │ │ │ │ ├── ServiceDiscoveryClient.java │ │ │ │ │ ├── ServiceDiscoveryClientFactory.java │ │ │ │ │ ├── DiscoveryException.java │ │ │ │ │ └── zookeeper │ │ │ │ │ │ ├── ZkInstance.java │ │ │ │ │ │ ├── ZkServiceDiscoveryClient.java │ │ │ │ │ │ └── ZkServiceDiscoveryClientFactory.java │ │ │ │ │ ├── annotations │ │ │ │ │ ├── TestingUtil.java │ │ │ │ │ └── ApiTest.java │ │ │ │ │ └── zookeeper │ │ │ │ │ ├── ZkManager.java │ │ │ │ │ ├── DefaultZkManagerFactory.java │ │ │ │ │ ├── TestingZkManager.java │ │ │ │ │ └── DefaultZkManager.java │ │ │ └── resources │ │ │ │ └── log4j2.xml │ │ └── test │ │ │ └── java │ │ │ └── org │ │ │ └── npathai │ │ │ └── util │ │ │ └── NullSafeTest.java │ └── build.gradle ├── user-service │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ ├── application-test.yml │ │ │ │ └── test-user-init.sql │ │ │ └── java │ │ │ │ └── org │ │ │ │ └── npathai │ │ │ │ └── dao │ │ │ │ └── MySqlUserDaoTest.java │ │ └── main │ │ │ ├── java │ │ │ └── org │ │ │ │ └── npathai │ │ │ │ ├── dao │ │ │ │ ├── DataAccessException.java │ │ │ │ └── UserDao.java │ │ │ │ ├── Application.java │ │ │ │ ├── config │ │ │ │ └── MySqlDatasourceConfiguration.java │ │ │ │ ├── DomainBeanFactory.java │ │ │ │ ├── auth │ │ │ │ ├── CustomUserDetails.java │ │ │ │ ├── CustomJWTClaimsSetGenerator.java │ │ │ │ └── BasicAuthenticationProvider.java │ │ │ │ ├── model │ │ │ │ └── User.java │ │ │ │ ├── metrics │ │ │ │ └── ServiceTags.java │ │ │ │ └── controller │ │ │ │ └── CustomLoginController.java │ │ │ └── resources │ │ │ ├── log4j2.xml │ │ │ └── application.yml │ ├── Dockerfile │ ├── build.gradle │ └── init │ │ └── user-init.sql ├── short-url-generator │ ├── src │ │ ├── test │ │ │ ├── resources │ │ │ │ ├── application-test.yml │ │ │ │ └── test-init.sql │ │ │ └── java │ │ │ │ └── org │ │ │ │ └── npathai │ │ │ │ ├── controller │ │ │ │ └── NoRedirectionConfiguration.java │ │ │ │ ├── IdGenerationServiceStub.java │ │ │ │ ├── model │ │ │ │ └── RedirectionTest.java │ │ │ │ └── cache │ │ │ │ └── RedisRedirectionCacheTest.java │ │ └── main │ │ │ ├── java │ │ │ └── org │ │ │ │ └── npathai │ │ │ │ ├── domain │ │ │ │ ├── UnauthorizedAccessException.java │ │ │ │ └── RedirectionHistoryService.java │ │ │ │ ├── dao │ │ │ │ ├── DataAccessException.java │ │ │ │ ├── RedirectionDao.java │ │ │ │ └── InMemoryRedirectionDao.java │ │ │ │ ├── Application.java │ │ │ │ ├── cache │ │ │ │ ├── RedirectionCache.java │ │ │ │ ├── InMemoryRedirectionCache.java │ │ │ │ └── RedisRedirectionCache.java │ │ │ │ ├── config │ │ │ │ ├── RedisConfiguration.java │ │ │ │ ├── ZookeeperConfiguration.java │ │ │ │ ├── UrlLifetimeConfiguration.java │ │ │ │ └── MySqlDatasourceConfiguration.java │ │ │ │ ├── client │ │ │ │ ├── AnalyticsServiceClient.java │ │ │ │ └── IdGenerationServiceClient.java │ │ │ │ ├── api │ │ │ │ └── ShortenRequest.java │ │ │ │ ├── model │ │ │ │ ├── AnalyticsInfo.java │ │ │ │ ├── UserInfo.java │ │ │ │ └── Redirection.java │ │ │ │ ├── metrics │ │ │ │ └── ServiceTags.java │ │ │ │ └── DomainBeanFactory.java │ │ │ └── resources │ │ │ ├── log4j2.xml │ │ │ └── application.yml │ ├── Dockerfile │ └── build.gradle ├── nginx.conf ├── prometheus-micronaut.yml ├── load-test │ ├── build.gradle │ └── src │ │ └── loadTest │ │ └── simulations │ │ ├── ParseJsonSimulation.scala │ │ └── ShortUrlGeneratorSimulation.scala ├── init │ └── init.sql ├── prometheus-queries.md ├── README.md └── gradlew.bat └── README.md /chatter/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ -------------------------------------------------------------------------------- /bookmyshow/settings.gradle: -------------------------------------------------------------------------------- 1 | include 'application' -------------------------------------------------------------------------------- /discourse/settings.gradle: -------------------------------------------------------------------------------- 1 | include 'application' -------------------------------------------------------------------------------- /bookmyshow/gradle.properties: -------------------------------------------------------------------------------- 1 | micronautVersion=1.3.6 -------------------------------------------------------------------------------- /discourse/gradle.properties: -------------------------------------------------------------------------------- 1 | micronautVersion=1.3.6 -------------------------------------------------------------------------------- /tiny-url-generator/gradle.properties: -------------------------------------------------------------------------------- 1 | micronautVersion=1.3.6 -------------------------------------------------------------------------------- /tiny-url-generator/.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | before_install: 3 | - chmod +x gradlew 4 | -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | dependencies { 3 | compile project(':common-goodies') 4 | } -------------------------------------------------------------------------------- /tiny-url-generator/app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /chatter/model/client.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | const ( 4 | HeaderToken = "token" 5 | HeaderAuth = "Authorization" 6 | ) 7 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/Address.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall; 2 | 3 | public class Address { 4 | } 5 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/Rating.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall; 2 | 3 | public class Rating { 4 | } 5 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/movie/MovieDao.java: -------------------------------------------------------------------------------- 1 | package org.npathai.movie; 2 | 3 | public interface MovieDao { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /discourse/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npathai/system-design-with-code/HEAD/discourse/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /tiny-url-generator/app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npathai/system-design-with-code/HEAD/tiny-url-generator/app/public/favicon.ico -------------------------------------------------------------------------------- /bookmyshow/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npathai/system-design-with-code/HEAD/bookmyshow/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /bookmyshow/.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | */build/ 3 | /.gradle/ 4 | build 5 | */out/ 6 | 7 | # Data directory 8 | */data 9 | 10 | # React 11 | **/node_modules -------------------------------------------------------------------------------- /discourse/.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | */build/ 3 | /.gradle/ 4 | build 5 | */out/ 6 | 7 | # Data directory 8 | */data 9 | 10 | # React 11 | **/node_modules -------------------------------------------------------------------------------- /tiny-url-generator/settings.gradle: -------------------------------------------------------------------------------- 1 | include 'short-url-generator', 'id-gen-service', 'common-goodies','user-service', 2 | 'analytics-service', 'load-test' -------------------------------------------------------------------------------- /chatter/app/team.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import "github.com/npathai/chatter/model" 4 | 5 | func (app *App) createTeamWithUser(team *model.Team, userId int) { 6 | 7 | } -------------------------------------------------------------------------------- /tiny-url-generator/.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | */build/ 3 | /.gradle/ 4 | build 5 | */out/ 6 | 7 | # Data directory 8 | */data 9 | 10 | # React 11 | **/node_modules -------------------------------------------------------------------------------- /tiny-url-generator/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/npathai/system-design-with-code/HEAD/tiny-url-generator/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compile project(":common-goodies") 3 | 4 | implementation "io.micronaut:micronaut-security-jwt" 5 | } -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/seat/SeatType.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall.seat; 2 | 3 | public enum SeatType { 4 | ECONOMY, 5 | DELUX 6 | } 7 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/util/Stoppable.java: -------------------------------------------------------------------------------- 1 | package org.npathai.util; 2 | 3 | public interface Stoppable { 4 | void stop() throws Exception; 5 | } 6 | -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/main/java/org/npathai/domain/IdExhaustedException.java: -------------------------------------------------------------------------------- 1 | package org.npathai.domain; 2 | 3 | public class IdExhaustedException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/movie/Genre.java: -------------------------------------------------------------------------------- 1 | package org.npathai.movie; 2 | 3 | public enum Genre { 4 | COMEDY, 5 | ROMANCE, 6 | DRAMA, 7 | SCI_FI 8 | } 9 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/test/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | micronaut: 2 | server: 3 | port: -1 4 | 5 | --- 6 | consul: 7 | client: 8 | registration: 9 | enabled: false -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/seat/SeatNo.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall.seat; 2 | 3 | public class SeatNo { 4 | private int row; 5 | private int number; 6 | } 7 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/test/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | micronaut: 2 | server: 3 | port: -1 4 | 5 | --- 6 | consul: 7 | client: 8 | registration: 9 | enabled: false -------------------------------------------------------------------------------- /chatter/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/npathai/chatter 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/google/uuid v1.1.2 7 | github.com/gorilla/mux v1.8.0 8 | github.com/spf13/cobra v1.0.0 9 | ) 10 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/domain/UnauthorizedAccessException.java: -------------------------------------------------------------------------------- 1 | package org.npathai.domain; 2 | 3 | public class UnauthorizedAccessException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/test/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | micronaut: 2 | server: 3 | port: -1 4 | 5 | --- 6 | 7 | consul: 8 | client: 9 | registration: 10 | enabled: false -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/test/resources/application-test.yml: -------------------------------------------------------------------------------- 1 | micronaut: 2 | server: 3 | port: -1 4 | 5 | --- 6 | 7 | consul: 8 | client: 9 | registration: 10 | enabled: false -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/usecases/BookingStatus.java: -------------------------------------------------------------------------------- 1 | package org.npathai.usecases; 2 | 3 | public enum BookingStatus { 4 | PENDING, 5 | RESERVED, 6 | CONFIRMED, 7 | CANCELLED 8 | } 9 | -------------------------------------------------------------------------------- /chatter/web/context.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/npathai/chatter/app" 5 | "github.com/npathai/chatter/model" 6 | ) 7 | 8 | type Context struct { 9 | App *app.App 10 | Err *model.AppError 11 | } 12 | -------------------------------------------------------------------------------- /chatter/app/options.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | type AppOption func(*App) 4 | type AppOptionCreator func() []AppOption 5 | 6 | func ServerConnector(s *Server) AppOption { 7 | return func(app *App) { 8 | app.srv = s 9 | } 10 | } -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/util/ThrowingConsumer.java: -------------------------------------------------------------------------------- 1 | package org.npathai.util; 2 | 3 | @FunctionalInterface 4 | public interface ThrowingConsumer { 5 | void accept(T o) throws Exception; 6 | } 7 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/Application.java: -------------------------------------------------------------------------------- 1 | package org.npathai; 2 | 3 | public class Application { 4 | 5 | public static void main(String[] args) { 6 | System.out.println("Application started"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /chatter/cmd/chatter/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/npathai/chatter/cmd/chatter/commands" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | if err := commands.Run(os.Args[1:]); err != nil { 10 | os.Exit(1) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/discovery/Instance.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discovery; 2 | 3 | public interface Instance { 4 | String name(); 5 | String id(); 6 | String address(); 7 | Integer port(); 8 | } 9 | -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/domain/users/UsernameAlreadyExistsException.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.domain.users; 2 | 3 | public class UsernameAlreadyExistsException extends RuntimeException { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/java/org/npathai/dao/DataAccessException.java: -------------------------------------------------------------------------------- 1 | package org.npathai.dao; 2 | 3 | public class DataAccessException extends Exception { 4 | 5 | public DataAccessException(Exception ex) { 6 | super(ex); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/main/java/org/npathai/dao/DataAccessException.java: -------------------------------------------------------------------------------- 1 | package org.npathai.dao; 2 | 3 | public class DataAccessException extends Exception { 4 | 5 | public DataAccessException(Exception ex) { 6 | super(ex); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk13-openj9:jdk-13.0.2_8_openj9-0.18.0-alpine-slim 2 | 3 | # Run the application 4 | CMD ["java", "-Dcom.sun.management.jmxremote", "-Xmx128m", "-XX:+IdleTuningGcOnIdle", "-Xtune:virtualized", "-jar", "app.jar"] -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/dao/DataAccessException.java: -------------------------------------------------------------------------------- 1 | package org.npathai.dao; 2 | 3 | public class DataAccessException extends Exception { 4 | 5 | public DataAccessException(Exception ex) { 6 | super(ex); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/city/CityId.java: -------------------------------------------------------------------------------- 1 | package org.npathai.city; 2 | 3 | import org.npathai.common.ValueObject; 4 | 5 | public class CityId extends ValueObject { 6 | 7 | public CityId(Long value) { 8 | super(value); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/CinemaHallDao.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall; 2 | 3 | import org.npathai.city.CityId; 4 | 5 | import java.util.List; 6 | 7 | public interface CinemaHallDao { 8 | List getCinemaHalls(CityId cityId); 9 | } 10 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/movie/MovieId.java: -------------------------------------------------------------------------------- 1 | package org.npathai.movie; 2 | 3 | import org.npathai.common.ValueObject; 4 | 5 | public class MovieId extends ValueObject { 6 | 7 | public MovieId(Long value) { 8 | super(value); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/seat/SeatId.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall.seat; 2 | 3 | import org.npathai.common.ValueObject; 4 | 5 | public class SeatId extends ValueObject { 6 | public SeatId(long id) { 7 | super(id); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/city/CityDao.java: -------------------------------------------------------------------------------- 1 | package org.npathai.city; 2 | 3 | import java.util.Collections; 4 | import java.util.List; 5 | 6 | public class CityDao { 7 | public List getCities() { 8 | return Collections.emptyList(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /bookmyshow/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 12 04:20:33 EDT 2020 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /discourse/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 12 04:20:33 EDT 2020 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/java/org/npathai/Application.java: -------------------------------------------------------------------------------- 1 | package org.npathai; 2 | 3 | import io.micronaut.runtime.Micronaut; 4 | 5 | public class Application { 6 | 7 | public static void main(String[] args) { 8 | Micronaut.run(Application.class); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/java/org/npathai/dao/UserDao.java: -------------------------------------------------------------------------------- 1 | package org.npathai.dao; 2 | 3 | import org.npathai.model.User; 4 | 5 | import java.util.Optional; 6 | 7 | public interface UserDao { 8 | Optional getUserByName(String username) throws DataAccessException; 9 | } 10 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/CinemaHallId.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall; 2 | 3 | import org.npathai.common.ValueObject; 4 | 5 | public class CinemaHallId extends ValueObject { 6 | public CinemaHallId(Long value) { 7 | super(value); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tiny-url-generator/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 12 04:20:33 EDT 2020 2 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-all.zip 3 | distributionBase=GRADLE_USER_HOME 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/main/java/org/npathai/Application.java: -------------------------------------------------------------------------------- 1 | package org.npathai; 2 | 3 | import io.micronaut.runtime.Micronaut; 4 | 5 | public class Application { 6 | 7 | public static void main(String[] args) { 8 | Micronaut.run(Application.class); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /tiny-url-generator/app/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import {combineReducers} from "redux"; 2 | import auth from './auth' 3 | import redirection from './redirection' 4 | import error from './error' 5 | 6 | export default combineReducers({ 7 | auth: auth, 8 | redirection: redirection, 9 | error: error 10 | }) -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/Application.java: -------------------------------------------------------------------------------- 1 | package org.npathai; 2 | 3 | import io.micronaut.runtime.Micronaut; 4 | 5 | public class Application { 6 | 7 | public static void main(String[] args) { 8 | Micronaut.run(Application.class); 9 | } 10 | 11 | } 12 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/util/thread/ScheduledJobService.java: -------------------------------------------------------------------------------- 1 | package org.npathai.util.thread; 2 | 3 | import java.util.concurrent.Callable; 4 | import java.util.concurrent.Future; 5 | 6 | public interface ScheduledJobService { 7 | Future submit(Callable callable); 8 | } 9 | -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/common/IdGenerator.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.common; 2 | 3 | import java.util.UUID; 4 | 5 | public class IdGenerator { 6 | 7 | public String nextId() { 8 | return UUID.randomUUID().toString(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/Application.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application; 2 | 3 | import io.micronaut.runtime.Micronaut; 4 | 5 | public class Application { 6 | 7 | public static void main(String[] args) { 8 | Micronaut.run(Application.class); 9 | } 10 | } -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/auditorium/AuditoriumId.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall.auditorium; 2 | 3 | import org.npathai.common.ValueObject; 4 | 5 | public class AuditoriumId extends ValueObject { 6 | public AuditoriumId(Long value) { 7 | super(value); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/city/City.java: -------------------------------------------------------------------------------- 1 | package org.npathai.city; 2 | 3 | public class City { 4 | public final CityId cityId; 5 | public final String name; 6 | 7 | public City(CityId cityId, String name) { 8 | this.cityId = cityId; 9 | this.name = name; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/domain/users/UserRepository.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.domain.users; 2 | 3 | import java.util.Optional; 4 | 5 | public interface UserRepository { 6 | void save(User user); 7 | 8 | Optional getById(String userId); 9 | } 10 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/discovery/ServiceDiscoveryClient.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discovery; 2 | 3 | import javax.annotation.Nonnull; 4 | import java.io.Closeable; 5 | 6 | public interface ServiceDiscoveryClient extends Closeable { 7 | @Nonnull Instance getInstance() throws DiscoveryException; 8 | } 9 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/util/thread/ThrowingRunnable.java: -------------------------------------------------------------------------------- 1 | package org.npathai.util.thread; 2 | 3 | public interface ThrowingRunnable { 4 | void run() throws Exception; 5 | 6 | default SwallowingRunnable toSwallowing() { 7 | return new SwallowingRunnable(this); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/domain/topics/TopicService.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.domain.topics; 2 | 3 | import java.util.List; 4 | 5 | public class TopicService { 6 | public List getTopics(int userId) { 7 | throw new UnsupportedOperationException(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/util/NullSafe.java: -------------------------------------------------------------------------------- 1 | package org.npathai.util; 2 | 3 | public class NullSafe { 4 | 5 | public static void ifNotNull(T object, ThrowingConsumer consumer) throws Exception { 6 | if (object != null) { 7 | consumer.accept(object); 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/test/java/org/npathai/api/IdGeneratorClient.java: -------------------------------------------------------------------------------- 1 | package org.npathai.api; 2 | 3 | import io.micronaut.http.annotation.Get; 4 | import io.micronaut.http.client.annotation.Client; 5 | 6 | @Client("/") 7 | public interface IdGeneratorClient { 8 | 9 | @Get("/generate") 10 | String generate(); 11 | } 12 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/show/ShowDao.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall.show; 2 | 3 | import org.npathai.cinemahall.auditorium.AuditoriumId; 4 | import org.npathai.movie.MovieId; 5 | 6 | import java.util.List; 7 | 8 | public interface ShowDao { 9 | List getShows(AuditoriumId id, MovieId movieId); 10 | } 11 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/discovery/ServiceDiscoveryClientFactory.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discovery; 2 | 3 | import java.util.Properties; 4 | 5 | public interface ServiceDiscoveryClientFactory { 6 | ServiceDiscoveryClient createDiscoveryClient(String serviceName, Properties properties) throws DiscoveryException; 7 | } 8 | -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk13-openj9:jdk-13.0.2_8_openj9-0.18.0-alpine-slim 2 | 3 | # Copy fat jar to docker image 4 | # COPY build/libs/id-gen-service.jar /app.jar 5 | 6 | # Run the application 7 | CMD ["java", "-Dcom.sun.management.jmxremote", "-Xmx128m", "-XX:+IdleTuningGcOnIdle", "-Xtune:virtualized", "-jar", "app.jar"] -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk13-openj9:jdk-13.0.2_8_openj9-0.18.0-alpine-slim 2 | 3 | # Copy fat jar to docker image 4 | # COPY build/libs/short-url-generator.jar /app.jar 5 | 6 | # Run the application 7 | CMD ["java", "-Dcom.sun.management.jmxremote", "-Xmx128m", "-XX:+IdleTuningGcOnIdle", "-Xtune:virtualized", "-jar", "app.jar"] -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/annotations/TestingUtil.java: -------------------------------------------------------------------------------- 1 | package org.npathai.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Target; 5 | 6 | /** 7 | * Documents that the type is for testing purposes only. 8 | */ 9 | @Target(ElementType.TYPE) 10 | public @interface TestingUtil { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/discovery/DiscoveryException.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discovery; 2 | 3 | public class DiscoveryException extends Exception { 4 | public DiscoveryException(Exception ex) { 5 | super(ex); 6 | } 7 | 8 | public DiscoveryException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/main/java/org/npathai/domain/Batch.java: -------------------------------------------------------------------------------- 1 | package org.npathai.domain; 2 | 3 | import java.util.Set; 4 | 5 | public class Batch { 6 | private final Set ids; 7 | 8 | public Batch(Set ids) { 9 | this.ids = ids; 10 | } 11 | 12 | public Set ids() { 13 | return ids; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /chatter/store/errors.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | type ErrNotFound struct { 4 | resource string 5 | Id string 6 | } 7 | 8 | func NewErrNotFound(resource, id string) *ErrNotFound { 9 | return &ErrNotFound{ 10 | resource: resource, 11 | Id: id, 12 | } 13 | } 14 | 15 | func (err *ErrNotFound) Error() string { 16 | return "resource: " + err.resource + " id: " + err.Id 17 | } -------------------------------------------------------------------------------- /chatter/cmd/chatter/commands/root.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import "github.com/spf13/cobra" 4 | 5 | func Run(args []string) error { 6 | RootCmd.SetArgs(args) 7 | return RootCmd.Execute() 8 | } 9 | 10 | var RootCmd = &cobra.Command{ 11 | Use: "chatter", 12 | Short: "A Mattermost clone", 13 | Long: "Offers workplace communication across all platforms", 14 | } 15 | 16 | 17 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/main/java/org/npathai/dao/AnalyticsDao.java: -------------------------------------------------------------------------------- 1 | package org.npathai.dao; 2 | 3 | import org.npathai.model.AnalyticsInfo; 4 | 5 | import java.util.Optional; 6 | 7 | public interface AnalyticsDao { 8 | void incrementClick(String id); 9 | Optional getById(String id); 10 | void save(AnalyticsInfo analyticsInfo); 11 | } 12 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/cache/RedirectionCache.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cache; 2 | 3 | import org.npathai.model.Redirection; 4 | 5 | import java.util.Optional; 6 | 7 | public interface RedirectionCache { 8 | 9 | Optional getById(String id); 10 | 11 | void put(Redirection redirection); 12 | 13 | void deleteById(String id); 14 | } 15 | -------------------------------------------------------------------------------- /tiny-url-generator/app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /tiny-url-generator/app/src/store/store.js: -------------------------------------------------------------------------------- 1 | import {applyMiddleware, createStore} from "redux"; 2 | import {createLogger} from "redux-logger/src"; 3 | import rootReducer from '../reducers/index' 4 | import thunk from "redux-thunk"; 5 | 6 | const logger = createLogger({ 7 | collapsed: true 8 | }) 9 | 10 | const store = createStore( 11 | rootReducer, 12 | applyMiddleware(thunk, logger) 13 | ) 14 | 15 | export default store -------------------------------------------------------------------------------- /tiny-url-generator/app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/test/resources/test-user-init.sql: -------------------------------------------------------------------------------- 1 | USE user_db; 2 | 3 | /* 4 | MySQL VARCHAR is not case-sensitive. 5 | */ 6 | CREATE TABLE users ( 7 | id binary(16) PRIMARY KEY, 8 | username VARCHAR(100) NOT NULL UNIQUE, 9 | password VARCHAR(100) NOT NULL, 10 | email VARCHAR(100) 11 | ); 12 | 13 | INSERT INTO users VALUES(UUID_TO_BIN(UUID(), true), 'root', 14 | '$2a$09$C75xhHFSNwj0GV6STPWTqOgZ2qYpvH88QxGXbxWUF/kC0qgfJAEI.', NULL); 15 | 16 | 17 | COMMIT; -------------------------------------------------------------------------------- /chatter/app/app.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import "github.com/npathai/chatter/model" 4 | 5 | type App struct { 6 | srv *Server 7 | session model.Session 8 | } 9 | 10 | func (app *App) Srv() *Server { 11 | return app.srv 12 | } 13 | 14 | func New(options...AppOption) *App { 15 | app := &App{} 16 | for _, option := range options { 17 | option(app) 18 | } 19 | return app 20 | } 21 | 22 | func (app *App) SetSession(session *model.Session) { 23 | app.session = *session 24 | } -------------------------------------------------------------------------------- /tiny-url-generator/nginx.conf: -------------------------------------------------------------------------------- 1 | user nginx; 2 | 3 | events { 4 | worker_connections 1000; 5 | } 6 | 7 | http { 8 | server { 9 | listen 4000; 10 | location /login { 11 | proxy_pass http://user-service:8080; 12 | } 13 | location /analytics { 14 | proxy_pass http://analytics-service:8080; 15 | } 16 | location / { 17 | proxy_pass http://short-url-generator:8080; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/config/RedisConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.npathai.config; 2 | 3 | import io.micronaut.context.annotation.ConfigurationProperties; 4 | 5 | @ConfigurationProperties("redis") 6 | public class RedisConfiguration { 7 | 8 | private String url; 9 | 10 | public String getUrl() { 11 | return url; 12 | } 13 | 14 | public void setUrl(String url) { 15 | this.url = url; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/test/java/org/npathai/controller/NoRedirectionConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.npathai.controller; 2 | 3 | import io.micronaut.http.client.DefaultHttpClientConfiguration; 4 | 5 | import javax.inject.Singleton; 6 | 7 | @Singleton 8 | public class NoRedirectionConfiguration extends DefaultHttpClientConfiguration { 9 | @Override 10 | public boolean isFollowRedirects() { 11 | return false; 12 | } 13 | } -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/usecases/GetCities.java: -------------------------------------------------------------------------------- 1 | package org.npathai.usecases; 2 | 3 | import org.npathai.city.City; 4 | import org.npathai.city.CityDao; 5 | 6 | import java.util.List; 7 | 8 | public class GetCities { 9 | private final CityDao cityDao; 10 | 11 | public GetCities(CityDao cityDao) { 12 | this.cityDao = cityDao; 13 | } 14 | 15 | public List execute() { 16 | return cityDao.getCities(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/main/java/org/npathai/config/ZookeeperConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.npathai.config; 2 | 3 | import io.micronaut.context.annotation.ConfigurationProperties; 4 | 5 | @ConfigurationProperties("zookeeper") 6 | public class ZookeeperConfiguration { 7 | 8 | private String url; 9 | 10 | public String getUrl() { 11 | return url; 12 | } 13 | 14 | public void setUrl(String url) { 15 | this.url = url; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/annotations/ApiTest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Documents that the test is an API level test 10 | */ 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @Target(ElementType.TYPE) 13 | public @interface ApiTest { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/config/ZookeeperConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.npathai.config; 2 | 3 | import io.micronaut.context.annotation.ConfigurationProperties; 4 | 5 | @ConfigurationProperties("zookeeper") 6 | public class ZookeeperConfiguration { 7 | 8 | private String url; 9 | 10 | public String getUrl() { 11 | return url; 12 | } 13 | 14 | public void setUrl(String url) { 15 | this.url = url; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /discourse/application/src/test/java/org/npathai/discourse/application/controller/topics/TopicControllerClient.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.controller.topics; 2 | 3 | import io.micronaut.http.annotation.Get; 4 | import io.micronaut.http.client.annotation.Client; 5 | import org.npathai.discourse.application.domain.topics.Topic; 6 | 7 | import java.util.List; 8 | 9 | @Client("/topics") 10 | public interface TopicControllerClient { 11 | @Get 12 | List get(); 13 | } -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/client/AnalyticsServiceClient.java: -------------------------------------------------------------------------------- 1 | package org.npathai.client; 2 | 3 | import io.micronaut.http.annotation.Post; 4 | import io.micronaut.http.client.annotation.Client; 5 | 6 | @Client("analytics-service") 7 | public interface AnalyticsServiceClient { 8 | 9 | @Post("/analytics/{id}") 10 | void redirectionCreated(String id); 11 | 12 | @Post("/analytics/{id}/click") 13 | void redirectionClicked(String id); 14 | } 15 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/usecases/Booking.java: -------------------------------------------------------------------------------- 1 | package org.npathai.usecases; 2 | 3 | import org.npathai.cinemahall.seat.Seat; 4 | import org.npathai.cinemahall.show.Show; 5 | import org.npathai.movie.Movie; 6 | 7 | import java.math.BigDecimal; 8 | 9 | public class Booking { 10 | private long id; 11 | private Seat seat; 12 | private Movie movie; 13 | private Show show; 14 | private BigDecimal totalAmount; 15 | private BookingStatus status; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /tiny-url-generator/prometheus-micronaut.yml: -------------------------------------------------------------------------------- 1 | scrape_configs: 2 | # The job name is added as a label `job=` to any timeseries scraped from this config. 3 | - job_name: 'prometheus' 4 | 5 | # metrics_path defaults to '/metrics' 6 | # scheme defaults to 'http'. 7 | 8 | static_configs: 9 | - targets: ['127.0.0.1:9090'] 10 | 11 | - job_name: 'micronaut' 12 | metrics_path: '/prometheus' 13 | scrape_interval: 5s 14 | consul_sd_configs: 15 | - server: "consul:8500" -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/main/java/org/npathai/Application.java: -------------------------------------------------------------------------------- 1 | package org.npathai; 2 | 3 | import io.micronaut.runtime.Micronaut; 4 | import org.apache.logging.log4j.LogManager; 5 | import org.apache.logging.log4j.Logger; 6 | 7 | public class Application { 8 | private static final Logger LOG = LogManager.getLogger(Application.class); 9 | 10 | public static void main(String[] args) { 11 | LOG.info("Starting application"); 12 | 13 | Micronaut.run(Application.class); 14 | } 15 | } -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/client/IdGenerationServiceClient.java: -------------------------------------------------------------------------------- 1 | package org.npathai.client; 2 | 3 | import io.micronaut.http.MediaType; 4 | import io.micronaut.http.annotation.Get; 5 | import io.micronaut.http.annotation.Produces; 6 | import io.micronaut.http.client.annotation.Client; 7 | 8 | @Client(id = "id-gen-service") 9 | public interface IdGenerationServiceClient { 10 | 11 | @Get("/generate") 12 | @Produces(MediaType.TEXT_PLAIN) 13 | String generateId(); 14 | } 15 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/auditorium/Auditorium.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall.auditorium; 2 | 3 | public class Auditorium { 4 | private long id; 5 | private String name; 6 | private int seatCount; 7 | 8 | public Auditorium(long id, String name, int seatCount) { 9 | this.id = id; 10 | this.name = name; 11 | this.seatCount = seatCount; 12 | } 13 | 14 | public AuditoriumId getId() { 15 | return new AuditoriumId(id); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/domain/users/MySqlUserRepository.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.domain.users; 2 | 3 | import java.util.Optional; 4 | 5 | public class MySqlUserRepository implements UserRepository { 6 | 7 | @Override 8 | public void save(User user) { 9 | throw new UnsupportedOperationException(); 10 | } 11 | 12 | @Override 13 | public Optional getById(String userId) { 14 | throw new UnsupportedOperationException(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /chatter/model/team_signup.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | ) 8 | 9 | type TeamSignup struct { 10 | Team Team `json:"team"` 11 | User User `json:"user"` 12 | Data string `json:"data"` 13 | Hash string `json:"hash"` 14 | } 15 | 16 | func TeamSignupFromJson(data io.Reader) *TeamSignup { 17 | decoder := json.NewDecoder(data) 18 | var signup TeamSignup 19 | err := decoder.Decode(&signup) 20 | if err == nil { 21 | return &signup 22 | } else { 23 | fmt.Println(err) 24 | return nil 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/dao/RedirectionDao.java: -------------------------------------------------------------------------------- 1 | package org.npathai.dao; 2 | 3 | import org.npathai.model.Redirection; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | 8 | public interface RedirectionDao { 9 | void save(Redirection redirection) throws DataAccessException; 10 | Optional getById(String id) throws DataAccessException; 11 | void deleteById(String id) throws DataAccessException; 12 | List getAllByUser(String uid) throws DataAccessException; 13 | } 14 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM adoptopenjdk/openjdk13-openj9:jdk-13.0.2_8_openj9-0.18.0-alpine-slim 2 | 3 | # Copy fat jar to docker image 4 | # COPY build/libs/user-service.jar /app.jar 5 | 6 | # Add docker-compose-wait tool ------------------- 7 | ENV WAIT_VERSION 2.7.2 8 | ADD https://github.com/ufoscout/docker-compose-wait/releases/download/$WAIT_VERSION/wait /wait 9 | RUN chmod +x /wait 10 | 11 | # Run the application 12 | CMD ["java", "-Dcom.sun.management.jmxremote", "-Xmx128m", "-XX:+IdleTuningGcOnIdle", "-Xtune:virtualized", "-jar", "app.jar"] -------------------------------------------------------------------------------- /tiny-url-generator/load-test/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'scala' 3 | id 'com.github.lkishalmi.gatling' version "3.3.4" 4 | } 5 | 6 | repositories { 7 | jcenter() 8 | mavenCentral() 9 | } 10 | 11 | sourceSets { 12 | gatling { 13 | scala.srcDir "src/loadTest" 14 | } 15 | } 16 | 17 | task loadTest() { 18 | outputs.upToDateWhen { false } 19 | // A mechanism to ensure running of the test 20 | // Ref: https://stackoverflow.com/a/52484259 21 | // dependsOn rootProject.getTasksByName("composeUp", true) 22 | dependsOn gatlingRun 23 | } -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/show/ShowingSeatDao.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall.show; 2 | 3 | import org.npathai.cinemahall.seat.Seat; 4 | import org.npathai.cinemahall.seat.SeatId; 5 | import org.npathai.cinemahall.seat.SeatNo; 6 | import org.npathai.cinemahall.seat.SeatType; 7 | 8 | import java.math.BigDecimal; 9 | 10 | public class ShowingSeatDao { 11 | 12 | public ShowingSeat reserveSeat(SeatId seatId) { 13 | ShowingSeat seat = new ShowingSeat(seatId.getValue(), SeatType.DELUX, BigDecimal.TEN, new SeatNo()); 14 | return seat; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /chatter/api/handlers.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/npathai/chatter/web" 5 | "net/http" 6 | ) 7 | 8 | func (api *API) ApiHandler(h func(*web.Context, http.ResponseWriter, *http.Request)) http.Handler { 9 | return &web.Handler{ 10 | GetGlobalAppOptions: api.GetGlobalAppOptions, 11 | HandlerFunc: h, 12 | } 13 | } 14 | 15 | func (api *API) ApiSessionRequired(h func(ctx *web.Context, w http.ResponseWriter, r *http.Request)) http.Handler { 16 | return &web.Handler{ 17 | GetGlobalAppOptions: api.GetGlobalAppOptions, 18 | HandlerFunc: h, 19 | RequireSession: true, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/util/thread/SwallowingRunnable.java: -------------------------------------------------------------------------------- 1 | package org.npathai.util.thread; 2 | 3 | public class SwallowingRunnable implements Runnable{ 4 | 5 | private final ThrowingRunnable throwingRunnable; 6 | 7 | public SwallowingRunnable(ThrowingRunnable throwingRunnable) { 8 | this.throwingRunnable = throwingRunnable; 9 | } 10 | 11 | @Override 12 | public void run() { 13 | try { 14 | throwingRunnable.run(); 15 | } catch (Exception ex) { 16 | ex.printStackTrace(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/test/resources/test-init.sql: -------------------------------------------------------------------------------- 1 | 2 | USE short_url_generator; 3 | 4 | /* 5 | MySQL VARCHAR is not case-sensitive, so it cased integrity violation because it considered AAAAa and AAAAA as same 6 | values. That's why SET utf8 COLLATE utf8_bin has been added which makes it case insensitive. 7 | */ 8 | CREATE TABLE redirection( 9 | id VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 10 | long_url VARCHAR(500) NOT NULL, 11 | created_at TIMESTAMP(3) NOT NULL, 12 | expiry_at TIMESTAMP(3) NOT NULL, 13 | uid VARCHAR(50), 14 | PRIMARY KEY(id) 15 | ); -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/seat/Seat.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall.seat; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class Seat { 6 | private long id; 7 | private SeatType seatType; 8 | private BigDecimal price; 9 | private SeatNo seatNo; 10 | 11 | public Seat(long id, SeatType seatType, BigDecimal price, SeatNo seatNo) { 12 | this.id = id; 13 | this.seatType = seatType; 14 | this.price = price; 15 | this.seatNo = seatNo; 16 | } 17 | 18 | public SeatId getId() { 19 | return new SeatId(id); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compile project(":common-goodies") 3 | // https://mvnrepository.com/artifact/mysql/mysql-connector-java 4 | compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.20' 5 | 6 | implementation "io.micronaut:micronaut-security-jwt" 7 | 8 | testCompile platform('org.testcontainers:testcontainers-bom:1.14.3') //import bom 9 | testCompile "org.testcontainers:testcontainers" 10 | testCompile "org.testcontainers:mysql" 11 | testCompile "org.testcontainers:junit-jupiter" 12 | 13 | implementation 'at.favre.lib:bcrypt:0.9.0' 14 | } -------------------------------------------------------------------------------- /discourse/application/src/test/java/org/npathai/discourse/application/controller/users/UserControllerClient.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.controller.users; 2 | 3 | import io.micronaut.http.HttpResponse; 4 | import io.micronaut.http.annotation.Post; 5 | import io.micronaut.http.client.annotation.Client; 6 | import org.npathai.discourse.application.domain.users.RegistrationData; 7 | import org.npathai.discourse.application.domain.users.User; 8 | 9 | @Client(value = "/users") 10 | public interface UserControllerClient { 11 | 12 | @Post 13 | HttpResponse create(RegistrationData registrationData); 14 | } 15 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/zookeeper/ZkManager.java: -------------------------------------------------------------------------------- 1 | package org.npathai.zookeeper; 2 | 3 | import org.apache.curator.framework.CuratorFramework; 4 | import org.apache.zookeeper.data.Stat; 5 | import org.npathai.util.Stoppable; 6 | 7 | import java.util.concurrent.Callable; 8 | 9 | public interface ZkManager extends Stoppable { 10 | Stat createIfAbsent(String path) throws Exception; 11 | Callable withinLock(String lockPath, Callable callable); 12 | byte[] getData(String path) throws Exception; 13 | void setData(String path, byte[] bytes) throws Exception; 14 | CuratorFramework client(); 15 | } 16 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/init/user-init.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS user_db; 2 | 3 | ALTER USER 'root' IDENTIFIED BY 'unsecured'; 4 | GRANT ALL PRIVILEGES ON urldb.* TO 'root'@'%'; 5 | GRANT ALL PRIVILEGES ON urldb.* TO 'root'@'localhost'; 6 | 7 | USE user_db; 8 | 9 | /* 10 | MySQL VARCHAR is not case-sensitive. 11 | */ 12 | CREATE TABLE users ( 13 | id binary(16) PRIMARY KEY, 14 | username VARCHAR(100) NOT NULL UNIQUE, 15 | password VARCHAR(100) NOT NULL, 16 | email VARCHAR(100) 17 | ); 18 | 19 | INSERT INTO users VALUES(UUID_TO_BIN(UUID(), true), 'root', 20 | '$2a$09$C75xhHFSNwj0GV6STPWTqOgZ2qYpvH88QxGXbxWUF/kC0qgfJAEI.', NULL); 21 | 22 | 23 | COMMIT; -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/usecases/GetMoviesByCityId.java: -------------------------------------------------------------------------------- 1 | package org.npathai.usecases; 2 | 3 | import org.npathai.city.CityId; 4 | import org.npathai.movie.Movie; 5 | import org.npathai.movie.MovieRepository; 6 | 7 | import java.util.List; 8 | 9 | public class GetMoviesByCityId { 10 | private final MovieRepository movieRepository; 11 | private final CityId cityId; 12 | 13 | public GetMoviesByCityId(MovieRepository movieRepository, CityId cityId) { 14 | this.movieRepository = movieRepository; 15 | this.cityId = cityId; 16 | } 17 | 18 | public List execute() { 19 | return movieRepository.getByCityId(cityId); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /discourse/application/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tiny-url-generator/app/src/components/login/SignIn.css: -------------------------------------------------------------------------------- 1 | .form-signin { 2 | width: 100%; 3 | max-width: 330px; 4 | padding: 15px; 5 | margin: 0 auto; 6 | } 7 | .form-signin .form-control { 8 | position: relative; 9 | box-sizing: border-box; 10 | height: auto; 11 | padding: 10px; 12 | font-size: 16px; 13 | } 14 | .form-signin .form-control:focus { 15 | z-index: 2; 16 | } 17 | .form-signin input[type="text"] { 18 | margin-bottom: -1px; 19 | border-bottom-right-radius: 0; 20 | border-bottom-left-radius: 0; 21 | } 22 | .form-signin input[type="password"] { 23 | margin-bottom: 10px; 24 | border-top-left-radius: 0; 25 | border-top-right-radius: 0; 26 | } -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | micronaut: 2 | application: 3 | name: id-gen-service 4 | server: 5 | port: 8080 6 | cors: 7 | enabled: true 8 | netty: 9 | log-level: ERROR 10 | 11 | --- 12 | endpoints: 13 | prometheus: 14 | sensitive: false 15 | --- 16 | micronaut: 17 | metrics: 18 | enabled: true 19 | export: 20 | prometheus: 21 | enabled: true 22 | step: PT1M 23 | descriptions: true 24 | --- 25 | zookeeper: 26 | url: zookeeper:2181 27 | 28 | --- 29 | consul: 30 | client: 31 | registration: 32 | enabled: true 33 | defaultZone: "${CONSUL_HOST:consul}:${CONSUL_PORT:8500}" 34 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/build.gradle: -------------------------------------------------------------------------------- 1 | dependencies { 2 | compile project(":common-goodies") 3 | // https://mvnrepository.com/artifact/mysql/mysql-connector-java 4 | compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.20' 5 | 6 | compile 'org.redisson:redisson:3.13.4' 7 | 8 | implementation "io.micronaut:micronaut-security-jwt" 9 | 10 | testCompile platform('org.testcontainers:testcontainers-bom:1.14.3') //import bom 11 | testCompile "org.testcontainers:testcontainers" 12 | testCompile "org.testcontainers:mysql" 13 | testCompile "org.testcontainers:junit-jupiter" 14 | 15 | compile "io.micronaut.configuration:micronaut-micrometer-core" 16 | } -------------------------------------------------------------------------------- /tiny-url-generator/app/src/constants.js: -------------------------------------------------------------------------------- 1 | import keyMirror from 'keymirror' 2 | 3 | 4 | export const ActionTypes = keyMirror({ 5 | CHANGE_USERNAME: null, 6 | CHANGE_PASSWORD: null, 7 | CHANGE_LONG_URL: null, 8 | CHANGE_COPIED: null, 9 | 10 | REQUEST_SIGN_IN: null, 11 | RECEIVED_SIGN_IN_SUCCESS: null, 12 | RECEIVED_SIGN_IN_FAILURE: null, 13 | RECEIVED_SIGN_IN_INVALID_ATTEMPT_FAILURE: null, 14 | REQUEST_REDIRECTION_HISTORY: null, 15 | RECEIVED_REDIRECTION_HISTORY_SUCCESS: null, 16 | RECEIVED_REDIRECTION_HISTORY_FAILURE: null, 17 | 18 | REQUEST_SHORTEN_REDIRECTION: null, 19 | RECEIVED_SHORTEN_REDIRECTION_SUCCESS: null, 20 | RECEIVED_SHORTEN_REDIRECTION_FAILURE: null, 21 | 22 | }) -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/test/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /chatter/api/team.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/npathai/chatter/app" 5 | "github.com/npathai/chatter/model" 6 | "github.com/npathai/chatter/web" 7 | "net/http" 8 | "strings" 9 | ) 10 | 11 | func (api *API) InitTeam() { 12 | api.BaseRoutes.Teams.Handle("", api.ApiSessionRequired(createTeam)).Methods("POST") 13 | } 14 | 15 | func createTeam(ctx *web.Context, w http.ResponseWriter, r *http.Request) { 16 | team := model.TeamFromJson(r.Body) 17 | 18 | team.Email = strings.ToLower(team.Email) 19 | 20 | rTeam, err := ctx.App.createTeamWithUser(team, ctx.App.Session().UserId) 21 | if err != nil { 22 | ctx.Err = err 23 | return 24 | } 25 | w.WriteHeader(http.StatusCreated) 26 | w.Write([]byte(rTeam.toJson())) 27 | } 28 | -------------------------------------------------------------------------------- /chatter/app/authentication.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/npathai/chatter/model" 5 | "net/http" 6 | ) 7 | 8 | func (app *App) authenticateUser(user *model.User, password string) (*model.User, *model.AppError) { 9 | 10 | if err := app.CheckPasswordAndCriteria(user, password); err != nil { 11 | err.StatusCode = http.StatusUnauthorized 12 | return nil, err 13 | } 14 | 15 | return user, nil 16 | } 17 | 18 | 19 | func (a *App) CheckPasswordAndCriteria(user *model.User, password string) *model.AppError { 20 | return nil 21 | } 22 | 23 | func ParseAuthTokenFromRequest(r *http.Request) string { 24 | authHeader := r.Header.Get(model.HeaderAuth) 25 | 26 | if len(authHeader) <= 6 { 27 | return "" 28 | } 29 | 30 | return authHeader[6:] 31 | } -------------------------------------------------------------------------------- /chatter/app/session.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/npathai/chatter/model" 5 | "net/http" 6 | ) 7 | 8 | func (app *App) CreateSession(session *model.Session) (*model.Session, *model.AppError) { 9 | session, err := app.Srv().Store.Session().Save(session) 10 | if err != nil { 11 | return nil, model.NewAppErrorWithStatus("Couldn't create session", http.StatusInternalServerError) 12 | } 13 | return session, nil 14 | } 15 | 16 | func (app *App) GetSessionByToken(token string) (*model.Session, *model.AppError) { 17 | session, err := app.Srv().Store.Session().GetSessionByToken(token) 18 | if err != nil { 19 | return nil, model.NewAppErrorWithStatus("Couldn't find session by token", http.StatusNotFound) 20 | } 21 | return session, nil 22 | } 23 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/test/java/org/npathai/IdGenerationServiceStub.java: -------------------------------------------------------------------------------- 1 | package org.npathai; 2 | 3 | import io.micronaut.context.annotation.Replaces; 4 | import io.micronaut.context.annotation.Requires; 5 | import io.micronaut.context.env.Environment; 6 | import org.npathai.client.IdGenerationServiceClient; 7 | 8 | import javax.inject.Singleton; 9 | 10 | @Singleton 11 | @Requires(env = Environment.TEST) 12 | @Replaces(IdGenerationServiceClient.class) 13 | public class IdGenerationServiceStub implements IdGenerationServiceClient { 14 | 15 | private String id; 16 | 17 | @Override 18 | public String generateId() { 19 | return id; 20 | } 21 | 22 | public void setId(String id) { 23 | this.id = id; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /discourse/application/src/test/java/org/npathai/discourse/application/domain/users/UserRepositoryStub.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.domain.users; 2 | 3 | import java.util.Optional; 4 | 5 | public class UserRepositoryStub implements UserRepository { 6 | private String userId; 7 | private User user; 8 | 9 | @Override 10 | public void save(User user) { 11 | this.user = user; 12 | user.setUserId(userId); 13 | } 14 | 15 | @Override 16 | public Optional getById(String userId) { 17 | return Optional.of(user); 18 | } 19 | 20 | public String getUserId() { 21 | return userId; 22 | } 23 | 24 | public void setUserId(String userId) { 25 | this.userId = userId; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /chatter/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/gorilla/mux" 5 | "github.com/npathai/chatter/app" 6 | ) 7 | 8 | type Routes struct { 9 | Root *mux.Router 10 | Users *mux.Router 11 | Teams *mux.Router 12 | } 13 | 14 | type API struct { 15 | GetGlobalAppOptions app.AppOptionCreator 16 | BaseRoutes *Routes 17 | } 18 | 19 | func Init(globalOptionsFunc app.AppOptionCreator, root *mux.Router) *API { 20 | api := &API{ 21 | GetGlobalAppOptions: globalOptionsFunc, 22 | BaseRoutes: &Routes{}, 23 | } 24 | api.BaseRoutes.Root = root 25 | api.BaseRoutes.Users = api.BaseRoutes.Root.PathPrefix("/users").Subrouter() 26 | api.BaseRoutes.Teams = api.BaseRoutes.Root.PathPrefix("/teams").Subrouter() 27 | 28 | api.InitUser() 29 | api.InitTeam() 30 | return api 31 | } 32 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/show/ShowingSeat.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall.show; 2 | 3 | import org.npathai.cinemahall.seat.SeatId; 4 | import org.npathai.cinemahall.seat.SeatNo; 5 | import org.npathai.cinemahall.seat.SeatType; 6 | 7 | import java.math.BigDecimal; 8 | 9 | public class ShowingSeat { 10 | private long id; 11 | private SeatType seatType; 12 | private BigDecimal price; 13 | private SeatNo seatNo; 14 | 15 | public ShowingSeat(long id, SeatType seatType, BigDecimal price, SeatNo seatNo) { 16 | this.id = id; 17 | this.seatType = seatType; 18 | this.price = price; 19 | this.seatNo = seatNo; 20 | } 21 | 22 | public SeatId getId() { 23 | return new SeatId(id); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/util/thread/DefaultScheduledJobService.java: -------------------------------------------------------------------------------- 1 | package org.npathai.util.thread; 2 | 3 | import java.util.concurrent.Callable; 4 | import java.util.concurrent.Executors; 5 | import java.util.concurrent.Future; 6 | import java.util.concurrent.ScheduledExecutorService; 7 | 8 | public class DefaultScheduledJobService implements ScheduledJobService { 9 | 10 | private final ScheduledExecutorService scheduledExecutorService; 11 | 12 | public DefaultScheduledJobService() { 13 | scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); 14 | } 15 | 16 | @Override 17 | public Future submit(Callable callable) { 18 | return scheduledExecutorService.submit(callable); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tiny-url-generator/init/init.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE IF NOT EXISTS short_url_generator; 2 | 3 | ALTER USER 'root' IDENTIFIED BY 'unsecured'; 4 | GRANT ALL PRIVILEGES ON urldb.* TO 'root'@'%'; 5 | GRANT ALL PRIVILEGES ON urldb.* TO 'root'@'localhost'; 6 | 7 | USE short_url_generator; 8 | 9 | /* 10 | MySQL VARCHAR is not case-sensitive, so it cased integrity violation because it considered AAAAa and AAAAA as same 11 | values. That's why SET utf8 COLLATE utf8_bin has been added which makes it case insensitive. 12 | */ 13 | CREATE TABLE redirection( 14 | id VARCHAR(10) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 15 | long_url VARCHAR(500) NOT NULL, 16 | created_at TIMESTAMP(3) NOT NULL, 17 | expiry_at TIMESTAMP(3) NOT NULL, 18 | uid VARCHAR(50), 19 | PRIMARY KEY(id) 20 | ) -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/cache/InMemoryRedirectionCache.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cache; 2 | 3 | import org.npathai.model.Redirection; 4 | 5 | import java.util.HashMap; 6 | import java.util.Optional; 7 | 8 | public class InMemoryRedirectionCache implements RedirectionCache { 9 | private HashMap cache = new HashMap<>(); 10 | 11 | @Override 12 | public Optional getById(String id) { 13 | return Optional.ofNullable(cache.get(id)); 14 | } 15 | 16 | @Override 17 | public void put(Redirection redirection) { 18 | cache.put(redirection.id(), redirection); 19 | } 20 | 21 | @Override 22 | public void deleteById(String id) { 23 | cache.remove(id); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/api/ShortenRequest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.api; 2 | 3 | import org.npathai.model.UserInfo; 4 | 5 | import javax.annotation.Nullable; 6 | 7 | public class ShortenRequest { 8 | private String longUrl; 9 | private UserInfo userInfo; 10 | 11 | public String getLongUrl() { 12 | return longUrl; 13 | } 14 | 15 | public void setLongUrl(String longUrl) { 16 | this.longUrl = longUrl; 17 | } 18 | 19 | @Nullable 20 | public UserInfo getUserInfo() { 21 | return userInfo; 22 | } 23 | 24 | public void setUserInfo(UserInfo userInfo) { 25 | this.userInfo = userInfo; 26 | } 27 | 28 | public boolean isAnonymous() { 29 | return userInfo == null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/config/UrlLifetimeConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.npathai.config; 2 | 3 | import io.micronaut.context.annotation.ConfigurationProperties; 4 | 5 | @ConfigurationProperties("url-lifetime-in-secs") 6 | public class UrlLifetimeConfiguration { 7 | 8 | private String anonymous; 9 | private String authenticated; 10 | 11 | public String getAnonymous() { 12 | return anonymous; 13 | } 14 | 15 | public void setAnonymous(String anonymous) { 16 | this.anonymous = anonymous; 17 | } 18 | 19 | public String getAuthenticated() { 20 | return authenticated; 21 | } 22 | 23 | public void setAuthenticated(String authenticated) { 24 | this.authenticated = authenticated; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/test/java/org/npathai/model/RedirectionTest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.model; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.assertj.core.api.Assertions.assertThat; 6 | 7 | class RedirectionTest { 8 | 9 | @Test 10 | public void redirectionIsAnonymousWhenUserIsNotAssociatedWithIt() { 11 | Redirection redirection = new Redirection("AAAAA", "www.google.com", 0L, 0L); 12 | assertThat(redirection.isAnonymous()).isTrue(); 13 | } 14 | 15 | @Test 16 | public void redirectionIsNotAnonymousWhenUserIsAssociatedWithIt() { 17 | Redirection redirection = new Redirection("AAAAA", "www.google.com", 0L, 0L, 18 | "test"); 19 | assertThat(redirection.isAnonymous()).isFalse(); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /chatter/web/handlers.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import ( 4 | "github.com/npathai/chatter/app" 5 | "net/http" 6 | ) 7 | 8 | type Handler struct { 9 | GetGlobalAppOptions app.AppOptionCreator 10 | HandlerFunc func(*Context, http.ResponseWriter, *http.Request) 11 | RequireSession bool 12 | } 13 | 14 | func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 15 | ctx := &Context{} 16 | 17 | ctx.App = app.New(h.GetGlobalAppOptions()...) 18 | 19 | w.Header().Set("Content-Type", "application/json") 20 | 21 | token := app.ParseAuthTokenFromRequest(r) 22 | if len(token) != 0 { 23 | session, err := ctx.App.GetSessionByToken(token) 24 | if err != nil { 25 | ctx.Err = err 26 | } 27 | ctx.App.SetSession(session) 28 | } else { 29 | // Not allowed 30 | } 31 | 32 | h.HandlerFunc(ctx, w, r) 33 | } -------------------------------------------------------------------------------- /chatter/model/session.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Session struct { 4 | Id string `json:"id"` 5 | UserId string `json:"user_id"` 6 | Token string `json:"token"` 7 | Props map[string]string 8 | } 9 | 10 | func (session *Session) GenerateCSRF() string { 11 | token := NewId() 12 | session.AddProp("csrf", token) 13 | return token 14 | } 15 | 16 | func (session *Session) AddProp(key, value string) { 17 | if session.Props == nil { 18 | session.Props = make(map[string]string) 19 | } 20 | session.Props[key] = value 21 | } 22 | 23 | func (session *Session) PreSave() { 24 | if session.Id == "" { 25 | session.Id = NewId() 26 | } 27 | 28 | if session.Token == "" { 29 | session.Token = NewId() 30 | } 31 | 32 | if session.Props == nil { 33 | session.Props = make(map[string]string) 34 | } 35 | } 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/domain/users/UserService.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.domain.users; 2 | 3 | public class UserService { 4 | 5 | private UserRepository userRepository; 6 | 7 | public UserService(UserRepository userRepository) { 8 | this.userRepository = userRepository; 9 | } 10 | 11 | public User create(RegistrationData registrationData) throws UsernameAlreadyExistsException { 12 | User user = new User(); 13 | user.setUsername(registrationData.getUsername()); 14 | user.setEmail(registrationData.getEmail()); 15 | user.setName(registrationData.getName()); 16 | user.setPassword(registrationData.getPassword()); 17 | 18 | userRepository.save(user); 19 | 20 | return user; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/main/java/org/npathai/DomainBeanFactory.java: -------------------------------------------------------------------------------- 1 | package org.npathai; 2 | 3 | import io.micronaut.context.BeanContext; 4 | import io.micronaut.context.annotation.Factory; 5 | import org.npathai.dao.AnalyticsDao; 6 | import org.npathai.dao.InMemoryAnalyticsDao; 7 | import org.npathai.domain.AnalyticsService; 8 | 9 | import javax.inject.Inject; 10 | import javax.inject.Singleton; 11 | 12 | @Factory 13 | public class DomainBeanFactory { 14 | 15 | @Inject 16 | BeanContext beanContext; 17 | 18 | @Singleton 19 | public AnalyticsDao analyticsDao() { 20 | return new InMemoryAnalyticsDao(); 21 | } 22 | 23 | @Singleton 24 | public AnalyticsService analyticsService() { 25 | return new AnalyticsService(beanContext.getBean(AnalyticsDao.class)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | micronaut: 2 | application: 3 | name: analytics-service 4 | server: 5 | port: 8080 6 | cors: 7 | enabled: true 8 | netty: 9 | log-level: ERROR 10 | 11 | --- 12 | micronaut: 13 | security: 14 | enabled: true 15 | endpoints: 16 | login: 17 | enabled: false 18 | logout: 19 | enabled: false 20 | oauth: 21 | enabled: false 22 | token: 23 | jwt: 24 | enabled: true 25 | signatures: 26 | secret: 27 | generator: 28 | secret: "${JWT_GENERATOR_SIGNATURE_SECRET:ThisIsHighlySensitiveInformation}" 29 | --- 30 | consul: 31 | client: 32 | registration: 33 | enabled: true 34 | defaultZone: "${CONSUL_HOST:consul}:${CONSUL_PORT:8500}" 35 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/show/Show.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall.show; 2 | 3 | import org.npathai.cinemahall.auditorium.AuditoriumId; 4 | import org.npathai.cinemahall.seat.Seat; 5 | import org.npathai.movie.MovieId; 6 | 7 | import java.time.ZonedDateTime; 8 | import java.util.List; 9 | 10 | public class Show { 11 | private long id; 12 | private AuditoriumId auditoriumId; 13 | private List seats; 14 | private MovieId movieId; 15 | private ZonedDateTime startTime; 16 | 17 | public Show(long id, AuditoriumId auditoriumId, List seats, MovieId movieId, ZonedDateTime startTime) { 18 | this.id = id; 19 | this.auditoriumId = auditoriumId; 20 | this.seats = seats; 21 | this.movieId = movieId; 22 | this.startTime = startTime; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/usecases/BookSeat.java: -------------------------------------------------------------------------------- 1 | package org.npathai.usecases; 2 | 3 | import org.npathai.cinemahall.show.ShowingSeatDao; 4 | import org.npathai.cinemahall.seat.SeatId; 5 | 6 | import java.util.concurrent.CompletableFuture; 7 | 8 | public class BookSeat { 9 | 10 | private ShowingSeatDao showingSeatDao; 11 | 12 | public BookSeat(ShowingSeatDao showingSeatDao) { 13 | this.showingSeatDao = showingSeatDao; 14 | } 15 | 16 | public CompletableFuture execute(SeatId seatId) { 17 | showingSeatDao.reserveSeat(seatId); 18 | // Reserve seat if available 19 | // Make payment 20 | // Mark as confirmed if payment succeeds 21 | // If payment fails Seat remains reserved for few minutes and freed afterwards 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /chatter/model/user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | ) 7 | 8 | type User struct { 9 | Id string `json:"id"` 10 | Username string `json:"username"` 11 | Password string `json:"password"` 12 | AuthData string `json:"auth_data"` 13 | Email string `json:"email"` 14 | FirstName string `json:"first_name"` 15 | LastName string `json:"last_name"` 16 | Nickname string `json:"nickname"` 17 | } 18 | 19 | func UserFromJson(data io.Reader) *User { 20 | var user *User 21 | json.NewDecoder(data).Decode(&user) 22 | return user 23 | } 24 | 25 | func UserListToJson(users []*User) string { 26 | b, _ := json.Marshal(users) 27 | return string(b) 28 | } 29 | 30 | func (user *User) ToJson() string { 31 | bytes, _ := json.Marshal(user) 32 | return string(bytes) 33 | } -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/usecases/Shows.java: -------------------------------------------------------------------------------- 1 | package org.npathai.usecases; 2 | 3 | import org.npathai.cinemahall.CinemaHall; 4 | import org.npathai.cinemahall.show.Show; 5 | 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | 11 | public class Shows { 12 | 13 | private final Map> showsByCinemaHall = new HashMap<>(); 14 | 15 | public static Shows empty() { 16 | return new Shows(); 17 | } 18 | 19 | public void add(CinemaHall cinemaHall, List shows) { 20 | showsByCinemaHall.computeIfAbsent(cinemaHall, key -> new ArrayList<>()); 21 | showsByCinemaHall.get(cinemaHall).addAll(shows); 22 | } 23 | 24 | public Map> getAll() { 25 | return showsByCinemaHall; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tiny-url-generator/app/src/components/navbar/NavBar.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {Link} from 'react-router-dom' 3 | import {connect} from 'react-redux' 4 | 5 | class NavBar extends React.Component { 6 | 7 | render() { 8 | const status = this.props.isLoggedIn ?

{this.props.username} logged in

: null; 9 | const signIn = this.props.isLoggedIn ? null :
  • Sign In
  • 10 | 11 | return ( 12 |
    13 |
      14 |
    • Home
    • 15 | {status} 16 | {signIn} 17 |
    18 |
    19 | ); 20 | } 21 | } 22 | 23 | export default connect((state, props) => { 24 | return { 25 | isLoggedIn: state.auth.isLoggedIn, 26 | username: state.auth.username 27 | } 28 | })(NavBar) -------------------------------------------------------------------------------- /tiny-url-generator/load-test/src/loadTest/simulations/ParseJsonSimulation.scala: -------------------------------------------------------------------------------- 1 | package simulations 2 | 3 | import io.gatling.core.Predef._ 4 | import io.gatling.http.Predef._ 5 | 6 | class ParseJsonSimulation extends Simulation { 7 | 8 | val scn = scenario("JSON parsing") 9 | .exec( 10 | http("GET") 11 | .get("http://jsonplaceholder.typicode.com/comments") 12 | .check(jmesPath("[0].name").saveAs("commentName")) 13 | ) 14 | 15 | .exec( 16 | http("PATCH") 17 | .patch("http://jsonplaceholder.typicode.com/comments/1") 18 | .header("Content-Type", "application/json") 19 | .body(StringBody { session => 20 | val commentName = session("commentName").as[String] 21 | s"""{"name": "FOO ${commentName.reverse}"}""" 22 | }) 23 | ) 24 | 25 | 26 | setUp( 27 | scn.inject(atOnceUsers(1)) 28 | ) 29 | } 30 | -------------------------------------------------------------------------------- /chatter/store/inmemory/userstore.go: -------------------------------------------------------------------------------- 1 | package inmemory 2 | 3 | import ( 4 | "github.com/npathai/chatter/model" 5 | "github.com/npathai/chatter/store" 6 | ) 7 | 8 | type MemoryUserStore struct { 9 | users []*model.User 10 | } 11 | 12 | func (muStore *MemoryUserStore) GetAllUsers() ([]*model.User, error) { 13 | if muStore.users == nil { 14 | return []*model.User{}, nil 15 | } 16 | return muStore.users, nil 17 | } 18 | 19 | func (muStore *MemoryUserStore) Save(user *model.User) (*model.User, error) { 20 | user.Id = model.NewId() 21 | muStore.users = append(muStore.users, user) 22 | return user, nil 23 | } 24 | 25 | func (muStore *MemoryUserStore) Get(userId string) (*model.User, error) { 26 | // TODO Use efficient implementation using map 27 | for _, u := range muStore.users { 28 | if u.Id == userId { 29 | return u, nil 30 | } 31 | } 32 | return nil, store.NewErrNotFound("User", userId) 33 | } -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/java/org/npathai/config/MySqlDatasourceConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.npathai.config; 2 | 3 | import io.micronaut.context.annotation.ConfigurationProperties; 4 | 5 | @ConfigurationProperties("mysql") 6 | public class MySqlDatasourceConfiguration { 7 | 8 | private String url; 9 | private String user; 10 | private String password; 11 | 12 | public String getUrl() { 13 | return url; 14 | } 15 | 16 | public void setUrl(String url) { 17 | this.url = url; 18 | } 19 | 20 | public String getUser() { 21 | return user; 22 | } 23 | 24 | public void setUser(String user) { 25 | this.user = user; 26 | } 27 | 28 | public String getPassword() { 29 | return password; 30 | } 31 | 32 | public void setPassword(String password) { 33 | this.password = password; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/common/ValueObject.java: -------------------------------------------------------------------------------- 1 | package org.npathai.common; 2 | 3 | import org.apache.commons.lang.builder.EqualsBuilder; 4 | import org.apache.commons.lang.builder.HashCodeBuilder; 5 | 6 | import static java.util.Objects.requireNonNull; 7 | 8 | public abstract class ValueObject { 9 | 10 | private final T value; 11 | 12 | public ValueObject(T value) { 13 | this.value = requireNonNull(value, "ValueObject will not take a null reference"); 14 | } 15 | 16 | public T getValue() { 17 | return value; 18 | } 19 | 20 | @Override 21 | public boolean equals(Object o) { 22 | return EqualsBuilder.reflectionEquals(this, o); 23 | } 24 | 25 | @Override 26 | public int hashCode() { 27 | return HashCodeBuilder.reflectionHashCode(this); 28 | } 29 | 30 | @Override 31 | public String toString() { 32 | return value.toString(); 33 | } 34 | } -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/config/MySqlDatasourceConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.npathai.config; 2 | 3 | import io.micronaut.context.annotation.ConfigurationProperties; 4 | 5 | @ConfigurationProperties("mysql") 6 | public class MySqlDatasourceConfiguration { 7 | 8 | private String url; 9 | private String user; 10 | private String password; 11 | 12 | public String getUrl() { 13 | return url; 14 | } 15 | 16 | public void setUrl(String url) { 17 | this.url = url; 18 | } 19 | 20 | public String getUser() { 21 | return user; 22 | } 23 | 24 | public void setUser(String user) { 25 | this.user = user; 26 | } 27 | 28 | public String getPassword() { 29 | return password; 30 | } 31 | 32 | public void setPassword(String password) { 33 | this.password = password; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tiny-url-generator/app/src/reducers/error.js: -------------------------------------------------------------------------------- 1 | import {ActionTypes as types} from '../constants' 2 | 3 | const defaultValue = { 4 | errorMessage: '' 5 | } 6 | 7 | function error(state = defaultValue, action) { 8 | switch(action.type) { 9 | case types.RECEIVED_SIGN_IN_FAILURE: 10 | case types.RECEIVED_REDIRECTION_HISTORY_FAILURE: 11 | case types.RECEIVED_SHORTEN_REDIRECTION_FAILURE: 12 | return Object.assign({}, state, {errorMessage: action.data.errorMessage}) 13 | case types.RECEIVED_SIGN_IN_INVALID_ATTEMPT_FAILURE: 14 | case types.RECEIVED_SIGN_IN_SUCCESS: 15 | case types.RECEIVED_SHORTEN_REDIRECTION_SUCCESS: 16 | case types.RECEIVED_SHORTEN_REDIRECTION_SUCCESS: 17 | return Object.assign({}, state, {errorMessage: defaultValue.errorMessage}) 18 | default: 19 | return state 20 | } 21 | } 22 | 23 | export default error -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/java/org/npathai/DomainBeanFactory.java: -------------------------------------------------------------------------------- 1 | package org.npathai; 2 | 3 | import io.micrometer.core.instrument.MeterRegistry; 4 | import io.micronaut.context.BeanContext; 5 | import io.micronaut.context.annotation.Factory; 6 | import org.npathai.config.MySqlDatasourceConfiguration; 7 | import org.npathai.dao.MySqlUserDao; 8 | import org.npathai.dao.UserDao; 9 | 10 | import javax.inject.Inject; 11 | import javax.inject.Singleton; 12 | 13 | @Factory 14 | public class DomainBeanFactory { 15 | 16 | private BeanContext beanContext; 17 | 18 | public DomainBeanFactory(BeanContext beanContext) { 19 | this.beanContext = beanContext; 20 | } 21 | 22 | @Singleton 23 | public UserDao createMySqlUserDao() { 24 | return new MySqlUserDao(beanContext.getBean(MySqlDatasourceConfiguration.class), 25 | beanContext.getBean(MeterRegistry.class)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/controllers/topics/TopicController.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.controllers.topics; 2 | 3 | import io.micronaut.http.annotation.Controller; 4 | import io.micronaut.http.annotation.Get; 5 | import org.npathai.discourse.application.domain.topics.Topic; 6 | import org.npathai.discourse.application.domain.topics.TopicService; 7 | 8 | import java.util.List; 9 | 10 | @Controller("/topics") 11 | public class TopicController { 12 | 13 | private final TopicService topicService; 14 | 15 | public TopicController(TopicService topicService) { 16 | this.topicService = topicService; 17 | } 18 | 19 | @Get 20 | public List getAll() { 21 | // FIXME for now assuming there is single user. It should be id of user authenticated using current 22 | // auth token 23 | return topicService.getTopics(1); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/discovery/zookeeper/ZkInstance.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discovery.zookeeper; 2 | 3 | import org.apache.curator.x.discovery.ServiceInstance; 4 | import org.npathai.discovery.Instance; 5 | 6 | public class ZkInstance implements Instance { 7 | private ServiceInstance serviceInstance; 8 | 9 | ZkInstance(ServiceInstance serviceInstance) { 10 | this.serviceInstance = serviceInstance; 11 | } 12 | 13 | @Override 14 | public String name() { 15 | return serviceInstance.getName(); 16 | } 17 | 18 | @Override 19 | public String id() { 20 | return serviceInstance.getId(); 21 | } 22 | 23 | @Override 24 | public String address() { 25 | return serviceInstance.getAddress(); 26 | } 27 | 28 | @Override 29 | public Integer port() { 30 | return serviceInstance.getPort(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/test/java/org/npathai/domain/BatchedIdGeneratorTest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.domain; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.assertj.core.api.Assertions.assertThat; 7 | 8 | public class BatchedIdGeneratorTest { 9 | 10 | private BatchedIdGenerator batchIdGenerator; 11 | 12 | @BeforeEach 13 | public void initialize() { 14 | batchIdGenerator = new BatchedIdGenerator(Id.first()); 15 | } 16 | 17 | @Test 18 | public void validateIdFormat() { 19 | assertThat(batchIdGenerator.generate(1).ids().iterator().next()).hasSize(5); 20 | } 21 | 22 | @Test 23 | public void returnsBatchWithUniqueIds() { 24 | Batch batch = batchIdGenerator.generate(1000); 25 | 26 | // If all were unique then set should have all unique values 27 | assertThat(batch.ids()).hasSize(1000); 28 | } 29 | } -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/java/org/npathai/auth/CustomUserDetails.java: -------------------------------------------------------------------------------- 1 | package org.npathai.auth; 2 | 3 | import io.micronaut.security.authentication.UserDetails; 4 | 5 | import java.util.Collection; 6 | import java.util.Map; 7 | 8 | public class CustomUserDetails extends UserDetails { 9 | private String userId; 10 | 11 | public CustomUserDetails(String username, Collection roles, String userId) { 12 | super(username, roles); 13 | this.userId = userId; 14 | } 15 | 16 | public CustomUserDetails(String username, Collection roles, Map attributes, 17 | String userId) { 18 | super(username, roles, attributes); 19 | this.userId = userId; 20 | } 21 | 22 | public String getUserId() { 23 | return userId; 24 | } 25 | 26 | public void setUserId(String userId) { 27 | this.userId = userId; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/movie/MovieRepository.java: -------------------------------------------------------------------------------- 1 | package org.npathai.movie; 2 | 3 | import org.npathai.cinemahall.CinemaHall; 4 | import org.npathai.cinemahall.CinemaHallDao; 5 | import org.npathai.cinemahall.show.ShowDao; 6 | import org.npathai.city.CityId; 7 | 8 | import java.time.Clock; 9 | import java.util.Collections; 10 | import java.util.List; 11 | 12 | public class MovieRepository { 13 | 14 | private final CinemaHallDao cinemaHallDao; 15 | 16 | public MovieRepository(CinemaHallDao cinemaHallDao, ShowDao showDao, MovieDao movieDao, Clock fixedClock) { 17 | this.cinemaHallDao = cinemaHallDao; 18 | } 19 | 20 | public List getByCityId(CityId cityId) { 21 | List cinemaHalls = cinemaHallDao.getCinemaHalls(cityId); 22 | // cinemaHalls 23 | // .stream() 24 | // .map(cinemaHall -> showsDao.getShows(cinemaHall.getId())) 25 | 26 | return Collections.emptyList(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chatter/cmd/chatter/commands/server.go: -------------------------------------------------------------------------------- 1 | package commands 2 | 3 | import ( 4 | "github.com/npathai/chatter/api" 5 | "github.com/npathai/chatter/app" 6 | "github.com/spf13/cobra" 7 | "os" 8 | "os/signal" 9 | "syscall" 10 | ) 11 | 12 | var serverCmd = &cobra.Command { 13 | Use: "server", 14 | Short: "Run the Chatter server", 15 | RunE: serverCommandFunc, 16 | } 17 | 18 | func init() { 19 | RootCmd.AddCommand(serverCmd) 20 | RootCmd.RunE = serverCommandFunc 21 | } 22 | 23 | func serverCommandFunc(command *cobra.Command, args []string) error { 24 | runServer() 25 | return nil 26 | } 27 | 28 | func runServer() { 29 | s, err := app.NewServer() 30 | if err != nil { 31 | panic("couldn't start server") 32 | } 33 | defer s.Stop() 34 | 35 | api.Init(s.AppOptions, s.Router) 36 | s.Start() 37 | 38 | ch := make(chan os.Signal) 39 | signal.Notify(ch, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) 40 | 41 | // Block on channel read till there is interrupt/terminate signal 42 | <- ch 43 | } 44 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/main/java/org/npathai/model/AnalyticsInfo.java: -------------------------------------------------------------------------------- 1 | package org.npathai.model; 2 | 3 | public class AnalyticsInfo { 4 | private String id; 5 | private int clickCount; 6 | 7 | public AnalyticsInfo() { 8 | 9 | } 10 | 11 | public AnalyticsInfo(String id) { 12 | this.id = id; 13 | this.clickCount = 0; 14 | } 15 | 16 | public AnalyticsInfo(String id, int clickCount) { 17 | this.id = id; 18 | this.clickCount = clickCount; 19 | } 20 | 21 | public String getId() { 22 | return id; 23 | } 24 | 25 | public int getClickCount() { 26 | return clickCount; 27 | } 28 | 29 | public void setId(String id) { 30 | this.id = id; 31 | } 32 | 33 | public void setClickCount(int clickCount) { 34 | this.clickCount = clickCount; 35 | } 36 | 37 | public AnalyticsInfo incrementClick() { 38 | return new AnalyticsInfo(id, clickCount + 1); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/model/AnalyticsInfo.java: -------------------------------------------------------------------------------- 1 | package org.npathai.model; 2 | 3 | public class AnalyticsInfo { 4 | private String id; 5 | private int clickCount; 6 | 7 | public AnalyticsInfo() { 8 | 9 | } 10 | 11 | public AnalyticsInfo(String id) { 12 | this.id = id; 13 | this.clickCount = 0; 14 | } 15 | 16 | public AnalyticsInfo(String id, int clickCount) { 17 | this.id = id; 18 | this.clickCount = clickCount; 19 | } 20 | 21 | public String getId() { 22 | return id; 23 | } 24 | 25 | public int getClickCount() { 26 | return clickCount; 27 | } 28 | 29 | public void setId(String id) { 30 | this.id = id; 31 | } 32 | 33 | public void setClickCount(int clickCount) { 34 | this.clickCount = clickCount; 35 | } 36 | 37 | public AnalyticsInfo incrementClick() { 38 | return new AnalyticsInfo(id, clickCount + 1); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tiny-url-generator/app/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import {BrowserRouter as Router, Route} from 'react-router-dom' 4 | import {Provider} from 'react-redux' 5 | 6 | import './index.css'; 7 | import Home from "./components/home/Home"; 8 | import NavBar from "./components/navbar/NavBar"; 9 | 10 | import SignIn from "./components/login/SignIn"; 11 | import store from './store/store' 12 | 13 | class App extends React.Component { 14 | render() { 15 | return ( 16 |
    17 |
    18 | 19 | 20 | 21 | 22 | 23 |
    24 |
    25 | ); 26 | } 27 | } 28 | 29 | ReactDOM.render( 30 | , document.getElementById('root') 31 | ); 32 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/main/java/org/npathai/dao/InMemoryAnalyticsDao.java: -------------------------------------------------------------------------------- 1 | package org.npathai.dao; 2 | 3 | import org.npathai.model.AnalyticsInfo; 4 | 5 | import java.util.Map; 6 | import java.util.Optional; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | // TODO contemplate using time series database for providing day wise, month wise etc statistics 10 | public class InMemoryAnalyticsDao implements AnalyticsDao { 11 | 12 | private Map clickCount = new ConcurrentHashMap<>(); 13 | 14 | @Override 15 | public void incrementClick(String id) { 16 | clickCount.computeIfPresent(id, (key, oldVal) -> oldVal.incrementClick()); 17 | } 18 | 19 | @Override 20 | public Optional getById(String id) { 21 | return Optional.ofNullable(clickCount.get(id)); 22 | } 23 | 24 | @Override 25 | public void save(AnalyticsInfo analyticsInfo) { 26 | clickCount.put(analyticsInfo.getId(), analyticsInfo); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/main/java/org/npathai/model/UserInfo.java: -------------------------------------------------------------------------------- 1 | package org.npathai.model; 2 | 3 | import io.micronaut.security.authentication.Authentication; 4 | 5 | public class UserInfo { 6 | private final String uid; 7 | private final String username; 8 | 9 | public UserInfo(String uid, String username) { 10 | this.uid = uid; 11 | this.username = username; 12 | } 13 | 14 | public static UserInfo fromAuthentication(Authentication authentication) { 15 | UserInfo userInfo = new UserInfo( 16 | authentication.getAttributes().get("uid").toString(), 17 | authentication.getName()); 18 | return userInfo; 19 | } 20 | 21 | public String uid() { 22 | return uid; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "UserInfo{" + 28 | "uid='" + uid + '\'' + 29 | ", username='" + username + '\'' + 30 | '}'; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/model/UserInfo.java: -------------------------------------------------------------------------------- 1 | package org.npathai.model; 2 | 3 | import io.micronaut.security.authentication.Authentication; 4 | 5 | public class UserInfo { 6 | private final String uid; 7 | private final String username; 8 | 9 | public UserInfo(String uid, String username) { 10 | this.uid = uid; 11 | this.username = username; 12 | } 13 | 14 | public static UserInfo fromAuthentication(Authentication authentication) { 15 | UserInfo userInfo = new UserInfo( 16 | authentication.getAttributes().get("uid").toString(), 17 | authentication.getName()); 18 | return userInfo; 19 | } 20 | 21 | public String uid() { 22 | return uid; 23 | } 24 | 25 | @Override 26 | public String toString() { 27 | return "UserInfo{" + 28 | "uid='" + uid + '\'' + 29 | ", username='" + username + '\'' + 30 | '}'; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/test/java/org/npathai/api/DirectExecutorJobService.java: -------------------------------------------------------------------------------- 1 | package org.npathai.api; 2 | 3 | import com.google.common.util.concurrent.MoreExecutors; 4 | import io.micronaut.context.annotation.Replaces; 5 | import io.micronaut.context.annotation.Requires; 6 | import io.micronaut.context.env.Environment; 7 | import org.npathai.annotations.TestingUtil; 8 | import org.npathai.util.thread.ScheduledJobService; 9 | 10 | import java.util.concurrent.Callable; 11 | import java.util.concurrent.ExecutorService; 12 | import java.util.concurrent.Future; 13 | 14 | @Requires(env = Environment.TEST) 15 | @Replaces(ScheduledJobService.class) 16 | @TestingUtil 17 | @SuppressWarnings("unused") 18 | public class DirectExecutorJobService implements ScheduledJobService { 19 | private ExecutorService executorService = MoreExecutors.newDirectExecutorService(); 20 | 21 | @Override 22 | public Future submit(Callable callable) { 23 | return executorService.submit(callable); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/zookeeper/DefaultZkManagerFactory.java: -------------------------------------------------------------------------------- 1 | package org.npathai.zookeeper; 2 | 3 | import org.apache.curator.framework.CuratorFramework; 4 | import org.apache.curator.framework.CuratorFrameworkFactory; 5 | import org.apache.curator.retry.ExponentialBackoffRetry; 6 | import org.apache.logging.log4j.LogManager; 7 | import org.apache.logging.log4j.Logger; 8 | 9 | public class DefaultZkManagerFactory { 10 | private static final Logger LOG = LogManager.getLogger(DefaultZkManagerFactory.class); 11 | 12 | public DefaultZkManager createConnected(String connectionString) throws InterruptedException { 13 | CuratorFramework client = CuratorFrameworkFactory.newClient(connectionString, 14 | new ExponentialBackoffRetry(1000, 3)); 15 | LOG.info("Connecting to Zookeeper using connection string: {}", connectionString); 16 | client.start(); 17 | LOG.info("Connected to Zookeeper"); 18 | return new DefaultZkManager(client); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /chatter/store/inmemory/sessionstore.go: -------------------------------------------------------------------------------- 1 | package inmemory 2 | 3 | import ( 4 | "github.com/npathai/chatter/model" 5 | "github.com/npathai/chatter/store" 6 | ) 7 | 8 | type MemorySessionStore struct { 9 | sessions []*model.Session 10 | } 11 | 12 | func (msStore *MemorySessionStore) Save(session *model.Session) (*model.Session, error) { 13 | session.PreSave() 14 | msStore.sessions = append(msStore.sessions, session) 15 | return session, nil 16 | } 17 | 18 | func (msStore *MemorySessionStore) GetSessionById(sessionId string) (*model.Session, error) { 19 | // TODO Use efficient implementation using map 20 | for _, s := range msStore.sessions { 21 | if s.Id == sessionId { 22 | return s, nil 23 | } 24 | } 25 | return nil, store.NewErrNotFound("Session", sessionId) 26 | } 27 | 28 | func (msStore *MemorySessionStore) GetSessionByToken(token string) (*model.Session, error) { 29 | for _, s := range msStore.sessions { 30 | if s.Token == token { 31 | return s, nil; 32 | } 33 | } 34 | return nil, store.NewErrNotFound("Session", token) 35 | } -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/domain/users/RegistrationData.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.domain.users; 2 | 3 | public class RegistrationData { 4 | private String username; 5 | private String name; 6 | private String password; 7 | private String email; 8 | 9 | public String getUsername() { 10 | return username; 11 | } 12 | 13 | public String getName() { 14 | return name; 15 | } 16 | 17 | public String getPassword() { 18 | return password; 19 | } 20 | 21 | public String getEmail() { 22 | return email; 23 | } 24 | 25 | public void setUsername(String username) { 26 | this.username = username; 27 | } 28 | 29 | public void setName(String name) { 30 | this.name = name; 31 | } 32 | 33 | public void setPassword(String password) { 34 | this.password = password; 35 | } 36 | 37 | public void setEmail(String email) { 38 | this.email = email; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/main/java/org/npathai/domain/BatchedIdGenerator.java: -------------------------------------------------------------------------------- 1 | package org.npathai.domain; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | /** 7 | * Generates batch of ids 8 | */ 9 | public class BatchedIdGenerator { 10 | private Id current; 11 | 12 | public BatchedIdGenerator(Id current) { 13 | this.current = current; 14 | } 15 | 16 | public Batch generate(int batchSize) { 17 | Set ids = new HashSet<>(); 18 | for (int i = 0; i < batchSize; i++) { 19 | try { 20 | ids.add(getAndIncrementCurrentId()); 21 | } catch (IdExhaustedException e) { 22 | // TODO handle this logically 23 | e.printStackTrace(); 24 | } 25 | } 26 | return new Batch(ids); 27 | } 28 | 29 | private String getAndIncrementCurrentId() throws IdExhaustedException { 30 | String id = current.encode(); 31 | current = current.incrementAndGet(); 32 | return id; 33 | } 34 | } -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/domain/RedirectionHistoryService.java: -------------------------------------------------------------------------------- 1 | package org.npathai.domain; 2 | 3 | import org.npathai.dao.DataAccessException; 4 | import org.npathai.dao.RedirectionDao; 5 | import org.npathai.model.Redirection; 6 | import org.npathai.model.UserInfo; 7 | 8 | import javax.annotation.Nonnull; 9 | import java.time.Clock; 10 | import java.util.List; 11 | import java.util.stream.Collectors; 12 | 13 | public class RedirectionHistoryService { 14 | 15 | private final RedirectionDao redirectionDao; 16 | 17 | public RedirectionHistoryService(RedirectionDao redirectionDao) { 18 | this.redirectionDao = redirectionDao; 19 | } 20 | 21 | public List getRedirectionHistory(@Nonnull UserInfo userInfo) throws DataAccessException { 22 | return redirectionDao.getAllByUser(userInfo.uid()) 23 | .stream() 24 | .filter(redirection -> !redirection.isExpired(Clock.systemDefaultZone())) 25 | .collect(Collectors.toList()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/domain/users/InMemoryUserRepository.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.domain.users; 2 | 3 | import org.npathai.discourse.application.common.IdGenerator; 4 | 5 | import javax.inject.Singleton; 6 | import java.util.Map; 7 | import java.util.Optional; 8 | import java.util.concurrent.ConcurrentHashMap; 9 | 10 | @Singleton 11 | public class InMemoryUserRepository implements UserRepository { 12 | 13 | private Map userById; 14 | private IdGenerator idGenerator; 15 | 16 | public InMemoryUserRepository(IdGenerator idGenerator) { 17 | this.idGenerator = idGenerator; 18 | this.userById = new ConcurrentHashMap<>(); 19 | } 20 | 21 | @Override 22 | public void save(User user) { 23 | user.setUserId(idGenerator.nextId()); 24 | 25 | userById.put(user.getUserId(), user); 26 | } 27 | 28 | @Override 29 | public Optional getById(String userId) { 30 | return Optional.ofNullable(userById.get(userId)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/dao/InMemoryRedirectionDao.java: -------------------------------------------------------------------------------- 1 | package org.npathai.dao; 2 | 3 | import org.npathai.model.Redirection; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Optional; 9 | import java.util.concurrent.ConcurrentHashMap; 10 | 11 | public class InMemoryRedirectionDao implements RedirectionDao { 12 | private Map idToRedirection = new ConcurrentHashMap<>(); 13 | 14 | @Override 15 | public void save(Redirection redirection) { 16 | idToRedirection.put(redirection.id(), redirection); 17 | } 18 | 19 | @Override 20 | public Optional getById(String id) { 21 | return Optional.ofNullable(idToRedirection.get(id)); 22 | } 23 | 24 | @Override 25 | public void deleteById(String id) { 26 | idToRedirection.remove(id); 27 | } 28 | 29 | @Override 30 | public List getAllByUser(String uid) { 31 | return new ArrayList<>(idToRedirection.values()); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/util/time/MutableClock.java: -------------------------------------------------------------------------------- 1 | package org.npathai.util.time; 2 | 3 | import org.npathai.annotations.TestingUtil; 4 | 5 | import java.time.Clock; 6 | import java.time.Instant; 7 | import java.time.ZoneId; 8 | import java.time.temporal.TemporalAmount; 9 | 10 | @TestingUtil 11 | public class MutableClock extends Clock { 12 | 13 | private Instant instant; 14 | private final ZoneId zoneId; 15 | 16 | public MutableClock() { 17 | this.instant = Instant.now(); 18 | this.zoneId = ZoneId.systemDefault(); 19 | } 20 | 21 | @Override 22 | public ZoneId getZone() { 23 | return zoneId; 24 | } 25 | 26 | @Override 27 | public Clock withZone(ZoneId zoneId) { 28 | throw new UnsupportedOperationException(); 29 | } 30 | 31 | @Override 32 | public Instant instant() { 33 | return instant; 34 | } 35 | 36 | /* 37 | Mutator methods 38 | */ 39 | public void advanceBy(TemporalAmount amount) { 40 | instant = instant.plus(amount); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /tiny-url-generator/app/src/components/home/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {connect} from 'react-redux' 3 | import Shortener from "../short-url-generator/Shortener"; 4 | import RedirectionHistory from "../redirection-history/RedirectionHistory"; 5 | 6 | class Home extends React.Component { 7 | render() { 8 | let errorMsg = null 9 | if (this.props.errorMessage) { 10 | errorMsg =
    {this.props.errorMessage}
    11 | } 12 | 13 | const isLoggedIn = this.props.isLoggedIn 14 | const redirectionHistoryComponent = isLoggedIn 15 | ? 16 | : null 17 | 18 | return ( 19 |
    20 | {errorMsg} 21 | 22 | {redirectionHistoryComponent} 23 |
    24 | ); 25 | } 26 | } 27 | 28 | export default connect((state, props) => { 29 | return { 30 | isLoggedIn: state.auth.isLoggedIn, 31 | errorMessage: state.error.errorMessage 32 | } 33 | })(Home) -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/test/java/org/npathai/util/NullSafeTest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.util; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Nested; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static org.mockito.Mockito.*; 8 | 9 | class NullSafeTest { 10 | 11 | @Nested 12 | class IfNotNull { 13 | 14 | private ThrowingConsumer mockConsumer; 15 | 16 | @BeforeEach 17 | public void initialize() { 18 | mockConsumer = mock(ThrowingConsumer.class); 19 | } 20 | 21 | @Test 22 | public void doesNotCallConsumerWhenObjectPassedIsNull() throws Exception { 23 | NullSafe.ifNotNull(null, mockConsumer); 24 | 25 | verifyZeroInteractions(mockConsumer); 26 | } 27 | 28 | @Test 29 | public void callsConsumerWhenObjectPassedInIsNotNull() throws Exception { 30 | String str = "Nulls suck"; 31 | 32 | NullSafe.ifNotNull(str, mockConsumer); 33 | 34 | verify(mockConsumer).accept(same(str)); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /tiny-url-generator/prometheus-queries.md: -------------------------------------------------------------------------------- 1 | ### Redirection Stats Panel 2 | 3 | Cache hit ratio (Active) 4 | rate(url_redirection_responses_total{cache_hit="true", redirection_status="active"}[10s]) / rate(url_redirection_responses_total[10s]) 5 | 6 | {{cache_hit}} ratio ({{redirection_status}}) 7 | rate(url_redirection_responses_total{cache_hit = "true", redirection_status="active"}[10s]) / rate(url_redirection_responses_total[10s]) 8 | 9 | Cache miss ratio (Expired) 10 | rate(url_redirection_responses_total{cache_hit = "false", redirection_status="expired"}[10s]) / rate(url_redirection_responses_total[10s]) 11 | 12 | 13 | ### Http Request Rates Panel 14 | 15 | Id generation 16 | irate(http_requests_total{api="/generate"}[10s]) 17 | 18 | Shortening 19 | irate(http_requests_total{api="/shorten"}[10s]) 20 | 21 | Redirection 22 | irate(http_requests_total{api="/{id}"}[10s]) 23 | 24 | 25 | ### Http Error Ratio 26 | rate(http_responses_total{http_status=~"4..|5.."}[1m]) / rate(http_responses_total[1m]) 27 | 28 | Http Success Ratio 29 | rate(http_responses_total{http_status!~"4..|5.."}[1m]) / rate(http_responses_total[1m]) -------------------------------------------------------------------------------- /chatter/store/store.go: -------------------------------------------------------------------------------- 1 | package store 2 | 3 | import ( 4 | "github.com/npathai/chatter/model" 5 | "github.com/npathai/chatter/store/inmemory" 6 | ) 7 | 8 | type Store interface { 9 | User() UserStore 10 | Session() SessionStore 11 | } 12 | 13 | type UserStore interface { 14 | Save(user *model.User) (*model.User, error) 15 | GetAllUsers() ([]*model.User, error) 16 | Get(userId string) (*model.User, error) 17 | } 18 | 19 | type SessionStore interface { 20 | Save(session *model.Session) (*model.Session, error) 21 | GetSessionById(sessionId string) (*model.Session, error) 22 | GetSessionByToken(token string) (*model.Session, error) 23 | } 24 | 25 | type LocalStore struct { 26 | uStore inmemory.MemoryUserStore 27 | sStore inmemory.MemorySessionStore 28 | } 29 | 30 | func (store *LocalStore) User() UserStore { 31 | return &store.uStore 32 | } 33 | 34 | func (store *LocalStore) Session() SessionStore { 35 | return &store.sStore 36 | } 37 | 38 | func NewStore() *LocalStore { 39 | store := &LocalStore{} 40 | store.uStore = inmemory.MemoryUserStore{} 41 | store.sStore = inmemory.MemorySessionStore{} 42 | return store 43 | } -------------------------------------------------------------------------------- /tiny-url-generator/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "keymirror": "^0.1.1", 10 | "react": "^16.13.1", 11 | "react-dom": "^16.13.1", 12 | "react-redux": "^7.2.0", 13 | "react-router-dom": "latest", 14 | "react-scripts": "3.4.1", 15 | "redux": "^4.0.5", 16 | "redux-thunk": "^2.3.0" 17 | }, 18 | "scripts": { 19 | "start": "react-scripts start", 20 | "build": "react-scripts build", 21 | "test": "react-scripts test", 22 | "eject": "react-scripts eject" 23 | }, 24 | "eslintConfig": { 25 | "extends": "react-app" 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | }, 39 | "devDependencies": { 40 | "redux-logger": "^3.0.6" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/controllers/users/UserController.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.controllers.users; 2 | 3 | import io.micronaut.http.HttpResponse; 4 | import io.micronaut.http.annotation.Controller; 5 | import io.micronaut.http.annotation.Post; 6 | import org.npathai.discourse.application.domain.users.RegistrationData; 7 | import org.npathai.discourse.application.domain.users.User; 8 | import org.npathai.discourse.application.domain.users.UserService; 9 | import org.npathai.discourse.application.domain.users.UsernameAlreadyExistsException; 10 | 11 | @Controller("/users") 12 | public class UserController { 13 | 14 | private UserService userService; 15 | 16 | UserController(UserService userService) { 17 | this.userService = userService; 18 | } 19 | 20 | @Post 21 | public HttpResponse create(RegistrationData registrationData) { 22 | try { 23 | return HttpResponse.ok(userService.create(registrationData)); 24 | } catch (UsernameAlreadyExistsException e) { 25 | return HttpResponse.badRequest(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /bookmyshow/application/src/test/java/org/npathai/usecases/GetCitiesTest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.usecases; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.junit.jupiter.api.extension.ExtendWith; 5 | import org.mockito.Mock; 6 | import org.mockito.junit.jupiter.MockitoExtension; 7 | import org.npathai.city.City; 8 | import org.npathai.city.CityDao; 9 | import org.npathai.city.CityId; 10 | 11 | import java.util.List; 12 | 13 | import static org.assertj.core.api.Assertions.assertThat; 14 | import static org.mockito.Mockito.when; 15 | 16 | @ExtendWith(MockitoExtension.class) 17 | class GetCitiesTest { 18 | 19 | @Mock 20 | CityDao cityDao; 21 | 22 | @Test 23 | public void returnsAllRegisteredCities() { 24 | List cities = List.of(city(1, "city1"), city(2, "city2")); 25 | when(cityDao.getCities()).thenReturn(cities); 26 | GetCities getCities = new GetCities(cityDao); 27 | List answer = getCities.execute(); 28 | assertThat(answer).containsExactlyElementsOf(cities); 29 | } 30 | 31 | private City city(long id, String name) { 32 | return new City(new CityId(id), name); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chatter/app/server.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "github.com/gorilla/mux" 7 | "github.com/npathai/chatter/store" 8 | "net/http" 9 | "time" 10 | ) 11 | 12 | type Server struct { 13 | Router *mux.Router 14 | Server *http.Server 15 | Store *store.LocalStore 16 | } 17 | 18 | func NewServer() (*Server, error) { 19 | Srv := &Server{} 20 | Srv.Router = mux.NewRouter() 21 | Srv.Store = store.NewStore() 22 | return Srv, nil 23 | } 24 | 25 | func (Srv *Server) Start() { 26 | fmt.Println("Starting server") 27 | Srv.Server = &http.Server{ 28 | Addr: "127.0.0.1:8080", 29 | Handler: Srv.Router, 30 | WriteTimeout: 15 * time.Second, 31 | ReadTimeout: 15 * time.Second, 32 | } 33 | if err := Srv.Server.ListenAndServe(); err != nil { 34 | panic("Couldn't start web server") 35 | } 36 | } 37 | 38 | func (Srv *Server) Stop() { 39 | if err := Srv.Server.Shutdown(context.Background()); err != nil { 40 | fmt.Printf("Error in stopping server: %v\n", err) 41 | } else { 42 | fmt.Println("Server stopped") 43 | } 44 | } 45 | 46 | func (s *Server) AppOptions() []AppOption { 47 | return []AppOption{ 48 | ServerConnector(s), 49 | } 50 | } -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/DomainBeanFactory.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application; 2 | 3 | import io.micronaut.context.BeanContext; 4 | import io.micronaut.context.annotation.Bean; 5 | import io.micronaut.context.annotation.Factory; 6 | import org.npathai.discourse.application.common.IdGenerator; 7 | import org.npathai.discourse.application.domain.users.InMemoryUserRepository; 8 | import org.npathai.discourse.application.domain.users.UserRepository; 9 | import org.npathai.discourse.application.domain.users.UserService; 10 | 11 | import javax.inject.Inject; 12 | 13 | @Factory 14 | public class DomainBeanFactory { 15 | 16 | @Inject 17 | BeanContext beanContext; 18 | 19 | @Bean 20 | public IdGenerator createIdGenerator() { 21 | return new IdGenerator(); 22 | } 23 | 24 | @Bean 25 | public UserRepository createUserRepository() { 26 | return new InMemoryUserRepository(beanContext.getBean(IdGenerator.class)); 27 | } 28 | 29 | @Bean 30 | public UserService createUserService() { 31 | return new UserService(beanContext.getBean(UserRepository.class)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tiny-url-generator/app/src/reducers/redirection.js: -------------------------------------------------------------------------------- 1 | import {ActionTypes as types} from "../constants"; 2 | 3 | const defaultValue = { 4 | loaded: false, 5 | redirections: [], 6 | newRedirection: '', 7 | longUrl: '', 8 | shortUrl: 'Short Url will appear here!', 9 | isCopied: false 10 | } 11 | 12 | function redirection(state = defaultValue, action) { 13 | switch (action.type) { 14 | case types.RECEIVED_REDIRECTION_HISTORY_SUCCESS: 15 | return Object.assign({}, state, { 16 | loaded: true, 17 | redirections: action.data 18 | }) 19 | case types.CHANGE_LONG_URL: 20 | return Object.assign({}, state, { 21 | longUrl: action.data.newLongUrl, isCopied: false, shortUrl: defaultValue.shortUrl 22 | }) 23 | case types.CHANGE_COPIED: 24 | return Object.assign({}, state, {isCopied: action.data.isCopied}) 25 | case types.RECEIVED_SHORTEN_REDIRECTION_SUCCESS: 26 | return Object.assign({}, state, {shortUrl: "http://localhost:4000/" + action.data.id}) 27 | default: 28 | return state 29 | } 30 | } 31 | 32 | export default redirection -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | micronaut: 2 | application: 3 | name: user-service 4 | server: 5 | port: 8080 6 | cors: 7 | enabled: true 8 | netty: 9 | log-level: ERROR 10 | 11 | --- 12 | micronaut: 13 | security: 14 | enabled: true 15 | endpoints: 16 | login: 17 | enabled: true 18 | logout: 19 | enabled: true 20 | oauth: 21 | enabled: true 22 | token: 23 | jwt: 24 | enabled: true 25 | signatures: 26 | secret: 27 | generator: 28 | secret: "${JWT_GENERATOR_SIGNATURE_SECRET:ThisIsHighlySensitiveInformation}" 29 | --- 30 | endpoints: 31 | prometheus: 32 | sensitive: false 33 | --- 34 | micronaut: 35 | metrics: 36 | enabled: true 37 | export: 38 | prometheus: 39 | enabled: true 40 | step: PT1M 41 | descriptions: true 42 | --- 43 | 44 | mysql: 45 | url: jdbc:mysql://userdb:3306/user_db 46 | user: root 47 | password: unsecured 48 | 49 | --- 50 | consul: 51 | client: 52 | registration: 53 | enabled: true 54 | defaultZone: "${CONSUL_HOST:consul}:${CONSUL_PORT:8500}" 55 | -------------------------------------------------------------------------------- /tiny-url-generator/README.md: -------------------------------------------------------------------------------- 1 | ## URL Shortening Service 2 | 3 | Starting the service is a single step process. 4 | 5 | ### How to start/stop the application stack 6 | 7 | ``` 8 | # Start the application stack 9 | gradlew composeUp 10 | 11 | # Stop the application stack 12 | gradlew composeDown 13 | ```` 14 | For easy way to view the logs and login to containers use Docker dashboard on Windows OS 15 | 16 | ### Start Shortening the URLs 17 | 18 | 1) Start the application stack using `gradlew composeUp` 19 | 2) Switch to `app` and execute the `npm install` followed by `npm start`, this will start the not so feature rich React app 20 | - Working on creating image for this as well, so can be part of docker compose stack 21 | 3) Shorten the URLs at `http://localhost:3000` :scissors: 22 | - The system has a user `root:root` created, which can be utilized for using authenticated user features 23 | 4) Stop the application stack and react app when done 24 | 25 | #### Component Urls 26 | 1) UI Application - `http://locahost:3000` 27 | 2) Nginx loadbalancer - `http://localhost:4000` 28 | 3) Consul - `http://localhost:8500` 29 | 4) Prometheus - `http://localhost:9090` 30 | 5) Grafana - `http://localhost:8081` 31 | -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/domain/users/User.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.domain.users; 2 | 3 | public class User { 4 | private String userId; 5 | private String username; 6 | private String name; 7 | private String password; 8 | private String email; 9 | 10 | public String getUsername() { 11 | return username; 12 | } 13 | 14 | public void setUsername(String username) { 15 | this.username = username; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public String getUserId() { 23 | return userId; 24 | } 25 | 26 | public void setName(String name) { 27 | this.name = name; 28 | } 29 | 30 | public String getPassword() { 31 | return password; 32 | } 33 | 34 | public void setPassword(String password) { 35 | this.password = password; 36 | } 37 | 38 | public String getEmail() { 39 | return email; 40 | } 41 | 42 | public void setEmail(String email) { 43 | this.email = email; 44 | } 45 | 46 | public void setUserId(String userId) { 47 | this.userId = userId; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/java/org/npathai/model/User.java: -------------------------------------------------------------------------------- 1 | package org.npathai.model; 2 | 3 | public class User { 4 | private String id; 5 | private String username; 6 | private String password; 7 | private String email; 8 | 9 | public String getId() { 10 | return id; 11 | } 12 | 13 | public void setId(String id) { 14 | this.id = id; 15 | } 16 | 17 | public String getUsername() { 18 | return username; 19 | } 20 | 21 | public void setUsername(String username) { 22 | this.username = username; 23 | } 24 | 25 | public String getPassword() { 26 | return password; 27 | } 28 | 29 | public void setPassword(String password) { 30 | this.password = password; 31 | } 32 | 33 | public String getEmail() { 34 | return email; 35 | } 36 | 37 | public void setEmail(String email) { 38 | this.email = email; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "User{" + 44 | "id='" + id + '\'' + 45 | ", username='" + username + '\'' + 46 | ", email='" + email + '\'' + 47 | '}'; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/zookeeper/TestingZkManager.java: -------------------------------------------------------------------------------- 1 | package org.npathai.zookeeper; 2 | 3 | import org.apache.curator.framework.CuratorFramework; 4 | import org.apache.zookeeper.data.Stat; 5 | import org.npathai.annotations.TestingUtil; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import java.util.concurrent.Callable; 10 | 11 | @TestingUtil 12 | public class TestingZkManager implements ZkManager { 13 | 14 | private Map data = new HashMap<>(); 15 | 16 | @Override 17 | public Stat createIfAbsent(String path) throws Exception { 18 | return null; 19 | } 20 | 21 | @Override 22 | public Callable withinLock(String lockPath, Callable callable) { 23 | return () -> callable.call(); 24 | } 25 | 26 | 27 | @Override 28 | public byte[] getData(String path) throws Exception { 29 | return data.get(path); 30 | } 31 | 32 | @Override 33 | public void setData(String path, byte[] bytes) throws Exception { 34 | data.put(path, bytes); 35 | } 36 | 37 | @Override 38 | public CuratorFramework client() { 39 | return null; 40 | } 41 | 42 | @Override 43 | public void stop() throws Exception { 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /chatter/app/user.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/npathai/chatter/model" 5 | "net/http" 6 | ) 7 | 8 | func (app *App) CreateUser(user *model.User) (*model.User, *model.AppError) { 9 | err := app.validateUser(user) 10 | if err != nil { 11 | return nil, model.NewAppErrorWithStatus("Invalid user", http.StatusBadRequest) 12 | } 13 | rUser, err := app.Srv().Store.User().Save(user) 14 | if err != nil { 15 | return nil, model.NewAppErrorWithStatus("Error creating the user", http.StatusInternalServerError) 16 | } 17 | 18 | return rUser, nil 19 | } 20 | 21 | func (app *App) GetAllUsers() ([]*model.User, *model.AppError) { 22 | users, err := app.Srv().Store.User().GetAllUsers() 23 | if err != nil { 24 | return nil, model.NewAppError("Cannot retrieve users") 25 | } 26 | return users, nil 27 | } 28 | 29 | func (app *App) validateUser(user *model.User) error { 30 | // TODO add validation for user 31 | return nil 32 | } 33 | 34 | func (app *App) GetUser(userId string) (*model.User, *model.AppError) { 35 | user, err := app.Srv().Store.User().Get(userId) 36 | if err != nil { 37 | // TODO if error is missing account then return NOT FOUND else return INTERNAL ERROR 38 | return nil, model.NewAppErrorWithStatus("Account not found", http.StatusNotFound) 39 | } 40 | return user, nil 41 | } -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/cinemahall/CinemaHall.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall; 2 | 3 | import org.npathai.cinemahall.auditorium.Auditorium; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Objects; 8 | 9 | public class CinemaHall { 10 | private CinemaHallId id; 11 | private String name; 12 | private Address address; 13 | private Rating rating; 14 | private List audis = new ArrayList<>(); 15 | 16 | public CinemaHall(CinemaHallId id, String name, Address address, Rating rating, List audis) { 17 | this.id = id; 18 | this.name = name; 19 | this.address = address; 20 | this.rating = rating; 21 | this.audis = audis; 22 | } 23 | 24 | public CinemaHallId getId() { 25 | return id; 26 | } 27 | 28 | public List getAudis() { 29 | return audis; 30 | } 31 | 32 | @Override 33 | public boolean equals(Object o) { 34 | if (this == o) return true; 35 | if (o == null || getClass() != o.getClass()) return false; 36 | CinemaHall that = (CinemaHall) o; 37 | return id == that.id; 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Objects.hash(id); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /chatter/model/team.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "io" 6 | ) 7 | 8 | type Team struct { 9 | Id string `json:"id"` 10 | Name string `json:"name"` 11 | Domain string `json:"domain"` 12 | Email string `json:"email"` 13 | CompanyName string `json:"company_name"` 14 | } 15 | 16 | func TeamFromJson(data io.Reader) *Team { 17 | var o *Team 18 | json.NewDecoder(data).Decode(&o) 19 | return o 20 | } 21 | 22 | func (o *Team) ToJson() string { 23 | b, _ := json.Marshal(o) 24 | return string(b) 25 | } 26 | 27 | func (team *Team) PreSave() { 28 | if team.Id == "" { 29 | team.Id = NewId() 30 | } 31 | } 32 | 33 | func (team *Team) IsValid() *AppError { 34 | if len(team.Id) != 26 { 35 | return NewAppError("Invalid Id") 36 | } 37 | 38 | if len(team.Email) > 128 { 39 | return NewAppError("Invalid email") 40 | } 41 | 42 | if !IsValidEmail(team.Email) { 43 | return NewAppError("Invalid email") 44 | } 45 | 46 | if len(team.Name) > 64 { 47 | return NewAppError("Invalid name") 48 | } 49 | 50 | if len(team.Domain) > 64 { 51 | return NewAppError("Invalid domain") 52 | } 53 | 54 | if !IsValidDomain(team.Domain) { 55 | return NewAppError("Invalid domain") 56 | } 57 | 58 | if len(team.CompanyName) > 64 { 59 | return NewAppError("Invalid company name") 60 | } 61 | 62 | return nil 63 | } 64 | 65 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | // https://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt 5 | compile group: 'com.nimbusds', name: 'nimbus-jose-jwt', version: '8.2' 6 | 7 | testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.0") 8 | testImplementation("org.junit.jupiter:junit-jupiter-params:5.6.0") 9 | 10 | testRuntime("org.junit.platform:junit-platform-launcher:1.6.0") 11 | testRuntime("org.junit.jupiter:junit-jupiter-engine:5.6.0") 12 | // https://mvnrepository.com/artifact/org.junit.vintage/junit-vintage-engine 13 | testRuntime group: 'org.junit.vintage', name: 'junit-vintage-engine', version: '5.6.0' 14 | 15 | // https://mvnrepository.com/artifact/junit/junit 16 | testCompile group: 'junit', name: 'junit', version: '4.13' 17 | testCompile group: 'org.mockito', name: 'mockito-core', version: '1.10.19' 18 | testImplementation group: 'org.assertj', name: 'assertj-core', version: '3.15.0' 19 | // https://mvnrepository.com/artifact/io.rest-assured/rest-assured 20 | testCompile group: 'io.rest-assured', name: 'rest-assured', version: '3.0.0' 21 | implementation 'junit:junit:4.12' 22 | // https://mvnrepository.com/artifact/pl.pragmatists/JUnitParams 23 | testCompile group: 'pl.pragmatists', name: 'JUnitParams', version: '1.1.1' 24 | } -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/main/java/org/npathai/metrics/ServiceTags.java: -------------------------------------------------------------------------------- 1 | package org.npathai.metrics; 2 | 3 | import io.micrometer.core.instrument.Tag; 4 | import io.micrometer.core.instrument.Tags; 5 | import io.micronaut.http.HttpMethod; 6 | import io.micronaut.http.HttpStatus; 7 | 8 | public class ServiceTags { 9 | 10 | public static Tags httpApiTags(String serviceName, String serviceModule, String api, HttpMethod httpMethod) { 11 | return Tags.of(Tag.of("service", serviceName), Tag.of("service.module", serviceModule), 12 | Tag.of("api", api), 13 | Tag.of("http.method", httpMethod.name())); 14 | } 15 | 16 | public static Tags httpOkStatusTags(Tags commonTags) { 17 | return commonTags.and(ServiceTags.httpStatusTags(commonTags, HttpStatus.OK)); 18 | } 19 | 20 | public static Tags httpInternalErrorStatusTags(Tags commonTags) { 21 | return commonTags.and(ServiceTags.httpStatusTags(commonTags, HttpStatus.INTERNAL_SERVER_ERROR)); 22 | } 23 | 24 | public static Tags httpNotFoundStatusTags(Tags commonTags) { 25 | return commonTags.and(ServiceTags.httpStatusTags(commonTags, HttpStatus.INTERNAL_SERVER_ERROR)); 26 | } 27 | 28 | public static Tags httpStatusTags(Tags commonTags, HttpStatus httpStatus) { 29 | return commonTags.and(Tag.of("http.status", String.valueOf(httpStatus.getCode()))); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/test/java/org/npathai/api/IdGeneratorAPITest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.api; 2 | 3 | import io.micrometer.core.instrument.MeterRegistry; 4 | import io.micronaut.test.annotation.MicronautTest; 5 | import io.micronaut.test.annotation.MockBean; 6 | import org.assertj.core.api.Assertions; 7 | import org.junit.jupiter.api.Test; 8 | import org.mockito.Mockito; 9 | import org.npathai.annotations.ApiTest; 10 | import org.npathai.domain.Id; 11 | import org.npathai.zookeeper.TestingZkManager; 12 | import org.npathai.zookeeper.ZkManager; 13 | 14 | import javax.inject.Inject; 15 | 16 | @ApiTest 17 | @MicronautTest 18 | class IdGeneratorAPITest { 19 | 20 | private static final String NEXT_ID_ZNODE = "/next-id"; 21 | 22 | @Inject 23 | ZkManager testingZkManager; 24 | 25 | @Inject 26 | IdGeneratorClient client; 27 | 28 | @Test 29 | public void returnsIdOfLengthFiveInTextPlainContentType() { 30 | String generatedId = client.generate(); 31 | Assertions.assertThat(generatedId) 32 | .isNotNull() 33 | .hasSize(5); 34 | } 35 | 36 | @MockBean(ZkManager.class) 37 | public ZkManager createTestingZkManager() throws Exception { 38 | TestingZkManager testingZkManager = new TestingZkManager(); 39 | testingZkManager.setData(NEXT_ID_ZNODE, Id.first().encode().getBytes()); 40 | return testingZkManager; 41 | } 42 | } -------------------------------------------------------------------------------- /bookmyshow/application/src/test/java/org/npathai/cinemahall/show/ShowBuilder.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall.show; 2 | 3 | import org.npathai.cinemahall.CinemaHallBuilder; 4 | import org.npathai.cinemahall.auditorium.AuditoriumId; 5 | import org.npathai.cinemahall.seat.Seat; 6 | import org.npathai.cinemahall.seat.SeatNo; 7 | import org.npathai.cinemahall.seat.SeatType; 8 | import org.npathai.movie.MovieId; 9 | 10 | import java.math.BigDecimal; 11 | import java.time.ZonedDateTime; 12 | import java.util.List; 13 | 14 | public class ShowBuilder { 15 | private long id; 16 | private AuditoriumId auditoriumId; 17 | private List seats = List.of( 18 | new Seat(1L, SeatType.DELUX, BigDecimal.TEN, new SeatNo()), 19 | new Seat(2L, SeatType.ECONOMY, BigDecimal.ONE, new SeatNo()) 20 | ); 21 | private MovieId movieId; 22 | private ZonedDateTime startTime = ZonedDateTime.now(); 23 | 24 | public static ShowBuilder aShow() { 25 | return new ShowBuilder(); 26 | } 27 | 28 | public ShowBuilder withAuditoriumId(AuditoriumId auditoriumId) { 29 | this.auditoriumId = auditoriumId; 30 | return this; 31 | } 32 | 33 | public ShowBuilder withMovieId(MovieId movieId) { 34 | this.movieId = movieId; 35 | return this; 36 | } 37 | 38 | public Show build() { 39 | return new Show(id, auditoriumId, seats, movieId, startTime); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tiny-url-generator/app/src/reducers/auth.js: -------------------------------------------------------------------------------- 1 | import {ActionTypes as types} from "../constants"; 2 | 3 | const defaultValue = { 4 | username: '', 5 | password: '', 6 | isLoggedIn: false, 7 | isLoginAttemptFailure: false, 8 | accessToken: undefined, 9 | refreshToken: undefined, 10 | tokenType: undefined 11 | } 12 | 13 | function auth(state = defaultValue, action) { 14 | switch (action.type) { 15 | case types.CHANGE_USERNAME: 16 | return Object.assign({}, state, {username: action.data.newUsername}) 17 | case types.CHANGE_PASSWORD: 18 | return Object.assign({}, state, {password: action.data.newPassword}) 19 | case types.RECEIVED_SIGN_IN_SUCCESS: 20 | return Object.assign({}, state, {isLoggedIn: true, password: defaultValue.password, 21 | accessToken: action.data.access_token, refreshToken: action.data.refresh_token, 22 | tokenType: action.data.token_type}) 23 | case types.RECEIVED_SIGN_IN_FAILURE: 24 | return Object.assign({}, state, {isLoggedIn: false, password: defaultValue.password, 25 | isLoginAttemptFailure: false}) 26 | case types.RECEIVED_SIGN_IN_INVALID_ATTEMPT_FAILURE: 27 | return Object.assign({}, state, {isLoggedIn: false, password: defaultValue.password, 28 | isLoginAttemptFailure: true}) 29 | default: 30 | return state 31 | } 32 | } 33 | 34 | export default auth; 35 | 36 | -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/main/java/org/npathai/domain/AnalyticsService.java: -------------------------------------------------------------------------------- 1 | package org.npathai.domain; 2 | 3 | import org.apache.logging.log4j.LogManager; 4 | import org.apache.logging.log4j.Logger; 5 | import org.npathai.dao.AnalyticsDao; 6 | import org.npathai.dao.DataAccessException; 7 | import org.npathai.model.AnalyticsInfo; 8 | import org.npathai.model.UserInfo; 9 | 10 | import java.util.Optional; 11 | 12 | public class AnalyticsService { 13 | private static final Logger LOG = LogManager.getLogger(AnalyticsService.class); 14 | private final AnalyticsDao dao; 15 | 16 | public AnalyticsService(AnalyticsDao dao) { 17 | this.dao = dao; 18 | } 19 | 20 | public void onRedirectionClicked(String id) { 21 | dao.incrementClick(id); 22 | LOG.debug("Incremented click count for id:{}", id); 23 | } 24 | 25 | public Optional getById(UserInfo userInfo, String id) throws DataAccessException { 26 | // FIXME handle this scenario, should we call short-url-generation service to ask for this detail? 27 | // if (!redirection.get().uid().equals(userInfo.uid())) { 28 | // throw new UnauthorizedAccessException(); 29 | // } 30 | 31 | return dao.getById(id).or(() -> Optional.of(new AnalyticsInfo())); 32 | } 33 | 34 | public void onRedirectionCreated(String id) { 35 | LOG.debug("Creating analytics info for id:{}", id); 36 | dao.save(new AnalyticsInfo(id)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/usecases/GetShowsByMovie.java: -------------------------------------------------------------------------------- 1 | package org.npathai.usecases; 2 | 3 | import org.npathai.cinemahall.CinemaHall; 4 | import org.npathai.cinemahall.CinemaHallDao; 5 | import org.npathai.cinemahall.auditorium.Auditorium; 6 | import org.npathai.cinemahall.show.Show; 7 | import org.npathai.cinemahall.show.ShowDao; 8 | import org.npathai.city.CityId; 9 | import org.npathai.movie.MovieId; 10 | 11 | import java.util.List; 12 | 13 | public class GetShowsByMovie { 14 | 15 | private final CinemaHallDao cinemaHallDao; 16 | private final ShowDao showDao; 17 | 18 | public GetShowsByMovie(CinemaHallDao cinemaHallDao, ShowDao showDao) { 19 | this.cinemaHallDao = cinemaHallDao; 20 | this.showDao = showDao; 21 | } 22 | 23 | public Shows execute(MovieId movieId, CityId cityId) { 24 | List cinemaHallList = cinemaHallDao.getCinemaHalls(cityId); 25 | if (cinemaHallList.isEmpty()) { 26 | return Shows.empty(); 27 | } 28 | 29 | Shows shows = new Shows(); 30 | for (CinemaHall cinemaHall : cinemaHallList) { 31 | for (Auditorium auditorium : cinemaHall.getAudis()) { 32 | List audiShows = showDao.getShows(auditorium.getId(), movieId); 33 | if (audiShows.isEmpty()) { 34 | continue; 35 | } 36 | shows.add(cinemaHall, audiShows); 37 | } 38 | } 39 | 40 | return shows; 41 | } 42 | } -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | micronaut: 2 | application: 3 | name: short-url-generator 4 | server: 5 | port: 8080 6 | cors: 7 | enabled: true 8 | netty: 9 | log-level: ERROR 10 | 11 | --- 12 | micronaut: 13 | security: 14 | enabled: true 15 | endpoints: 16 | login: 17 | enabled: false 18 | logout: 19 | enabled: false 20 | oauth: 21 | enabled: false 22 | token: 23 | propagation: 24 | enabled: true 25 | service-id-regex: "analytics-service" 26 | uri-regex: ".*" 27 | jwt: 28 | enabled: true 29 | signatures: 30 | secret: 31 | generator: 32 | secret: "${JWT_GENERATOR_SIGNATURE_SECRET:ThisIsHighlySensitiveInformation}" 33 | --- 34 | endpoints: 35 | prometheus: 36 | sensitive: false 37 | --- 38 | micronaut: 39 | metrics: 40 | enabled: true 41 | export: 42 | prometheus: 43 | enabled: true 44 | step: PT1M 45 | descriptions: true 46 | --- 47 | zookeeper: 48 | url: zookeeper:2181 49 | --- 50 | redis: 51 | url: redis://redis-cache:6379 52 | --- 53 | url-lifetime-in-secs: 54 | anonymous: 1000 55 | authenticated: 1000 56 | --- 57 | 58 | mysql: 59 | url: jdbc:mysql://db:3306/short_url_generator 60 | user: root 61 | password: unsecured 62 | 63 | --- 64 | consul: 65 | client: 66 | registration: 67 | enabled: true 68 | defaultZone: "${CONSUL_HOST:consul}:${CONSUL_PORT:8500}" 69 | -------------------------------------------------------------------------------- /chatter/app/login.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "github.com/npathai/chatter/model" 5 | "net/http" 6 | ) 7 | 8 | func (app *App) AuthenticateUserForLogin(userId, loginId, password string) (user *model.User, err *model.AppError) { 9 | 10 | if len(password) == 0 { 11 | return nil, model.NewAppErrorWithStatus("Password blank", http.StatusBadRequest) 12 | } 13 | 14 | if user, err = app.GetUserForLogin(userId, loginId); err != nil { 15 | return nil, err 16 | } 17 | 18 | if user, err = app.authenticateUser(user, password); err != nil { 19 | return nil, err 20 | } 21 | 22 | return user, nil 23 | } 24 | 25 | func (app *App) GetUserForLogin(userId, loginId string) (*model.User, *model.AppError){ 26 | 27 | // Find user by id and fail if we can't find 28 | if len(userId) != 0 { 29 | user, err := app.GetUser(userId) 30 | if err != nil { 31 | err.StatusCode = http.StatusBadRequest 32 | return nil, err 33 | } 34 | return user, nil 35 | } 36 | 37 | // TODO get user by email 38 | return nil, nil 39 | } 40 | 41 | func (app *App) DoLogin(w http.ResponseWriter, r *http.Request, user *model.User) *model.AppError { 42 | session := &model.Session{ 43 | UserId: user.Id, 44 | } 45 | session.GenerateCSRF() 46 | // TODO add session expiry 47 | 48 | var err *model.AppError 49 | if session, err = app.CreateSession(session); err != nil { 50 | err.StatusCode = http.StatusInternalServerError 51 | return err 52 | } 53 | 54 | w.Header().Set(model.HeaderToken, session.Token) 55 | 56 | app.SetSession(session) 57 | return nil 58 | } -------------------------------------------------------------------------------- /tiny-url-generator/load-test/src/loadTest/simulations/ShortUrlGeneratorSimulation.scala: -------------------------------------------------------------------------------- 1 | package simulations 2 | 3 | import io.gatling.core.Predef._ 4 | import io.gatling.http.Predef._ 5 | 6 | import concurrent.duration.{DurationInt} 7 | 8 | class ShortUrlGeneratorSimulation extends Simulation { 9 | 10 | val scn = scenario("Anonymous shortening flow") 11 | .pause(10 seconds) 12 | .exec( 13 | http("First request - discard as it times out") 14 | .post("http://localhost:4000/shorten") 15 | .header("Content-Type", "application/json") 16 | .body(StringBody("""{"longUrl": "https://www.google.com"}""")) 17 | ) 18 | 19 | .exec( 20 | http("Shorten request") 21 | .post("http://localhost:4000/shorten") 22 | .header("Content-Type", "application/json") 23 | .body(StringBody("""{"longUrl": "https://www.google.com"}""")) 24 | .check(jmesPath("id").saveAs("id")) 25 | ) 26 | 27 | .repeat(100, "n") { 28 | exec( 29 | 30 | http("Visit shortened url") 31 | .get({ session => s"""http://localhost:4000/${session("id").as[String]}""" }) 32 | .disableFollowRedirect 33 | .check(status.is(301)) 34 | .check(header("Location").saveAs("expandedUrl")) 35 | ) 36 | .exec { session => 37 | println(">>>>>> " + session("expandedUrl").as[String] + " <<<<<<<<") 38 | session 39 | } 40 | .pause(50 milliseconds) 41 | } 42 | 43 | 44 | setUp( 45 | scn.inject(rampUsers(100) during (50 seconds)) 46 | ) 47 | } 48 | -------------------------------------------------------------------------------- /bookmyshow/application/src/main/java/org/npathai/movie/Movie.java: -------------------------------------------------------------------------------- 1 | package org.npathai.movie; 2 | 3 | import org.npathai.cinemahall.show.Show; 4 | import org.npathai.city.CityId; 5 | 6 | import java.time.Duration; 7 | import java.time.ZonedDateTime; 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | public class Movie { 12 | MovieId movieId; 13 | String name; 14 | Genre genre; 15 | String language; 16 | private Duration runDuration; 17 | private ZonedDateTime releaseDate; 18 | 19 | public List getShows(CityId cityId) { 20 | return Collections.emptyList(); 21 | } 22 | 23 | public MovieId getMovieId() { 24 | return movieId; 25 | } 26 | 27 | public void setId(MovieId movieId) { 28 | this.movieId = movieId; 29 | } 30 | 31 | public Duration getRunDuration() { 32 | return runDuration; 33 | } 34 | 35 | public void setDuration(Duration runDuration) { 36 | this.runDuration = runDuration; 37 | } 38 | 39 | public String getName() { 40 | return name; 41 | } 42 | 43 | public void setName(String name) { 44 | this.name = name; 45 | } 46 | 47 | public void setReleaseDate(ZonedDateTime releaseDate) { 48 | this.releaseDate = releaseDate; 49 | } 50 | 51 | public ZonedDateTime getReleaseDate() { 52 | return releaseDate; 53 | } 54 | 55 | public void setGenre(Genre genre) { 56 | this.genre = genre; 57 | } 58 | 59 | public Genre getGenre() { 60 | return genre; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/main/java/org/npathai/DomainBeanFactory.java: -------------------------------------------------------------------------------- 1 | package org.npathai; 2 | 3 | import io.micronaut.context.BeanContext; 4 | import io.micronaut.context.annotation.Factory; 5 | import org.npathai.config.ZookeeperConfiguration; 6 | import org.npathai.domain.IdGenerationService; 7 | import org.npathai.util.thread.DefaultScheduledJobService; 8 | import org.npathai.util.thread.ScheduledJobService; 9 | import org.npathai.zookeeper.DefaultZkManagerFactory; 10 | import org.npathai.zookeeper.ZkManager; 11 | 12 | import javax.inject.Inject; 13 | import javax.inject.Singleton; 14 | 15 | @Factory 16 | public class DomainBeanFactory { 17 | 18 | @Inject 19 | BeanContext beanContext; 20 | 21 | @Singleton 22 | public ZkManager createZkManager() throws InterruptedException { 23 | DefaultZkManagerFactory zkManagerFactory = new DefaultZkManagerFactory(); 24 | return zkManagerFactory.createConnected( 25 | beanContext.getBean(ZookeeperConfiguration.class).getUrl()); 26 | } 27 | 28 | @Singleton 29 | public IdGenerationService idGenerationService() throws Exception { 30 | IdGenerationService idGenerationService = new IdGenerationService(beanContext.getBean(ZkManager.class), 31 | beanContext.getBean(ScheduledJobService.class)); 32 | idGenerationService.init(); 33 | return idGenerationService; 34 | } 35 | 36 | @Singleton 37 | public ScheduledJobService defaultScheduledJobServiceService() { 38 | return new DefaultScheduledJobService(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /tiny-url-generator/app/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | 15 | .padding { 16 | padding: 10rem !important 17 | } 18 | 19 | body { 20 | background-color: #f8fafe 21 | } 22 | 23 | .domain-form .form-group { 24 | border: 1px solid #9ff0c8; 25 | padding: 10px 26 | } 27 | 28 | .domain-form .form-group input { 29 | height: 50px !important; 30 | border: transparent 31 | } 32 | 33 | .form-control { 34 | height: 52px !important; 35 | background: #fff !important; 36 | color: #3a4348 !important; 37 | font-size: 18px; 38 | border-radius: 0px; 39 | -webkit-box-shadow: none !important; 40 | box-shadow: none !important 41 | } 42 | 43 | .px-4 { 44 | padding-left: 1.5rem !important 45 | } 46 | 47 | .domain-form .form-group .search-domain { 48 | background: #22d47b; 49 | border: 2px solid #22d47b; 50 | color: #fff; 51 | -webkit-border-radius: 0; 52 | -moz-border-radius: 0; 53 | -ms-border-radius: 0; 54 | border-radius: 0 55 | } 56 | 57 | .domain-price span { 58 | color: #3a4348; 59 | margin: 0 10px 60 | } 61 | 62 | .domain-price span small { 63 | color: #24bdc9 64 | } 65 | 66 | .errorMsg { 67 | background-color: #a20000; 68 | color: #fff; 69 | margin-bottom: 15px; 70 | padding: 10px; 71 | } -------------------------------------------------------------------------------- /discourse/application/src/main/java/org/npathai/discourse/application/domain/topics/Topic.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.domain.topics; 2 | 3 | import java.time.ZonedDateTime; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | public class Topic { 8 | private long id; 9 | private String name; 10 | private long userId; 11 | private long categoryId; 12 | private List tagIds = new ArrayList<>(); 13 | private ZonedDateTime creationTime; 14 | private long lastPostUserId; 15 | 16 | public long getId() { 17 | return id; 18 | } 19 | 20 | public void setId(long id) { 21 | this.id = id; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | public void setName(String name) { 29 | this.name = name; 30 | } 31 | 32 | public long getUserId() { 33 | return userId; 34 | } 35 | 36 | public void setUserId(long userId) { 37 | this.userId = userId; 38 | } 39 | 40 | public long getCategoryId() { 41 | return categoryId; 42 | } 43 | 44 | public void setCategoryId(long categoryId) { 45 | this.categoryId = categoryId; 46 | } 47 | 48 | public List getTagIds() { 49 | return tagIds; 50 | } 51 | 52 | public void setTagIds(List tagIds) { 53 | this.tagIds = tagIds; 54 | } 55 | 56 | public ZonedDateTime getCreationTime() { 57 | return creationTime; 58 | } 59 | 60 | public void setCreationTime(ZonedDateTime creationTime) { 61 | this.creationTime = creationTime; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /bookmyshow/application/src/test/java/org/npathai/cinemahall/CinemaHallBuilder.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cinemahall; 2 | 3 | import org.npathai.cinemahall.auditorium.Auditorium; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Random; 8 | 9 | public class CinemaHallBuilder { 10 | private static final Random random = new Random(); 11 | private final long id; 12 | private String name; 13 | private Address address; 14 | private Rating rating; 15 | private List audis = new ArrayList<>(); 16 | 17 | public CinemaHallBuilder(int id) { 18 | this.id = id; 19 | name = "CinemaHall-" + id; 20 | } 21 | 22 | public static CinemaHallBuilder aCinemaHall() { 23 | CinemaHallBuilder cinemaHallBuilder = new CinemaHallBuilder(random.ints(1, 1000000) 24 | .findFirst().getAsInt()); 25 | return cinemaHallBuilder; 26 | } 27 | 28 | public AuditoriumBuilder anAuditorium() { 29 | return new AuditoriumBuilder(random.ints(1, 1000000) 30 | .findFirst().getAsInt()); 31 | } 32 | 33 | public CinemaHall build() { 34 | return new CinemaHall(id, name, address, rating, audis); 35 | } 36 | 37 | public class AuditoriumBuilder { 38 | private long id; 39 | private String name; 40 | private int seatCount; 41 | 42 | public AuditoriumBuilder(int id) { 43 | name = "Audi-" + id; 44 | } 45 | 46 | public CinemaHallBuilder build() { 47 | audis.add(new Auditorium(id, name, seatCount)); 48 | return CinemaHallBuilder.this; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/util/jwt/JWTCreator.java: -------------------------------------------------------------------------------- 1 | package org.npathai.util.jwt; 2 | 3 | import com.nimbusds.jose.JOSEException; 4 | import com.nimbusds.jose.JOSEObjectType; 5 | import com.nimbusds.jose.JWSAlgorithm; 6 | import com.nimbusds.jose.JWSHeader; 7 | import com.nimbusds.jose.crypto.MACSigner; 8 | import com.nimbusds.jwt.JWTClaimsSet; 9 | import com.nimbusds.jwt.SignedJWT; 10 | 11 | import java.time.Instant; 12 | import java.util.Date; 13 | 14 | public class JWTCreator { 15 | 16 | public static final String USER_ID = "78e98faa-b502-11ea-a146-0242ac190002"; 17 | public static final String USER_NAME = "root"; 18 | private final String secret; 19 | 20 | public JWTCreator(String secret) { 21 | this.secret = secret; 22 | } 23 | 24 | public SignedJWT createJwtForRoot() throws JOSEException { 25 | return createJwtFor(USER_ID, USER_NAME); 26 | } 27 | 28 | public SignedJWT createJwtFor(String uid, String username) throws JOSEException { 29 | JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.HS256) 30 | .type(JOSEObjectType.JWT) 31 | .build(); 32 | JWTClaimsSet payload = new JWTClaimsSet.Builder() 33 | .issuer("test-api") 34 | .subject(USER_NAME) 35 | .claim("uid", USER_ID) 36 | .expirationTime(Date.from(Instant.now().plusSeconds(120))) 37 | .build(); 38 | 39 | SignedJWT signedJWT = new SignedJWT(header, payload); 40 | MACSigner macSigner = new MACSigner(secret); 41 | signedJWT.sign(macSigner); 42 | return signedJWT; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /bookmyshow/application/src/test/java/org/npathai/movie/MovieBuilder.java: -------------------------------------------------------------------------------- 1 | package org.npathai.movie; 2 | 3 | import javax.validation.constraints.NotNull; 4 | import java.time.Duration; 5 | import java.time.ZonedDateTime; 6 | import java.util.Random; 7 | 8 | public class MovieBuilder { 9 | private final Movie movie; 10 | 11 | public MovieBuilder() { 12 | movie = new Movie(); 13 | } 14 | 15 | public MovieBuilder withId(long id) { 16 | movie.setId(new MovieId(id)); 17 | return this; 18 | } 19 | 20 | public MovieBuilder withName(@NotNull String name) { 21 | movie.setName(name); 22 | return this; 23 | } 24 | 25 | public MovieBuilder withReleaseDate(ZonedDateTime releaseDateTime) { 26 | movie.setReleaseDate(releaseDateTime); 27 | return this; 28 | } 29 | 30 | public MovieBuilder withDuration(Duration duration) { 31 | movie.setDuration(duration); 32 | return this; 33 | } 34 | 35 | private MovieBuilder withGenre(Genre genre) { 36 | movie.setGenre(genre); 37 | return this; 38 | } 39 | 40 | public Movie create() { 41 | return movie; 42 | } 43 | 44 | public static MovieBuilder aMovieNamed(String name) { 45 | final Random random = new Random(); 46 | return new MovieBuilder() 47 | .withId(random.nextLong()) 48 | .withName(name) 49 | .withReleaseDate(ZonedDateTime.now().minusDays(random.nextInt())) 50 | .withGenre(Genre.values()[random.nextInt(Genre.values().length)]) 51 | .withDuration(Duration.ofHours(random.nextInt(5))); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /discourse/application/src/test/java/org/npathai/discourse/application/controller/topics/TopicsControllerTest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.controller.topics; 2 | 3 | import io.micronaut.test.annotation.MicronautTest; 4 | import io.micronaut.test.annotation.MockBean; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | import org.mockito.Mockito; 8 | import org.npathai.discourse.application.domain.topics.Topic; 9 | import org.npathai.discourse.application.domain.topics.TopicService; 10 | 11 | import javax.inject.Inject; 12 | import java.util.List; 13 | 14 | import static org.mockito.Mockito.when; 15 | import static org.unitils.reflectionassert.ReflectionAssert.assertReflectionEquals; 16 | 17 | @MicronautTest 18 | public class TopicsControllerTest { 19 | 20 | public static final int USER_ID = 1; 21 | 22 | @Inject 23 | TopicService topicService; 24 | 25 | @Inject 26 | TopicControllerClient topicControllerClient; 27 | Topic topic; 28 | 29 | @BeforeEach 30 | public void setUp() { 31 | topic = new Topic(); 32 | topic.setCategoryId(1); 33 | topic.setId(2); 34 | topic.setName("Test topic"); 35 | topic.setUserId(USER_ID); 36 | when(topicService.getTopics(USER_ID)).thenReturn(List.of(topic)); 37 | } 38 | 39 | @Test 40 | public void returnsTopicsForUser() { 41 | List topics = topicControllerClient.get(); 42 | 43 | assertReflectionEquals(List.of(topic), topics); 44 | } 45 | 46 | @MockBean(TopicService.class) 47 | public TopicService createMockTopicService() { 48 | return Mockito.mock(TopicService.class); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/cache/RedisRedirectionCache.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cache; 2 | 3 | import org.npathai.config.RedisConfiguration; 4 | import org.npathai.model.Redirection; 5 | import org.redisson.Redisson; 6 | import org.redisson.api.RMap; 7 | import org.redisson.api.RedissonClient; 8 | import org.redisson.codec.JsonJacksonCodec; 9 | import org.redisson.config.Config; 10 | 11 | import java.io.Closeable; 12 | import java.util.Optional; 13 | 14 | public class RedisRedirectionCache implements RedirectionCache, Closeable { 15 | 16 | private final RedissonClient redissonClient; 17 | private final RMap cache; 18 | 19 | public RedisRedirectionCache(RedisConfiguration redisConfiguration) { 20 | Config config = new Config(); 21 | config.setCodec(new JsonJacksonCodec()); 22 | config.useSingleServer().setAddress(redisConfiguration.getUrl()); 23 | redissonClient = Redisson.create(config); 24 | cache = redissonClient.getMap("urlCache"); 25 | } 26 | 27 | @Override 28 | public Optional getById(String id) { 29 | Redirection redirection = cache.get(id); 30 | if (redirection == null) { 31 | return Optional.empty(); 32 | } 33 | return Optional.of(redirection); 34 | } 35 | 36 | @Override 37 | public void put(Redirection redirection) { 38 | cache.put(redirection.id(), redirection); 39 | } 40 | 41 | @Override 42 | public void deleteById(String id) { 43 | cache.remove(id); 44 | } 45 | 46 | @Override 47 | public void close() { 48 | redissonClient.shutdown(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /tiny-url-generator/id-gen-service/src/main/java/org/npathai/api/IdGeneratorAPI.java: -------------------------------------------------------------------------------- 1 | package org.npathai.api; 2 | 3 | import io.micrometer.core.annotation.Timed; 4 | import io.micrometer.core.instrument.MeterRegistry; 5 | import io.micrometer.core.instrument.Tags; 6 | import io.micronaut.http.HttpMethod; 7 | import io.micronaut.http.MediaType; 8 | import io.micronaut.http.annotation.Controller; 9 | import io.micronaut.http.annotation.Get; 10 | import io.micronaut.http.annotation.Produces; 11 | import org.npathai.domain.IdGenerationService; 12 | import org.npathai.metrics.ServiceTags; 13 | 14 | @Controller(IdGeneratorAPI.GENERATION_API_ENDPOINT) 15 | public class IdGeneratorAPI { 16 | 17 | public static final String GENERATION_API_ENDPOINT = "/generate"; 18 | private final IdGenerationService idGenerationService; 19 | private final MeterRegistry meterRegistry; 20 | 21 | public IdGeneratorAPI(IdGenerationService idGenerationService, MeterRegistry meterRegistry) { 22 | this.idGenerationService = idGenerationService; 23 | this.meterRegistry = meterRegistry; 24 | } 25 | 26 | @Timed(value = "http.response.time", extraTags = {"id.generation", "generation"}) 27 | @Get 28 | @Produces(MediaType.TEXT_PLAIN) 29 | public String generateId() { 30 | Tags commonTags = ServiceTags.httpApiTags("id.generation", "generation", 31 | GENERATION_API_ENDPOINT, HttpMethod.GET); 32 | 33 | meterRegistry.counter("http.requests.total", commonTags).increment(); 34 | 35 | String id = idGenerationService.nextId(); 36 | 37 | meterRegistry.counter("http.responses.total", ServiceTags.httpOkStatusTags(commonTags)).increment(); 38 | return id; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /chatter/api/user.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/npathai/chatter/model" 5 | "github.com/npathai/chatter/web" 6 | "net/http" 7 | ) 8 | 9 | func (api *API) InitUser() { 10 | api.BaseRoutes.Users.Handle("", api.ApiHandler(getUsers)).Methods("GET") 11 | api.BaseRoutes.Users.Handle("", api.ApiHandler(createUser)).Methods("POST") 12 | api.BaseRoutes.Users.Handle("/login", api.ApiHandler(login)).Methods("POST") 13 | } 14 | 15 | func getUsers(ctx *web.Context, w http.ResponseWriter, r *http.Request) { 16 | 17 | users, err := ctx.App.GetAllUsers() 18 | if err != nil { 19 | ctx.Err = err 20 | } 21 | w.Write([]byte(model.UserListToJson(users))) 22 | } 23 | 24 | func createUser(ctx *web.Context, w http.ResponseWriter, r *http.Request) { 25 | user := model.UserFromJson(r.Body) 26 | if user == nil { 27 | w.WriteHeader(http.StatusBadRequest) 28 | return 29 | } 30 | 31 | // Sanitize the input 32 | // Ask the app layer to create the user from signup 33 | user, err := ctx.App.CreateUser(user) 34 | 35 | if err != nil { 36 | ctx.Err = err 37 | return 38 | } 39 | 40 | w.WriteHeader(http.StatusCreated) 41 | w.Write([]byte(user.ToJson())) 42 | } 43 | 44 | func login(ctx *web.Context, w http.ResponseWriter, r *http.Request) { 45 | props := model.MapFromJson(r.Body) 46 | userId := props["id"] 47 | loginId := props["loginId"] 48 | password := props["password"] 49 | 50 | user, err := ctx.App.AuthenticateUserForLogin(userId, loginId, password) 51 | if err != nil { 52 | ctx.Err = err 53 | return 54 | } 55 | 56 | err = ctx.App.DoLogin(w, r, user) 57 | if err != nil { 58 | ctx.Err = err; 59 | return 60 | } 61 | 62 | // Sanitize user information to remove sensitive information 63 | w.Write([]byte(user.ToJson())) 64 | } -------------------------------------------------------------------------------- /discourse/application/src/test/java/org/npathai/discourse/application/domain/users/InMemoryUserRepositoryTest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.domain.users; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.mockito.Mock; 6 | import org.mockito.MockitoAnnotations; 7 | import org.npathai.discourse.application.common.IdGenerator; 8 | 9 | import static org.assertj.core.api.Assertions.assertThat; 10 | import static org.mockito.Mockito.when; 11 | 12 | class InMemoryUserRepositoryTest { 13 | 14 | private static final String USER_ID = "1234"; 15 | 16 | @Mock 17 | IdGenerator idGenerator; 18 | 19 | InMemoryUserRepository repository; 20 | 21 | @BeforeEach 22 | public void setUp() { 23 | MockitoAnnotations.initMocks(this); 24 | repository = new InMemoryUserRepository(idGenerator); 25 | when(idGenerator.nextId()).thenReturn(USER_ID); 26 | } 27 | 28 | @Test 29 | public void assignsIdToUser() { 30 | User user = createUser(); 31 | repository.save(user); 32 | 33 | assertThat(user.getUserId()).isEqualTo(USER_ID); 34 | } 35 | 36 | @Test 37 | public void returnsSavedUserById() { 38 | User user = createUser(); 39 | 40 | repository.save(user); 41 | 42 | assertThat(repository.getById(user.getUserId())) 43 | .isPresent() 44 | .hasValue(user); 45 | } 46 | 47 | private User createUser() { 48 | User user = new User(); 49 | user.setUserId(USER_ID); 50 | user.setUsername("buddha"); 51 | user.setEmail("buddha@peace.in"); 52 | user.setName("Siddhartha Gautama"); 53 | user.setPassword("asdf@123"); 54 | return user; 55 | } 56 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # system-design-with-code :computer: 2 | System design not just on whiteboard, but with code. 3 | 4 | [![Build Status](https://api.travis-ci.org/npathai/system-design-with-code.svg?branch=master)](https://travis-ci.org/npathai/system-design-with-code) 5 | 6 | 7 | ## Why this repository? 8 | This repository represents an effort to learn System Design by experimenting, failing-fast, learning from that experince and providing all the fellow developers a wealth of knowledge in terms of source code. 9 | 10 | System design always seemed quiet far from reach because the knowledge available online either is too shallow or basic, or quiet high level. Being a developer, I wanted to learn by looking at code and experimenting. Facing challenges head on and building robust systems. If you feel the same, then this repository is perfect for you! 11 | 12 | ## Goals 13 | Try to re-create system design case studies, not just on prototype scale but full scale. I intend to deploy sytems on cloud and load test them at scale. 14 | 15 | All systems built in this repository should be 16 | - Microservices driven (or even Serverless) 17 | - Lightweight 18 | - Highly scalable 19 | - Fault tolerant (kill machines randomly and system shouldn't cave in) 20 | - No single point of failure 21 | - High performance 22 | - Observable and Traceable 23 | 24 | 25 | ## Projects 26 | ### URL Shortening Service 27 | ### Chatter (Slack) - Inspired from Mattermost 28 | ### Discourse (QnA) 29 | ### BookMyShow 30 | 31 | 32 | ## Technology Stack 33 | 34 | 1) Micronaut Framework 35 | 2) Zookeeper 36 | 3) Consul Service Discovery 37 | 4) MySQL 38 | 5) Redis Cache 39 | 6) Test Containers 40 | 7) React (frontend) 41 | 8) Docker 42 | 9) Zipkin 43 | 10) Hystrix/Resilience4J 44 | 11) Micrometer 45 | 12) Prometheus 46 | 13) Grafana 47 | 14) Gatling 48 | -------------------------------------------------------------------------------- /tiny-url-generator/app/src/components/redirection-history/Redirection.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {connect} from 'react-redux' 3 | 4 | class Redirection extends React.Component { 5 | 6 | constructor(props) { 7 | super(props); 8 | this.state = { 9 | clickCount: 0 10 | } 11 | } 12 | componentDidMount() { 13 | let headers = { 14 | 'Content-Type': 'application/json', 15 | "Authorization": this.props.tokenType + " " + this.props.accessToken 16 | } 17 | 18 | fetch("http://localhost:4000/analytics/" + this.props.data.id, { 19 | method: 'GET', 20 | headers: headers, 21 | mode: 'cors' 22 | }) 23 | .then(res => { 24 | if (res.status !== 200) { 25 | throw new Error("Error in fetching analytics") 26 | } 27 | return res.json() 28 | }) 29 | .then(res => { 30 | console.log(res) 31 | this.setState({ 32 | clickCount: res.clickCount 33 | }) 34 | }) 35 | .catch(err => { 36 | console.log(err) 37 | }) 38 | } 39 | 40 | render() { 41 | return ( 42 | 43 | {this.props.data.url} 44 | {this.state.clickCount} 45 | {this.props.data.longUrl} 46 | {new Date(this.props.data.createdAtMillis).toUTCString()} 47 | 48 | ); 49 | } 50 | } 51 | 52 | export default connect((state, props) => { 53 | return { 54 | tokenType: state.auth.tokenType, 55 | accessToken: state.auth.accessToken 56 | } 57 | })(Redirection) -------------------------------------------------------------------------------- /tiny-url-generator/analytics-service/src/main/java/org/npathai/api/AnalyticsAPI.java: -------------------------------------------------------------------------------- 1 | package org.npathai.api; 2 | 3 | import io.micronaut.http.HttpResponse; 4 | import io.micronaut.http.annotation.Controller; 5 | import io.micronaut.http.annotation.Get; 6 | import io.micronaut.http.annotation.Post; 7 | import io.micronaut.security.annotation.Secured; 8 | import io.micronaut.security.authentication.Authentication; 9 | import org.npathai.dao.DataAccessException; 10 | import org.npathai.domain.AnalyticsService; 11 | import org.npathai.model.AnalyticsInfo; 12 | import org.npathai.model.UserInfo; 13 | 14 | import java.util.Optional; 15 | 16 | // FIXME converting APIs to anonymous, as we need service-service token between short-url-generator and this service. 17 | // Need to find out how to achieve it using micronaut. 18 | @Controller 19 | public class AnalyticsAPI { 20 | 21 | private final AnalyticsService analyticsService; 22 | 23 | public AnalyticsAPI(AnalyticsService analyticsService) { 24 | this.analyticsService = analyticsService; 25 | } 26 | 27 | @Secured("isAnonymous()") 28 | @Post("/analytics/{id}") 29 | public void post(String id) { 30 | analyticsService.onRedirectionCreated(id); 31 | } 32 | 33 | @Secured("isAnonymous()") 34 | @Post("/analytics/{id}/click") 35 | public void redirectionClicked(String id) { 36 | analyticsService.onRedirectionClicked(id); 37 | } 38 | 39 | @Get("/analytics/{id}") 40 | @Secured("isAnonymous()") 41 | public HttpResponse get(String id) throws DataAccessException { 42 | Optional info; 43 | info = analyticsService.getById(null, id); 44 | 45 | if (info.isEmpty()) { 46 | return HttpResponse.notFound(); 47 | } 48 | 49 | return HttpResponse.ok(info.get()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/discovery/zookeeper/ZkServiceDiscoveryClient.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discovery.zookeeper; 2 | 3 | import org.apache.curator.x.discovery.ServiceDiscovery; 4 | import org.apache.curator.x.discovery.ServiceInstance; 5 | import org.apache.curator.x.discovery.ServiceProvider; 6 | import org.npathai.discovery.DiscoveryException; 7 | import org.npathai.discovery.Instance; 8 | import org.npathai.discovery.ServiceDiscoveryClient; 9 | 10 | import javax.annotation.Nonnull; 11 | import java.io.IOException; 12 | 13 | public class ZkServiceDiscoveryClient implements ServiceDiscoveryClient { 14 | 15 | private final String serviceName; 16 | private final ServiceDiscovery serviceDiscovery; 17 | private final ServiceProvider serviceProvider; 18 | 19 | public ZkServiceDiscoveryClient(String serviceName, 20 | ServiceDiscovery serviceDiscovery, 21 | ServiceProvider serviceProvider) { 22 | this.serviceName = serviceName; 23 | this.serviceDiscovery = serviceDiscovery; 24 | this.serviceProvider = serviceProvider; 25 | } 26 | 27 | @Nonnull 28 | @Override 29 | public Instance getInstance() throws DiscoveryException { 30 | try { 31 | ServiceInstance chosenInstance = serviceProvider.getInstance(); 32 | if (chosenInstance == null) { 33 | throw new DiscoveryException("No instance is available for service: " + serviceName); 34 | } 35 | return new ZkInstance(chosenInstance); 36 | } catch (Exception e) { 37 | throw new DiscoveryException(e); 38 | } 39 | } 40 | 41 | @Override 42 | public void close() throws IOException { 43 | serviceProvider.close(); 44 | serviceDiscovery.close(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/java/org/npathai/auth/CustomJWTClaimsSetGenerator.java: -------------------------------------------------------------------------------- 1 | package org.npathai.auth; 2 | 3 | import com.nimbusds.jwt.JWTClaimsSet; 4 | import io.micronaut.context.annotation.Replaces; 5 | import io.micronaut.runtime.ApplicationConfiguration; 6 | import io.micronaut.security.authentication.UserDetails; 7 | import io.micronaut.security.token.config.TokenConfiguration; 8 | import io.micronaut.security.token.jwt.generator.claims.ClaimsAudienceProvider; 9 | import io.micronaut.security.token.jwt.generator.claims.JWTClaimsSetGenerator; 10 | import io.micronaut.security.token.jwt.generator.claims.JwtIdGenerator; 11 | import org.apache.logging.log4j.LogManager; 12 | import org.apache.logging.log4j.Logger; 13 | 14 | import javax.annotation.Nullable; 15 | import javax.inject.Singleton; 16 | 17 | @Singleton 18 | @Replaces(bean = JWTClaimsSetGenerator.class) 19 | public class CustomJWTClaimsSetGenerator extends JWTClaimsSetGenerator { 20 | private static final Logger LOG = LogManager.getLogger(CustomJWTClaimsSetGenerator.class); 21 | 22 | public CustomJWTClaimsSetGenerator(TokenConfiguration tokenConfiguration, 23 | @Nullable JwtIdGenerator jwtIdGenerator, 24 | @Nullable ClaimsAudienceProvider claimsAudienceProvider, 25 | @Nullable ApplicationConfiguration applicationConfiguration) { 26 | super(tokenConfiguration, jwtIdGenerator, claimsAudienceProvider, applicationConfiguration); 27 | } 28 | 29 | @Override 30 | protected void populateWithUserDetails(JWTClaimsSet.Builder builder, UserDetails userDetails) { 31 | LOG.info("Custom claims builder called"); 32 | super.populateWithUserDetails(builder, userDetails); 33 | if (userDetails instanceof CustomUserDetails) { 34 | builder.claim("uid", ((CustomUserDetails) userDetails).getUserId()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tiny-url-generator/app/src/components/redirection-history/RedirectionHistory.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import {connect} from 'react-redux' 3 | import Redirection from "./Redirection"; 4 | import * as actions from '../../actions/actions' 5 | 6 | class RedirectionHistory extends React.Component { 7 | 8 | componentDidMount() { 9 | const token = { 10 | type: this.props.tokenType, 11 | accessToken: this.props.accessToken 12 | } 13 | 14 | this.props.dispatch(actions.fetchRedirectionHistory(token)) 15 | } 16 | 17 | render() { 18 | 19 | const redirectionElements = this.props.loaded ? this.props.redirections.map(each => { 20 | each['url'] = "http://localhost:4000/" + each.id; 21 | return 22 | }) : null; 23 | 24 | return this.props.loaded ? 25 | ( 26 | <> 27 |
    28 |
    {this.props.redirections.length} redirections
    29 |
    30 |
    31 |
    32 | 33 | 34 | {redirectionElements} 35 | 36 |
    37 |
    38 |
    39 | 40 | ) : null 41 | } 42 | } 43 | 44 | export default connect((state, props) => { 45 | console.log('state', state) 46 | return { 47 | tokenType: state.auth.tokenType, 48 | accessToken: state.auth.accessToken, 49 | loaded: state.redirection.loaded, 50 | redirections: state.redirection.redirections 51 | } 52 | })(RedirectionHistory) -------------------------------------------------------------------------------- /chatter/model/utils.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base32" 6 | "encoding/json" 7 | "github.com/google/uuid" 8 | "io" 9 | "net/mail" 10 | "regexp" 11 | "strings" 12 | ) 13 | 14 | var encoding = base32.NewEncoding("ybndrfg8ejkmcpqxot1uwisza345h769") 15 | 16 | func NewId() string { 17 | var b bytes.Buffer 18 | encoder :=base32.NewEncoder(encoding, &b) 19 | id := uuid.New() 20 | idBytes, _ := id.MarshalBinary() 21 | encoder.Write(idBytes) 22 | encoder.Close() 23 | b.Truncate(26) 24 | return b.String() 25 | } 26 | 27 | type AppError struct { 28 | StatusCode int `json:"status_code"` 29 | Message string `json:"message"` 30 | } 31 | 32 | func (err *AppError) Error() string { 33 | return err.Message 34 | } 35 | 36 | func NewAppError(message string) *AppError { 37 | return &AppError{ 38 | StatusCode: 500, 39 | Message: message, 40 | } 41 | } 42 | 43 | func NewAppErrorWithStatus(message string, statusCode int) *AppError { 44 | return &AppError{ 45 | StatusCode: statusCode, 46 | Message: message, 47 | } 48 | } 49 | 50 | func IsValidEmail(email string) bool { 51 | if !IsLower(email) { 52 | return false 53 | } 54 | 55 | if _, err := mail.ParseAddress(email); err != nil { 56 | return false 57 | } 58 | 59 | return true 60 | } 61 | 62 | func IsLower(email string) bool { 63 | return strings.ToLower(email) == email 64 | } 65 | 66 | func IsValidDomain(domain string) bool { 67 | if !IsValidAlphaNum(domain) { 68 | return false 69 | } 70 | if len(domain) <= 3 { 71 | return false 72 | } 73 | return true 74 | } 75 | 76 | var validAlphaNum = regexp.MustCompile(`^[a-z0-9]+([a-z\-0-9]+|(__)?)[a-z0-9]+$`) 77 | func IsValidAlphaNum(str string) bool { 78 | return validAlphaNum.MatchString(str) 79 | } 80 | 81 | func MapFromJson(data io.Reader) map[string]string { 82 | decoder := json.NewDecoder(data) 83 | var decoded map[string]string 84 | if err := decoder.Decode(&decoded); err != nil { 85 | return make(map[string]string) 86 | } 87 | return decoded 88 | } -------------------------------------------------------------------------------- /bookmyshow/application/src/test/java/org/npathai/usecases/GetMoviesByCityIdTest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.usecases; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.junit.jupiter.api.extension.ExtendWith; 5 | import org.mockito.Mock; 6 | import org.mockito.Mockito; 7 | import org.mockito.junit.jupiter.MockitoExtension; 8 | import org.npathai.city.CityId; 9 | import org.npathai.movie.Movie; 10 | import org.npathai.movie.MovieId; 11 | import org.npathai.movie.MovieRepository; 12 | 13 | import java.time.Duration; 14 | import java.util.List; 15 | 16 | import static org.assertj.core.api.Assertions.assertThat; 17 | 18 | @ExtendWith(MockitoExtension.class) 19 | public class GetMoviesByCityIdTest { 20 | 21 | private static final CityId CITY_ID = new CityId(1L); 22 | private static final Movie HARRY_POTTER_MOVIE = createHarryPotterMovie(); 23 | private static final Movie INTERSTELLAR_MOVIE = createInterstellarMovie(); 24 | 25 | @Mock 26 | MovieRepository movieRepository; 27 | 28 | @Test 29 | public void returnsAllMoviesRunningInCity() { 30 | GetMoviesByCityId useCase = new GetMoviesByCityId(movieRepository, CITY_ID); 31 | Mockito.when(movieRepository.getByCityId(CITY_ID)).thenReturn(List.of(HARRY_POTTER_MOVIE, INTERSTELLAR_MOVIE)); 32 | List movies = useCase.execute(); 33 | assertThat(movies).containsExactlyElementsOf(List.of(HARRY_POTTER_MOVIE, INTERSTELLAR_MOVIE)); 34 | } 35 | 36 | private static Movie createHarryPotterMovie() { 37 | Movie movie = new Movie(); 38 | movie.setId(new MovieId(1L)); 39 | movie.setDuration(Duration.ofMinutes(200)); 40 | movie.setName("Harry Potter and the Sorcerers stone"); 41 | return movie; 42 | } 43 | 44 | private static Movie createInterstellarMovie() { 45 | Movie movie = new Movie(); 46 | movie.setId(new MovieId(2L)); 47 | movie.setDuration(Duration.ofMinutes(240)); 48 | movie.setName("Interstellar"); 49 | return movie; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/java/org/npathai/metrics/ServiceTags.java: -------------------------------------------------------------------------------- 1 | package org.npathai.metrics; 2 | 3 | import io.micrometer.core.instrument.Tag; 4 | import io.micrometer.core.instrument.Tags; 5 | import io.micronaut.http.HttpMethod; 6 | import io.micronaut.http.HttpStatus; 7 | 8 | public class ServiceTags { 9 | 10 | public static Tags httpApiTags(String serviceName, String serviceModule, String api, HttpMethod httpMethod) { 11 | return Tags.of(Tag.of("service", serviceName), Tag.of("service.module", serviceModule), 12 | Tag.of("api", api), 13 | Tag.of("http.method", httpMethod.name())); 14 | } 15 | 16 | public static Tags httpOkStatusTags(Tags commonTags) { 17 | return commonTags.and(ServiceTags.httpStatusTags(commonTags, HttpStatus.OK)); 18 | } 19 | 20 | public static Tags httpInternalErrorStatusTags(Tags commonTags) { 21 | return commonTags.and(ServiceTags.httpStatusTags(commonTags, HttpStatus.INTERNAL_SERVER_ERROR)); 22 | } 23 | 24 | public static Tags httpNotFoundStatusTags(Tags commonTags) { 25 | return commonTags.and(ServiceTags.httpStatusTags(commonTags, HttpStatus.INTERNAL_SERVER_ERROR)); 26 | } 27 | 28 | public static Tags httpStatusTags(Tags commonTags, HttpStatus httpStatus) { 29 | return commonTags.and(Tag.of("http.status", String.valueOf(httpStatus.getCode()))); 30 | } 31 | 32 | public static class DatabaseTags { 33 | public static Tags databaseTags(String serviceName, String serviceModule, String operation) { 34 | return Tags.of(Tag.of("service", serviceName), Tag.of("service.module", serviceModule), 35 | Tag.of("database.operation", operation)); 36 | } 37 | 38 | public static Tags databaseSuccessTags(Tags commonTags) { 39 | return commonTags.and("database.operation.result", "success"); 40 | } 41 | 42 | public static Tags databaseErrorTags(Tags commonTags) { 43 | return commonTags.and("database.operation.result", "error"); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tiny-url-generator/app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 16 | 17 | 18 | 20 | 21 | 30 | React App 31 | 32 | 33 | 34 |
    35 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/test/java/org/npathai/dao/MySqlUserDaoTest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.dao; 2 | 3 | import io.micrometer.core.instrument.MeterRegistry; 4 | import io.micronaut.test.annotation.MicronautTest; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | import org.npathai.config.MySqlDatasourceConfiguration; 8 | import org.npathai.model.User; 9 | import org.testcontainers.containers.MySQLContainer; 10 | import org.testcontainers.junit.jupiter.Container; 11 | import org.testcontainers.junit.jupiter.Testcontainers; 12 | import org.unitils.reflectionassert.ReflectionAssert; 13 | 14 | import javax.inject.Inject; 15 | import java.util.Optional; 16 | 17 | import static org.assertj.core.api.Assertions.assertThat; 18 | 19 | @Testcontainers 20 | @MicronautTest 21 | class MySqlUserDaoTest { 22 | 23 | @Inject 24 | MeterRegistry meterRegistry; 25 | 26 | @Container 27 | public MySQLContainer mysqlContainer = new MySQLContainer<>("mysql:latest") 28 | .withDatabaseName("user_db") 29 | .withUsername("testUser") 30 | .withPassword("unsecured") 31 | .withInitScript("test-user-init.sql") 32 | .withExposedPorts(3306); 33 | 34 | private MySqlUserDao dao; 35 | 36 | @BeforeEach 37 | public void setUp() { 38 | MySqlDatasourceConfiguration configuration = new MySqlDatasourceConfiguration(); 39 | configuration.setUser("testUser"); 40 | configuration.setPassword("unsecured"); 41 | configuration.setUrl(String.format("jdbc:mysql://%s:%d/user_db", 42 | mysqlContainer.getContainerIpAddress(), mysqlContainer.getMappedPort(3306))); 43 | dao = new MySqlUserDao(configuration, meterRegistry); 44 | } 45 | 46 | @Test 47 | public void canGetRootUser() throws DataAccessException { 48 | User rootUser = new User(); 49 | rootUser.setUsername("root"); 50 | rootUser.setPassword("$2a$09$C75xhHFSNwj0GV6STPWTqOgZ2qYpvH88QxGXbxWUF/kC0qgfJAEI."); 51 | 52 | Optional optionalRoot = dao.getUserByName("root"); 53 | assertThat(optionalRoot).isNotEmpty(); 54 | ReflectionAssert.assertLenientEquals(rootUser, optionalRoot.get()); 55 | } 56 | } -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/metrics/ServiceTags.java: -------------------------------------------------------------------------------- 1 | package org.npathai.metrics; 2 | 3 | import io.micrometer.core.instrument.Tag; 4 | import io.micrometer.core.instrument.Tags; 5 | import io.micronaut.http.HttpMethod; 6 | import io.micronaut.http.HttpStatus; 7 | 8 | public class ServiceTags { 9 | 10 | public static Tags httpApiTags(String serviceName, String serviceModule, String api, HttpMethod httpMethod) { 11 | return Tags.of(Tag.of("service", serviceName), Tag.of("service.module", serviceModule), 12 | Tag.of("api", api), 13 | Tag.of("http.method", httpMethod.name())); 14 | } 15 | 16 | public static Tags httpOkStatusTags(Tags commonTags) { 17 | return commonTags.and(ServiceTags.httpStatusTags(commonTags, HttpStatus.OK)); 18 | } 19 | 20 | public static Tags httpInternalErrorStatusTags(Tags commonTags) { 21 | return commonTags.and(ServiceTags.httpStatusTags(commonTags, HttpStatus.INTERNAL_SERVER_ERROR)); 22 | } 23 | 24 | public static Tags httpNotFoundStatusTags(Tags commonTags) { 25 | return commonTags.and(ServiceTags.httpStatusTags(commonTags, HttpStatus.INTERNAL_SERVER_ERROR)); 26 | } 27 | 28 | public static Tags httpStatusTags(Tags commonTags, HttpStatus httpStatus) { 29 | return commonTags.and(Tag.of("http.status", String.valueOf(httpStatus.getCode()))); 30 | } 31 | 32 | public static Tags serviceTags(String serviceName, String serviceModule, String operation) { 33 | return Tags.of(Tag.of("service", serviceName), Tag.of("service.module", serviceModule), 34 | Tag.of("service.operation", operation)); 35 | } 36 | 37 | public static class DatabaseTags { 38 | public static Tags databaseTags(String serviceName, String serviceModule, String operation) { 39 | return Tags.of(Tag.of("service", serviceName), Tag.of("service.module", serviceModule), 40 | Tag.of("database.operation", operation)); 41 | } 42 | 43 | public static Tags databaseSuccessTags(Tags commonTags) { 44 | return commonTags.and("database.operation.result", "success"); 45 | } 46 | 47 | public static Tags databaseErrorTags(Tags commonTags) { 48 | return commonTags.and("database.operation.result", "error"); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/zookeeper/DefaultZkManager.java: -------------------------------------------------------------------------------- 1 | package org.npathai.zookeeper; 2 | 3 | import org.apache.curator.framework.CuratorFramework; 4 | import org.apache.curator.framework.recipes.locks.InterProcessMutex; 5 | import org.apache.logging.log4j.LogManager; 6 | import org.apache.logging.log4j.Logger; 7 | import org.apache.zookeeper.KeeperException; 8 | import org.apache.zookeeper.data.Stat; 9 | 10 | import java.util.concurrent.Callable; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | public class DefaultZkManager implements ZkManager { 14 | private static final Logger LOG = LogManager.getLogger(DefaultZkManager.class); 15 | 16 | private final CuratorFramework client; 17 | 18 | public DefaultZkManager(CuratorFramework client) { 19 | this.client = client; 20 | } 21 | 22 | @Override 23 | public Stat createIfAbsent(String path) throws Exception { 24 | try { 25 | client.create().creatingParentContainersIfNeeded().forPath(path, "".getBytes()); 26 | } catch (KeeperException.NodeExistsException ex) { 27 | LOG.debug(path + " node already exists."); 28 | } 29 | return null; 30 | } 31 | 32 | @Override 33 | public Callable withinLock(String lockPath, Callable callable) { 34 | return () -> { 35 | InterProcessMutex lock = new InterProcessMutex(client, lockPath); 36 | if (!lock.acquire(100, TimeUnit.SECONDS)) { 37 | throw new IllegalStateException("Could not acquire the lock to Next id node"); 38 | } 39 | try { 40 | LOG.debug("Acquired lock: " + System.currentTimeMillis()); 41 | return callable.call(); 42 | } finally { 43 | lock.release(); // always release the lock in a finally block 44 | LOG.debug("Released lock: " + System.currentTimeMillis()); 45 | } 46 | }; 47 | } 48 | 49 | @Override 50 | public byte[] getData(String path) throws Exception { 51 | return client.getData().forPath(path); 52 | } 53 | 54 | @Override 55 | public void setData(String path, byte[] bytes) throws Exception { 56 | client.setData().forPath(path, bytes); 57 | } 58 | 59 | @Override 60 | public void stop() { 61 | client.close(); 62 | } 63 | 64 | @Override 65 | public CuratorFramework client() { 66 | return client; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/java/org/npathai/controller/CustomLoginController.java: -------------------------------------------------------------------------------- 1 | package org.npathai.controller; 2 | 3 | import io.micrometer.core.instrument.MeterRegistry; 4 | import io.micrometer.core.instrument.Tags; 5 | import io.micronaut.context.annotation.Replaces; 6 | import io.micronaut.context.event.ApplicationEventPublisher; 7 | import io.micronaut.http.HttpMethod; 8 | import io.micronaut.http.HttpRequest; 9 | import io.micronaut.http.HttpResponse; 10 | import io.micronaut.http.annotation.Controller; 11 | import io.micronaut.security.authentication.Authenticator; 12 | import io.micronaut.security.authentication.UsernamePasswordCredentials; 13 | import io.micronaut.security.endpoints.LoginController; 14 | import io.micronaut.security.handlers.LoginHandler; 15 | import io.reactivex.Single; 16 | import org.npathai.metrics.ServiceTags; 17 | 18 | import javax.validation.Valid; 19 | 20 | @Controller 21 | @Replaces(LoginController.class) 22 | public class CustomLoginController extends LoginController { 23 | 24 | private static final String LOGIN_ENDPOINT = "/login"; 25 | private final MeterRegistry meterRegistry; 26 | 27 | /** 28 | * @param authenticator {@link Authenticator} collaborator 29 | * @param loginHandler A collaborator which helps to build HTTP response depending on success or failure. 30 | * @param eventPublisher The application event publisher 31 | */ 32 | public CustomLoginController(Authenticator authenticator, LoginHandler loginHandler, 33 | ApplicationEventPublisher eventPublisher, MeterRegistry meterRegistry) { 34 | super(authenticator, loginHandler, eventPublisher); 35 | this.meterRegistry = meterRegistry; 36 | } 37 | 38 | @Override 39 | public Single login(@Valid UsernamePasswordCredentials usernamePasswordCredentials, HttpRequest request) { 40 | Tags commonTags = ServiceTags.httpApiTags("user.service", "authentication", 41 | LOGIN_ENDPOINT, HttpMethod.POST); 42 | 43 | meterRegistry.counter("http.requests.total", commonTags).increment(); 44 | return super.login(usernamePasswordCredentials, request) 45 | .map(loginResponse -> { 46 | meterRegistry.counter("http.responses.total", 47 | ServiceTags.httpStatusTags(commonTags, loginResponse.getStatus())).increment(); 48 | return loginResponse; 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /discourse/application/src/test/java/org/npathai/discourse/application/domain/users/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discourse.application.domain.users; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.mockito.MockitoAnnotations; 6 | import org.mockito.Spy; 7 | 8 | import static org.mockito.Mockito.verify; 9 | import static org.unitils.reflectionassert.ReflectionAssert.assertReflectionEquals; 10 | 11 | class UserServiceTest { 12 | 13 | public static final String USER_ID = "1234"; 14 | UserService userService; 15 | 16 | @Spy 17 | UserRepositoryStub userRepository; 18 | 19 | @BeforeEach 20 | public void setUp() { 21 | MockitoAnnotations.initMocks(this); 22 | userService = new UserService(userRepository); 23 | } 24 | 25 | @Test 26 | public void createANewUser() { 27 | RegistrationData registrationData = createRegistrationData(); 28 | User expectedUser = userFrom(registrationData); 29 | expectedUser.setUserId(USER_ID); 30 | userRepository.setUserId(USER_ID); 31 | 32 | User actualUser = userService.create(registrationData); 33 | 34 | verify(userRepository).save(actualUser); 35 | } 36 | 37 | @Test 38 | public void returnNewlyCreatedUser() { 39 | RegistrationData registrationData = createRegistrationData(); 40 | User expectedUser = userFrom(registrationData); 41 | expectedUser.setUserId(USER_ID); 42 | userRepository.setUserId(USER_ID); 43 | 44 | User actualUser = userService.create(registrationData); 45 | 46 | assertReflectionEquals(expectedUser, actualUser); 47 | } 48 | 49 | private RegistrationData createRegistrationData() { 50 | RegistrationData registrationData = new RegistrationData(); 51 | registrationData.setUsername("buddha"); 52 | registrationData.setName("Siddhartha Gautama"); 53 | registrationData.setPassword("asdf@1234"); 54 | registrationData.setEmail("buddha@peace.in"); 55 | return registrationData; 56 | } 57 | 58 | private User userFrom(RegistrationData registrationData) { 59 | User user = new User(); 60 | user.setUserId(USER_ID); 61 | user.setUsername(registrationData.getUsername()); 62 | user.setEmail(registrationData.getEmail()); 63 | user.setName(registrationData.getName()); 64 | user.setPassword(registrationData.getPassword()); 65 | return user; 66 | } 67 | } -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/model/Redirection.java: -------------------------------------------------------------------------------- 1 | package org.npathai.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | 5 | import javax.annotation.Nullable; 6 | import java.time.Clock; 7 | import java.util.Objects; 8 | 9 | @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY) 10 | public class Redirection { 11 | private String id; 12 | private String longUrl; 13 | private long createdAtMillis; 14 | private long expiryAtMillis; 15 | private String uid; 16 | 17 | public Redirection() { 18 | 19 | } 20 | 21 | public Redirection(String id, String longUrl, long createdAtMillis, long expiryAtMillis) { 22 | this(id, longUrl, createdAtMillis, expiryAtMillis, null); 23 | } 24 | 25 | public Redirection(String id, String longUrl, long createdAtMillis, long expiryAtMillis, String uid) { 26 | this.id = id; 27 | this.longUrl = longUrl; 28 | this.createdAtMillis = createdAtMillis; 29 | this.expiryAtMillis = expiryAtMillis; 30 | this.uid = uid; 31 | } 32 | 33 | public String id() { 34 | return id; 35 | } 36 | 37 | public String longUrl() { 38 | return longUrl; 39 | } 40 | 41 | public long createdAtMillis() { 42 | return createdAtMillis; 43 | } 44 | 45 | @Nullable 46 | public String uid() { 47 | return uid; 48 | } 49 | 50 | @Override 51 | public boolean equals(Object o) { 52 | if (this == o) return true; 53 | if (o == null || getClass() != o.getClass()) return false; 54 | Redirection redirection = (Redirection) o; 55 | return Objects.equals(id, redirection.id); 56 | } 57 | 58 | @Override 59 | public int hashCode() { 60 | return Objects.hash(id); 61 | } 62 | 63 | public long expiryAtMillis() { 64 | return expiryAtMillis; 65 | } 66 | 67 | public boolean isExpired(Clock clock) { 68 | return expiryAtMillis <= clock.millis(); 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return "Redirection{" + 74 | "id='" + id + '\'' + 75 | ", longUrl='" + longUrl + '\'' + 76 | ", createdAtMillis=" + createdAtMillis + 77 | ", expiryAtMillis=" + expiryAtMillis + 78 | ", uid='" + uid + '\'' + 79 | '}'; 80 | } 81 | 82 | public boolean isAnonymous() { 83 | return uid == null; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /bookmyshow/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /discourse/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /tiny-url-generator/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /tiny-url-generator/common-goodies/src/main/java/org/npathai/discovery/zookeeper/ZkServiceDiscoveryClientFactory.java: -------------------------------------------------------------------------------- 1 | package org.npathai.discovery.zookeeper; 2 | 3 | import org.apache.curator.x.discovery.ServiceDiscovery; 4 | import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; 5 | import org.apache.curator.x.discovery.ServiceProvider; 6 | import org.apache.curator.x.discovery.details.JsonInstanceSerializer; 7 | import org.npathai.discovery.DiscoveryException; 8 | import org.npathai.discovery.ServiceDiscoveryClient; 9 | import org.npathai.discovery.ServiceDiscoveryClientFactory; 10 | import org.npathai.zookeeper.ZkManager; 11 | 12 | import java.util.Objects; 13 | import java.util.Properties; 14 | 15 | public class ZkServiceDiscoveryClientFactory implements ServiceDiscoveryClientFactory { 16 | public static final String Z_NODE_PATH = "zNodePath"; 17 | 18 | private final ZkManager zkManager; 19 | 20 | public ZkServiceDiscoveryClientFactory(ZkManager zkManager) { 21 | this.zkManager = zkManager; 22 | } 23 | 24 | @Override 25 | public ServiceDiscoveryClient createDiscoveryClient(String serviceName, Properties properties) throws DiscoveryException { 26 | String zNodePath = Objects.requireNonNull(properties.getProperty(Z_NODE_PATH)); 27 | try { 28 | ServiceDiscovery serviceDiscovery = createServiceDiscovery(zNodePath); 29 | ServiceProvider serviceProvider = createServiceProvider(serviceName, serviceDiscovery); 30 | return new ZkServiceDiscoveryClient(serviceName, serviceDiscovery, serviceProvider); 31 | } catch (Exception ex) { 32 | throw new DiscoveryException(ex); 33 | } 34 | } 35 | 36 | private ServiceProvider createServiceProvider(String serviceName, ServiceDiscovery serviceDiscovery) throws Exception { 37 | ServiceProvider serviceProvider = serviceDiscovery.serviceProviderBuilder() 38 | .serviceName(serviceName) 39 | .build(); 40 | serviceProvider.start(); 41 | return serviceProvider; 42 | } 43 | 44 | private ServiceDiscovery createServiceDiscovery(String zNodePath) throws Exception { 45 | ServiceDiscovery serviceDiscovery = ServiceDiscoveryBuilder.builder(String.class) 46 | .client(zkManager.client()) 47 | .basePath(zNodePath) 48 | .serializer(new JsonInstanceSerializer<>(String.class)) 49 | .build(); 50 | serviceDiscovery.start(); 51 | return serviceDiscovery; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tiny-url-generator/user-service/src/main/java/org/npathai/auth/BasicAuthenticationProvider.java: -------------------------------------------------------------------------------- 1 | package org.npathai.auth; 2 | 3 | import at.favre.lib.crypto.bcrypt.BCrypt; 4 | import io.micronaut.security.authentication.AuthenticationFailed; 5 | import io.micronaut.security.authentication.AuthenticationProvider; 6 | import io.micronaut.security.authentication.AuthenticationRequest; 7 | import io.micronaut.security.authentication.AuthenticationResponse; 8 | import io.reactivex.Flowable; 9 | import org.apache.logging.log4j.Logger; 10 | import org.npathai.dao.DataAccessException; 11 | import org.npathai.dao.UserDao; 12 | import org.npathai.model.User; 13 | import org.reactivestreams.Publisher; 14 | 15 | import javax.inject.Singleton; 16 | import java.util.ArrayList; 17 | import java.util.Optional; 18 | 19 | import static org.apache.logging.log4j.LogManager.getLogger; 20 | 21 | @Singleton 22 | public class BasicAuthenticationProvider implements AuthenticationProvider { 23 | private static final Logger LOG = getLogger(BasicAuthenticationProvider.class); 24 | 25 | private final UserDao userDao; 26 | 27 | public BasicAuthenticationProvider(UserDao userDao) { 28 | this.userDao = userDao; 29 | } 30 | 31 | @Override 32 | public Publisher authenticate(AuthenticationRequest authenticationRequest) { 33 | LOG.debug("Authentication request received for user: {}", authenticationRequest.getIdentity()); 34 | 35 | Optional optionalUser; 36 | try { 37 | optionalUser = userDao.getUserByName(String.valueOf(authenticationRequest.getIdentity())); 38 | } catch (DataAccessException ex) { 39 | LOG.trace(ex); 40 | return Flowable.just(new AuthenticationFailed()); 41 | } 42 | 43 | if (optionalUser.isEmpty()) { 44 | return Flowable.just(new AuthenticationFailed()); 45 | } 46 | 47 | User user = optionalUser.get(); 48 | if (validateCredentials(authenticationRequest, user)) { 49 | return Flowable.just(new CustomUserDetails(user.getUsername(), new ArrayList<>(), user.getId())); 50 | } 51 | return Flowable.just(new AuthenticationFailed()); 52 | } 53 | 54 | /** 55 | * Uses bcrypt cryptographic hash for calculating and validating password hash 56 | */ 57 | private boolean validateCredentials(AuthenticationRequest authenticationRequest, User user) { 58 | return BCrypt.verifyer().verify(String.valueOf(authenticationRequest.getSecret()).toCharArray(), 59 | user.getPassword()).verified; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/test/java/org/npathai/cache/RedisRedirectionCacheTest.java: -------------------------------------------------------------------------------- 1 | package org.npathai.cache; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.npathai.config.RedisConfiguration; 6 | import org.npathai.model.Redirection; 7 | import org.testcontainers.containers.GenericContainer; 8 | import org.testcontainers.junit.jupiter.Container; 9 | import org.testcontainers.junit.jupiter.Testcontainers; 10 | import org.unitils.reflectionassert.ReflectionAssert; 11 | 12 | import java.util.Optional; 13 | 14 | import static org.assertj.core.api.Assertions.assertThat; 15 | 16 | @Testcontainers 17 | class RedisRedirectionCacheTest { 18 | 19 | private static final long CREATION_TIME = System.currentTimeMillis(); 20 | public static final String LONG_URL = "www.google.com"; 21 | private static final String ID = "AAAAA"; 22 | private static final String UNKNOWN_ID = "UNKNOWN_ID"; 23 | private static final String USER_ID = "test"; 24 | 25 | public static final Redirection ORIGINAL_REDIRECTION = new Redirection(ID, LONG_URL, CREATION_TIME, 26 | CREATION_TIME + 1000, USER_ID); 27 | 28 | @Container 29 | public GenericContainer redis = new GenericContainer<>("redis:latest") 30 | .withExposedPorts(6379); 31 | private RedisRedirectionCache redisRedirectionCache; 32 | 33 | @BeforeEach 34 | public void setUp() { 35 | RedisConfiguration redisConfiguration = new RedisConfiguration(); 36 | String containerIpAddress = redis.getContainerIpAddress(); 37 | int port = redis.getMappedPort(6379); 38 | redisConfiguration.setUrl(String.format("redis://%s:%d", containerIpAddress, port)); 39 | redisRedirectionCache = new RedisRedirectionCache(redisConfiguration); 40 | } 41 | 42 | @Test 43 | public void canGetCachedRedirection() { 44 | redisRedirectionCache.put(ORIGINAL_REDIRECTION); 45 | 46 | Optional foundRedirection = redisRedirectionCache.getById(ID); 47 | assertThat(foundRedirection).isNotEmpty(); 48 | ReflectionAssert.assertReflectionEquals(ORIGINAL_REDIRECTION, foundRedirection.get()); 49 | } 50 | 51 | @Test 52 | public void returnsEmptyOptionalIfKeyIsNotPresentInCache() { 53 | assertThat(redisRedirectionCache.getById(UNKNOWN_ID)).isEmpty(); 54 | } 55 | 56 | @Test 57 | public void deleteByIdRemovesTheElementFromCache() { 58 | redisRedirectionCache.put(ORIGINAL_REDIRECTION); 59 | 60 | redisRedirectionCache.deleteById(ORIGINAL_REDIRECTION.id()); 61 | 62 | assertThat(redisRedirectionCache.getById(ORIGINAL_REDIRECTION.id())).isEmpty(); 63 | } 64 | } -------------------------------------------------------------------------------- /tiny-url-generator/short-url-generator/src/main/java/org/npathai/DomainBeanFactory.java: -------------------------------------------------------------------------------- 1 | package org.npathai; 2 | 3 | import io.micrometer.core.instrument.MeterRegistry; 4 | import io.micronaut.context.BeanContext; 5 | import io.micronaut.context.annotation.Factory; 6 | import org.npathai.cache.RedirectionCache; 7 | import org.npathai.cache.RedisRedirectionCache; 8 | import org.npathai.client.AnalyticsServiceClient; 9 | import org.npathai.client.IdGenerationServiceClient; 10 | import org.npathai.config.MySqlDatasourceConfiguration; 11 | import org.npathai.config.RedisConfiguration; 12 | import org.npathai.config.UrlLifetimeConfiguration; 13 | import org.npathai.config.ZookeeperConfiguration; 14 | import org.npathai.dao.MySqlRedirectionDao; 15 | import org.npathai.dao.RedirectionDao; 16 | import org.npathai.domain.RedirectionHistoryService; 17 | import org.npathai.domain.UrlShortener; 18 | import org.npathai.zookeeper.DefaultZkManagerFactory; 19 | import org.npathai.zookeeper.ZkManager; 20 | 21 | import javax.inject.Inject; 22 | import javax.inject.Singleton; 23 | import java.sql.SQLException; 24 | import java.time.Clock; 25 | 26 | @Factory 27 | public class DomainBeanFactory { 28 | 29 | @Inject 30 | BeanContext beanContext; 31 | 32 | @Singleton 33 | public ZkManager createZkManager() throws InterruptedException { 34 | DefaultZkManagerFactory zkManagerFactory = new DefaultZkManagerFactory(); 35 | return zkManagerFactory.createConnected(beanContext.getBean(ZookeeperConfiguration.class).getUrl()); 36 | } 37 | 38 | @Singleton 39 | public RedirectionCache redirectionCache() { 40 | return new RedisRedirectionCache(beanContext.getBean(RedisConfiguration.class)); 41 | } 42 | 43 | @Singleton 44 | public RedirectionDao redirectionDao() throws SQLException { 45 | return new MySqlRedirectionDao(beanContext.getBean(MySqlDatasourceConfiguration.class), 46 | beanContext.getBean(MeterRegistry.class)); 47 | } 48 | 49 | @Singleton 50 | public UrlShortener urlShortener() { 51 | return new UrlShortener(beanContext.getBean(UrlLifetimeConfiguration.class), 52 | beanContext.getBean(IdGenerationServiceClient.class), 53 | beanContext.getBean(AnalyticsServiceClient.class), 54 | beanContext.getBean(RedirectionDao.class), 55 | beanContext.getBean(RedirectionCache.class), 56 | Clock.systemDefaultZone(), 57 | beanContext.getBean(MeterRegistry.class)); 58 | } 59 | 60 | @Singleton 61 | public RedirectionHistoryService redirectionHistoryService() { 62 | return new RedirectionHistoryService(beanContext.getBean(RedirectionDao.class)); 63 | } 64 | } 65 | --------------------------------------------------------------------------------