diff --git a/Argparse_app/main.py b/boilerplates-argparse/main.py similarity index 100% rename from Argparse_app/main.py rename to boilerplates-argparse/main.py diff --git a/Click_app/Makefile b/boilerplates-click/Makefile similarity index 100% rename from Click_app/Makefile rename to boilerplates-click/Makefile diff --git a/Click_app/README.md b/boilerplates-click/README.md similarity index 100% rename from Click_app/README.md rename to boilerplates-click/README.md diff --git a/Click_app/pytest.ini b/boilerplates-click/pytest.ini similarity index 100% rename from Click_app/pytest.ini rename to boilerplates-click/pytest.ini diff --git a/Click_app/requirements.txt b/boilerplates-click/requirements.txt similarity index 100% rename from Click_app/requirements.txt rename to boilerplates-click/requirements.txt diff --git a/Click_app/setup.py b/boilerplates-click/setup.py similarity index 100% rename from Click_app/setup.py rename to boilerplates-click/setup.py diff --git a/Click_app/tests/__init__.py b/boilerplates-click/tests/__init__.py similarity index 100% rename from Click_app/tests/__init__.py rename to boilerplates-click/tests/__init__.py diff --git a/Click_app/yourapp/__init__.py b/boilerplates-click/yourapp/__init__.py similarity index 100% rename from Click_app/yourapp/__init__.py rename to boilerplates-click/yourapp/__init__.py diff --git a/Click_app/yourapp/yourapp.py b/boilerplates-click/yourapp/yourapp.py similarity index 100% rename from Click_app/yourapp/yourapp.py rename to boilerplates-click/yourapp/yourapp.py diff --git a/Concurrence_examples/README.md b/boilerplates-concurrence/README.md similarity index 100% rename from Concurrence_examples/README.md rename to boilerplates-concurrence/README.md diff --git a/Concurrence_examples/asyncio_simple_example.py b/boilerplates-concurrence/asyncio_simple_example.py similarity index 100% rename from Concurrence_examples/asyncio_simple_example.py rename to boilerplates-concurrence/asyncio_simple_example.py diff --git a/Concurrence_examples/concurrent_future_example.py b/boilerplates-concurrence/concurrent_future_example.py similarity index 100% rename from Concurrence_examples/concurrent_future_example.py rename to boilerplates-concurrence/concurrent_future_example.py diff --git a/Concurrence_examples/daemon_example.py b/boilerplates-concurrence/daemon_example.py similarity index 100% rename from Concurrence_examples/daemon_example.py rename to boilerplates-concurrence/daemon_example.py diff --git a/Concurrence_examples/deadlock_example.py b/boilerplates-concurrence/deadlock_example.py similarity index 100% rename from Concurrence_examples/deadlock_example.py rename to boilerplates-concurrence/deadlock_example.py diff --git a/Concurrence_examples/logging_example.py b/boilerplates-concurrence/logging_example.py similarity index 100% rename from Concurrence_examples/logging_example.py rename to boilerplates-concurrence/logging_example.py diff --git a/Concurrence_examples/multiprocessing_example.py b/boilerplates-concurrence/multiprocessing_example.py similarity index 100% rename from Concurrence_examples/multiprocessing_example.py rename to boilerplates-concurrence/multiprocessing_example.py diff --git a/Concurrence_examples/pool_example.py b/boilerplates-concurrence/pool_example.py similarity index 100% rename from Concurrence_examples/pool_example.py rename to boilerplates-concurrence/pool_example.py diff --git a/Concurrence_examples/race_coditions.py b/boilerplates-concurrence/race_coditions.py similarity index 100% rename from Concurrence_examples/race_coditions.py rename to boilerplates-concurrence/race_coditions.py diff --git a/Concurrence_examples/thread_example.py b/boilerplates-concurrence/thread_example.py similarity index 100% rename from Concurrence_examples/thread_example.py rename to boilerplates-concurrence/thread_example.py diff --git a/Concurrence_examples/threadpool_example.py b/boilerplates-concurrence/threadpool_example.py similarity index 100% rename from Concurrence_examples/threadpool_example.py rename to boilerplates-concurrence/threadpool_example.py diff --git a/Concurrence_examples/threads_with_queues.py b/boilerplates-concurrence/threads_with_queues.py similarity index 100% rename from Concurrence_examples/threads_with_queues.py rename to boilerplates-concurrence/threads_with_queues.py diff --git a/dash_app/.env_example b/boilerplates-dash/.env_example similarity index 100% rename from dash_app/.env_example rename to boilerplates-dash/.env_example diff --git a/dash_app/Makefile b/boilerplates-dash/Makefile similarity index 100% rename from dash_app/Makefile rename to boilerplates-dash/Makefile diff --git a/dash_app/Procfile b/boilerplates-dash/Procfile similarity index 100% rename from dash_app/Procfile rename to boilerplates-dash/Procfile diff --git a/dash_app/README.md b/boilerplates-dash/README.md similarity index 100% rename from dash_app/README.md rename to boilerplates-dash/README.md diff --git a/dash_app/app.py b/boilerplates-dash/app.py similarity index 100% rename from dash_app/app.py rename to boilerplates-dash/app.py diff --git a/dash_app/requirements.txt b/boilerplates-dash/requirements.txt similarity index 100% rename from dash_app/requirements.txt rename to boilerplates-dash/requirements.txt diff --git a/dash_app/wrappers/k8s_wrapper.py b/boilerplates-dash/wrappers/k8s_wrapper.py similarity index 100% rename from dash_app/wrappers/k8s_wrapper.py rename to boilerplates-dash/wrappers/k8s_wrapper.py diff --git a/dash_app/wrappers/settings.py b/boilerplates-dash/wrappers/settings.py similarity index 100% rename from dash_app/wrappers/settings.py rename to boilerplates-dash/wrappers/settings.py diff --git a/dash_app/wrappers/style.py b/boilerplates-dash/wrappers/style.py similarity index 100% rename from dash_app/wrappers/style.py rename to boilerplates-dash/wrappers/style.py diff --git a/Security_examples/README.md b/boilerplates-security/README.md similarity index 100% rename from Security_examples/README.md rename to boilerplates-security/README.md diff --git a/Security_examples/python_sniff_scanner/icmp_example.py b/boilerplates-security/python_sniff_scanner/icmp_example.py similarity index 100% rename from Security_examples/python_sniff_scanner/icmp_example.py rename to boilerplates-security/python_sniff_scanner/icmp_example.py diff --git a/Security_examples/python_sniff_scanner/main_example.py b/boilerplates-security/python_sniff_scanner/main_example.py similarity index 100% rename from Security_examples/python_sniff_scanner/main_example.py rename to boilerplates-security/python_sniff_scanner/main_example.py diff --git a/Security_examples/python_sniff_scanner/sniff_example.py b/boilerplates-security/python_sniff_scanner/sniff_example.py similarity index 100% rename from Security_examples/python_sniff_scanner/sniff_example.py rename to boilerplates-security/python_sniff_scanner/sniff_example.py diff --git a/Security_examples/python_sniff_scanner/udp_sender_example.py b/boilerplates-security/python_sniff_scanner/udp_sender_example.py similarity index 100% rename from Security_examples/python_sniff_scanner/udp_sender_example.py rename to boilerplates-security/python_sniff_scanner/udp_sender_example.py diff --git a/Security_examples/python_sockets/first_example.py b/boilerplates-security/python_sockets/first_example.py similarity index 100% rename from Security_examples/python_sockets/first_example.py rename to boilerplates-security/python_sockets/first_example.py diff --git a/Security_examples/python_sockets/netcat_example.py b/boilerplates-security/python_sockets/netcat_example.py similarity index 100% rename from Security_examples/python_sockets/netcat_example.py rename to boilerplates-security/python_sockets/netcat_example.py diff --git a/Security_examples/python_sockets/second_example.py b/boilerplates-security/python_sockets/second_example.py similarity index 100% rename from Security_examples/python_sockets/second_example.py rename to boilerplates-security/python_sockets/second_example.py diff --git a/Security_examples/python_sockets/tcp_server.py b/boilerplates-security/python_sockets/tcp_server.py similarity index 100% rename from Security_examples/python_sockets/tcp_server.py rename to boilerplates-security/python_sockets/tcp_server.py diff --git a/Security_examples/python_sockets/udp_client.py b/boilerplates-security/python_sockets/udp_client.py similarity index 100% rename from Security_examples/python_sockets/udp_client.py rename to boilerplates-security/python_sockets/udp_client.py diff --git a/Security_examples/python_sockets/udp_server.py b/boilerplates-security/python_sockets/udp_server.py similarity index 100% rename from Security_examples/python_sockets/udp_server.py rename to boilerplates-security/python_sockets/udp_server.py diff --git a/Security_examples/python_ssh_modules/paramiko_example.py b/boilerplates-security/python_ssh_modules/paramiko_example.py similarity index 100% rename from Security_examples/python_ssh_modules/paramiko_example.py rename to boilerplates-security/python_ssh_modules/paramiko_example.py diff --git a/Security_examples/python_ssh_modules/server_example.py b/boilerplates-security/python_ssh_modules/server_example.py similarity index 100% rename from Security_examples/python_ssh_modules/server_example.py rename to boilerplates-security/python_ssh_modules/server_example.py diff --git a/Security_examples/python_ssh_modules/ssh_client.py b/boilerplates-security/python_ssh_modules/ssh_client.py similarity index 100% rename from Security_examples/python_ssh_modules/ssh_client.py rename to boilerplates-security/python_ssh_modules/ssh_client.py diff --git a/Security_examples/python_ssh_modules/usage_example.py b/boilerplates-security/python_ssh_modules/usage_example.py similarity index 100% rename from Security_examples/python_ssh_modules/usage_example.py rename to boilerplates-security/python_ssh_modules/usage_example.py diff --git a/Test_Boilerplate/Makefile b/boilerplates-tests/Makefile similarity index 100% rename from Test_Boilerplate/Makefile rename to boilerplates-tests/Makefile diff --git a/Test_Boilerplate/README.md b/boilerplates-tests/README.md similarity index 100% rename from Test_Boilerplate/README.md rename to boilerplates-tests/README.md diff --git a/Test_Boilerplate/requirements.txt b/boilerplates-tests/requirements.txt similarity index 100% rename from Test_Boilerplate/requirements.txt rename to boilerplates-tests/requirements.txt diff --git a/Test_Boilerplate/setup.py b/boilerplates-tests/setup.py similarity index 100% rename from Test_Boilerplate/setup.py rename to boilerplates-tests/setup.py diff --git a/Test_Boilerplate/tests/example_test.py b/boilerplates-tests/tests/example_test.py similarity index 100% rename from Test_Boilerplate/tests/example_test.py rename to boilerplates-tests/tests/example_test.py diff --git a/Test_Boilerplate/tox.ini b/boilerplates-tests/tox.ini similarity index 100% rename from Test_Boilerplate/tox.ini rename to boilerplates-tests/tox.ini diff --git a/soundcloud/.gitkeep b/maze-puzzle/.gitkeep similarity index 100% rename from soundcloud/.gitkeep rename to maze-puzzle/.gitkeep diff --git a/maze-puzzle/README.md b/maze-puzzle/README.md new file mode 100644 index 0000000..5ccbd86 --- /dev/null +++ b/maze-puzzle/README.md @@ -0,0 +1,62 @@ +# Follower Maze Code Challenge Instructions + +Follower-Maze is a **client-server application** built for social networking. +Users follow other users, post messages, send private messages, unfollow users, and so on. +* Each of these actions is an _event_, created by an _event-source_ and sent to the _server_. +* Each event is processed by the _server_. +* The processing of the event may have follow-on effects on other _user-clients_. + +The **server** listens for two kinds of **clients**: +* **1 event source**, which emits events. +* **N user-clients** that connect to the server. + + +#### Client/Server Protocol + +* The protocol is line-based i.e. a LF control character terminates each message. +All strings are encoded in UTF-8. +* Data is transmitted over TCP sockets. +* The _event source_ connects on port 9090 and will start sending events as soon as the connection is accepted. +* The _user clients_ connect on port 9099 and identify themselves with their user ID. +For example, once connected a _user client_ may send down `2932\r\n`, +indicating that it is representing user 2932. +* After the identification is sent, +the user client starts waiting for events to be sent to them by the server. + +#### Events + +There are five possible events. +The table below describes payloads sent by the event source and what each represents: + +``` +| Payload | Sequence # | Type | From User Id | To User Id | +|---------------|------------|---------------|--------------|------------| +| 666|F|60|50 | 666 | Follow | 60 | 50 | +| 1|U|12|9 | 1 | Unfollow | 12 | 9 | +| 542532|B | 542532 | Broadcast | - | - | +| 43|P|32|56 | 43 | Private Msg | 32 | 56 | +| 634|S|32 | 634 | Status Update | 32 | - | +``` + +#### Event Rules + +* Each message begins with a sequence number. +* However, the event source _does not send events in any given order_. +In particular, sequence number has no effect on the order in which events are sent. +* Events _may_ generate notifications for _user clients_. +If there is a respective target _user client_ connected, +the notification is routed according to the following rules: + * Follow: Only the `To User Id` is notified + * A follow event from user A to user B means that user A now follows user B. + * Unfollow: No clients are notified + * An unfollow event from user A to user B means that user A stopped following user B. + * Broadcast: All connected _user clients_ are notified + * Private Message: Only the `To User Id` is notified + * Status Update: All current followers of the `From User ID` are notified +* If there are no _user clients_ connected for a user, +any notifications for them are currently ignored. +_User clients_ are notified of events _in the correct sequence-number order_, +regardless of the order in which the _event source_ sent them. + + + diff --git a/soundcloud/solution/src/clients/product_detail_client.py b/maze-puzzle/src/clients/product_detail_client.py similarity index 100% rename from soundcloud/solution/src/clients/product_detail_client.py rename to maze-puzzle/src/clients/product_detail_client.py diff --git a/soundcloud/solution/src/clients/product_search_client.py b/maze-puzzle/src/clients/product_search_client.py similarity index 100% rename from soundcloud/solution/src/clients/product_search_client.py rename to maze-puzzle/src/clients/product_search_client.py diff --git a/soundcloud/solution/src/service.py b/maze-puzzle/src/service.py similarity index 100% rename from soundcloud/solution/src/service.py rename to maze-puzzle/src/service.py diff --git a/soundcloud/solution/test/service_test.py b/maze-puzzle/tests/service_test.py similarity index 100% rename from soundcloud/solution/test/service_test.py rename to maze-puzzle/tests/service_test.py diff --git a/soundcloud/solution/.gitignore b/soundcloud/solution/.gitignore deleted file mode 100644 index 39b4889..0000000 --- a/soundcloud/solution/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -.python-version -__pycache__ -*.pyc diff --git a/soundcloud/solution/README.md b/soundcloud/solution/README.md deleted file mode 100644 index 5067f4b..0000000 --- a/soundcloud/solution/README.md +++ /dev/null @@ -1,50 +0,0 @@ -## SoundCloud backend engineering code review interview exercise - -### Introduction - -Welcome to the SoundCloud backend Pull Request review challenge! - -This repository contains a simulation of a microservice. The service is -intentionally simplistic. The code for the service is python, but only using -basic aspects of the language. -We intend this to read like pseudocode - and in particular -_we do not judge your submission based on your knowledge of Python_. Exposure to -any popular programming language(s) should be enough to effectively work this exercise. - -You will be reviewing Pull Request #1 (present under the "Pull Requests" tab). -It's meant to be a simplified simulation of the sort of code review you might -encounter in your everyday work. - -> Also see: [a quick introduction to GitHub pull requests](https://help.github.com/en/articles/about-pull-request-reviews#about-pull-request-reviews). - -## Your challenge - -The code should be robust and reasonably feature-complete. -We only want to merge this Pull Request once you and your (imaginary) colleague are confident that: - - - The implementation actually supports the requested feature. - - The implementation is likely to work, including proper handling of - easily-foreseeable corner cases. - - The implementation is not easy for a mailcious user to attack. - - Other engineers can productively and confidently change this code, meaning: - - The code is clear and straightforward to reason through. - - It has high-quality unit tests and good test coverage. - - It's well-organized and code elements have intention-revealing names. - - There are comments, where those are useful. - -In short, we are interested in gaining some insight into what is important to -you when you review code. We'll be concentrating especially on what you feel -is important to change, and the way you communicate that. Please provide change -requests, ask questions, and otherwise make comments as you normally would when -reviewing code. - -The only GitHub review features that matter in this exercise are -commenting-related: commenting on the whole Pull Request, and comments targeted at specific -areas of the code. If you prefer to make use of Github's markdown features -in your comments, please feel free to do so - but if you haven't used these features -before, please do not use them. - -As a general guideline, this exercise should take about 30-60 minutes. You are free -to take more (or less) time than that, however. - -When you're done, please notify your SoundCloud recruiter contact via email. \ No newline at end of file diff --git a/soundcloud/soundcloud_problem_statement/.DS_Store b/soundcloud/soundcloud_problem_statement/.DS_Store deleted file mode 100644 index fbc7e87..0000000 Binary files a/soundcloud/soundcloud_problem_statement/.DS_Store and /dev/null differ diff --git a/soundcloud/soundcloud_problem_statement/INSTRUCTIONS.md b/soundcloud/soundcloud_problem_statement/INSTRUCTIONS.md deleted file mode 100644 index 8e77fec..0000000 --- a/soundcloud/soundcloud_problem_statement/INSTRUCTIONS.md +++ /dev/null @@ -1,218 +0,0 @@ -# Follower Maze Code Challenge Instructions - -Thank you for applying for a backend engineering position at SoundCloud. -We ask engineering candidates to work and complete this code challenge. -Your submission is part of the basis upon which we evaluate your candidacy. - -We hope it’s indeed challenging (and maybe even fun). - -The challenge has two parts: -1. **Refactor** an existing client-server application and provide a README explaining your approach -1. **Extend** the server application with a small feature request - -Our aim with these instructions is to leave no doubt about the details of the challenge and the expectations of -your submission - however, if you have questions of any sort _please ask_. -Also: please completely read the instructions before starting! - -## Time Expectations - -Based on internal testing, we roughly estimate the full Follower-Maze challenge will take -somewhere around 2-7 hours of your time. - -* Our goal for this time budget is to keep the time investment predictable for candidates - -it is not intended to create significant “time pressure”. -* The length of time you take to submit your work is not a factor in how we review it. -* Many candidates will work the challenge and submit it back within a day or two. -Or some might submit it a week later, depending on personal circumstances. - -## Discretion -In order to ensure continued fairness to all past and future participants, -please do not share any of the details of this code challenge with others, -including the work you submit. - -## Anonymity -Code challenge submissions are treated as a form of [Blind Audition](https://en.wikipedia.org/wiki/Blind_audition): -reviewers don’t have access to a candidate’s personal information, -including name and background. -Please help us by keeping your submission free of information -that would be revealing of who you are, or any personal attributes. - -## Part 1: Follower Maze - Refactor - -### Overview - -Follower-Maze is a **client-server application** built for social networking. -Users follow other users, post messages, send private messages, unfollow users, and so on. -* Each of these actions is an _event_, created by an _event-source_ and sent to the _server_. -* Each event is processed by the _server_. -* The processing of the event may have follow-on effects on other _user-clients_. - -The **server** listens for two kinds of **clients**: -* **1 event source**, which emits events. -* **N user-clients** that connect to the server. - -The server routes certain events to certain clients, according to the social networking domain rules. -Please see the [Specification](#specification) section for more detail. - -### Running the End-to-End Tester - -Please see the repository [README](README.md) for instructions on how to run client/server implementations, -and the end-to-end tester. - - -You have the choice of one of six programming language implementations: -please choose the language you’re most comfortable in, -and try running the client/server implementation for that language, -and then run the end-to-end tester to successful completion. - -### Specification - -This specification is just for your reference. -The provided implementations are based on this specification. - - -#### Client/Server Protocol - -* The protocol is line-based i.e. a LF control character terminates each message. -All strings are encoded in UTF-8. -* Data is transmitted over TCP sockets. -* The _event source_ connects on port 9090 and will start sending events as soon as the connection is accepted. -* The _user clients_ connect on port 9099 and identify themselves with their user ID. -For example, once connected a _user client_ may send down `2932\r\n`, -indicating that it is representing user 2932. -* After the identification is sent, -the user client starts waiting for events to be sent to them by the server. - -#### Events - -There are five possible events. -The table below describes payloads sent by the event source and what each represents: - -``` -| Payload | Sequence # | Type | From User Id | To User Id | -|---------------|------------|---------------|--------------|------------| -| 666|F|60|50 | 666 | Follow | 60 | 50 | -| 1|U|12|9 | 1 | Unfollow | 12 | 9 | -| 542532|B | 542532 | Broadcast | - | - | -| 43|P|32|56 | 43 | Private Msg | 32 | 56 | -| 634|S|32 | 634 | Status Update | 32 | - | -``` - -#### Event Rules - -* Each message begins with a sequence number. -* However, the event source _does not send events in any given order_. -In particular, sequence number has no effect on the order in which events are sent. -* Events _may_ generate notifications for _user clients_. -If there is a respective target _user client_ connected, -the notification is routed according to the following rules: - * Follow: Only the `To User Id` is notified - * A follow event from user A to user B means that user A now follows user B. - * Unfollow: No clients are notified - * An unfollow event from user A to user B means that user A stopped following user B. - * Broadcast: All connected _user clients_ are notified - * Private Message: Only the `To User Id` is notified - * Status Update: All current followers of the `From User ID` are notified -* If there are no _user clients_ connected for a user, -any notifications for them are currently ignored. -_User clients_ are notified of events _in the correct sequence-number order_, -regardless of the order in which the _event source_ sent them. - -### Challenge -We have provided the client and server code - this is your starting point. -However, the code is in poor shape: -it’s condensed into giant blocks of code in a single file, -making it hard for you to understand, debug, and test. -**Your task is to refactor the code so that it’s _easy to test, -easy to understand and easy to maintain._** - -### Guidance - -**Time budget:** We roughly estimate this portion will take between 2-6 hours. - -The **primary goals** of this exercise are: -* To provide structure and clarity to the server code base by introducing appropriate abstractions. -* To produce code that accommodates change and is easy to test. -* To maintain correctness of the server program. -* To document your approach. - -Some **non-goals**: -* To introduce functionality that was not asked for. -* To make the solution production-ready. - * **HOWEVER** we ARE interested in what top improvements you think need to be made - to make it production-ready (see README details below). -* To write unit tests; your focus should be on writing testable code first, -and while you're free to write unit tests with this part of the exercise, -it is not a stated requirement here. - -## Part 2: Follower Maze - Extension - -### The Problem - -In Part 1, we asked you to refactor an existing solution to the follower-maze problem. -The original specification of the problem leaves the question of bad event data unanswered, -and it simplifies the case of undeliverable notifications in case of user disconnects. -Your task is to **add a [Dead Letter Queue (DLQ)](https://en.wikipedia.org/wiki/Dead_letter_queue) -to the server**, so that undeliverable or malformed messages aren't lost. - -“Dead letters” build up and ultimately must be dealt with. -For the purposes of this interview problem, -it's OK to simply log them to the console. -But please **include a short explanation in your README** of how you would store and process -that data in a production scenario. - - -### Guidance - -**Time budget:** We roughly estimate this portion will take between 1-2 hours. - -The **primary goals** of this exercise are: -* To introduce a DLQ implementation -* To capture messages that fall outside of the original specification of the protocol, for example: - * Malformed messages - * New message types not yet supported by the server -* To capture messages that cannot be delivered because the target user is not connected -* To write unit tests demonstrating the DLQ operates as specified -* To explain your approach to handling “dead letters” in production, in your README - -Some **non-goals**: - -* To introduce functionality or improvements that were not asked for -* To make the solution production-ready -(but you're welcome to comment on that aspect in your README file) - -## Constraints - -* Pick one of the six programming languages, -and base your solution on that language’s initial implementation. -* Please restrict yourself to the standard library - don’t introduce 3rd party libraries. -**Exception:** libraries that facilitate unit testing are acceptable. - -## Deliverables - -### Deliverables For Part 1 - -* Your refactored version of the code. -* A README file detailing: - * The approach you took and reasoning you applied to arrive at your final solution. - * Explanation of any design trade-offs or short-cuts you may have taken. - * How to run the server, in case you made changes to the setup we provided. - * The top priorities you would have for additions or modifications to make the solution production-ready. - -_Remember: unit tests are not required for Part 1_ - -### Deliverables For Part 2 -* A working implementation of a DLQ that handles messages falling into the categories specified above. -* Unit tests demonstrating the newly added functionality is working as specified. -* In your README: - * The approach you took and reasoning you applied to arrive at your final solution; -pay particular attention to design trade-offs or short-cuts you may have taken. - * Explanation of what more you would do with DLQ messages in a production scenario -(i.e. aside from simply logging them to the console). - -## Tips -* Run the end-to-end tester regularly as you change code. -* We're looking for a simple, straight-forward, clean & concise solution. -* If you have technical questions, please ask! - - diff --git a/soundcloud/soundcloud_problem_statement/README.md b/soundcloud/soundcloud_problem_statement/README.md deleted file mode 100644 index 427ddbf..0000000 --- a/soundcloud/soundcloud_problem_statement/README.md +++ /dev/null @@ -1,56 +0,0 @@ -**Please [read the instructions first](INSTRUCTIONS.md).** - - -## Running the server - -### go -``` -cd go -go run main.go -``` - -### java -``` -cd java -gradle run -``` - -### js -``` -cd js -node index.js -``` - -### python -Please use a modern version of python 3, managed via a tool like pyenv. -``` -cd python -python main.py -``` - -### ruby -Please use a modern version of cruby, managed via a tool like rvm. -``` -cd ruby -ruby main.rb -``` - -### scala -``` -cd scala -sbt run -``` - -## About the tester - -The tester makes socket connections to the server: - -- The event source connects on port 9090 and will start sending events as soon as the connection is accepted. -- The user clients can connect on port 9099, -and communicate with server following events specification and rules outlined in challenge instructions. - -## Running the tester - -From the project root, run: - -`tester/run100k.sh` diff --git a/soundcloud/soundcloud_problem_statement/go/go.mod b/soundcloud/soundcloud_problem_statement/go/go.mod deleted file mode 100644 index 263e5df..0000000 --- a/soundcloud/soundcloud_problem_statement/go/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module followermaze - -go 1.13 diff --git a/soundcloud/soundcloud_problem_statement/go/main.go b/soundcloud/soundcloud_problem_statement/go/main.go deleted file mode 100644 index c636938..0000000 --- a/soundcloud/soundcloud_problem_statement/go/main.go +++ /dev/null @@ -1,187 +0,0 @@ -package main - -import ( - "bufio" - "fmt" - "io" - "log" - "net" - "strconv" - "strings" -) - -const eventPort = 9090 -const clientPort = 9099 - -func main() { - clientPool := make(map[int]net.Conn) - followRegistry := map[int]map[int]bool{} - - seqNoToMessage := make(map[int][]string) - - go func() { - lastSeqNo := 0 - - eventListener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", eventPort)) - if err != nil { - log.Fatal(err) - } - defer eventListener.Close() - - fmt.Printf("Listening for events on %d\n", eventPort) - - outer: - for { - conn, err := eventListener.Accept() - - if err != nil { - log.Fatal(err) - } - defer conn.Close() - - reader := bufio.NewReader(conn) - - for { - payloadRaw, err := reader.ReadString('\n') - - if err == io.EOF { - conn.Close() - continue outer - - } else if err != nil { - log.Fatal(err) - } - - payload := strings.TrimSpace(payloadRaw) - - fmt.Printf("Message received: %s\n", payload) - - payloadParts := strings.Split(payload, "|") - - incomingSeqNo, err := strconv.Atoi(payloadParts[0]) - if err != nil { - log.Fatal(err) - } - - seqNoToMessage[incomingSeqNo] = payloadParts - - for { - - nextMessage, ok := seqNoToMessage[lastSeqNo+1] - delete(seqNoToMessage, lastSeqNo+1) - - if !ok { - break - } - - nextPayload := strings.Join(nextMessage, "|") + "\n" - kind := strings.TrimSpace(nextMessage[1]) - - switch kind { - case "F": - fromUserID, err := strconv.Atoi(nextMessage[2]) - if err != nil { - log.Fatal(err) - } - toUserID, err := strconv.Atoi(nextMessage[3]) - if err != nil { - log.Fatal(err) - } - - if _, ok := followRegistry[toUserID]; !ok { - followRegistry[toUserID] = make(map[int]bool) - } - - followers, _ := followRegistry[toUserID] - followers[fromUserID] = true - - clientConn, ok := clientPool[toUserID] - if ok { - fmt.Fprint(clientConn, nextPayload) - } - - case "U": - fromUserID, err := strconv.Atoi(nextMessage[2]) - if err != nil { - log.Fatal(err) - } - toUserID, err := strconv.Atoi(nextMessage[3]) - if err != nil { - log.Fatal(err) - } - - if followers, ok := followRegistry[toUserID]; ok { - delete(followers, fromUserID) - } - - case "P": - toUserID, err := strconv.Atoi(nextMessage[3]) - if err != nil { - log.Fatal(err) - } - - if clientConn, ok := clientPool[toUserID]; ok { - fmt.Fprint(clientConn, nextPayload) - } - - case "B": - for _, clientConn := range clientPool { - fmt.Fprint(clientConn, nextPayload) - } - - case "S": - fromUserID, err := strconv.Atoi(nextMessage[2]) - if err != nil { - log.Fatal(err) - } - - if followers, ok := followRegistry[fromUserID]; ok { - for follower := range followers { - clientConn, ok := clientPool[follower] - if ok { - fmt.Fprint(clientConn, nextPayload) - } - } - } - } - - lastSeqNo = lastSeqNo + 1 - } - } - } - }() - - eventListener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", clientPort)) - if err != nil { - log.Fatal(err) - } - defer eventListener.Close() - - fmt.Printf("Listening for client requests on %d\n", clientPort) - - for { - conn, err := eventListener.Accept() - if err != nil { - log.Fatal(err) - } - - reader := bufio.NewReader(conn) - - userIDRaw, err := reader.ReadString('\n') - - if err != nil { - log.Fatal(err) - } - - userIDStr := strings.TrimSpace(userIDRaw) - - userID, err := strconv.Atoi(userIDStr) - if err != nil { - log.Fatal(err) - } - - clientPool[userID] = conn - - fmt.Printf("User connected: %d (%d total)\n", userID, len(clientPool)) - } -} diff --git a/soundcloud/soundcloud_problem_statement/java/build.gradle b/soundcloud/soundcloud_problem_statement/java/build.gradle deleted file mode 100644 index d4d0c16..0000000 --- a/soundcloud/soundcloud_problem_statement/java/build.gradle +++ /dev/null @@ -1,26 +0,0 @@ -buildscript { - repositories { - mavenCentral() - jcenter() - } -} - -apply plugin: 'java' -apply plugin: 'application' - -mainClassName = "com.soundcloud.maze.Main" - -tasks.withType(JavaCompile) { - sourceCompatibility = 1.8 - targetCompatibility = 1.8 -} - -repositories { - mavenCentral() - jcenter() -} - -dependencies { - testCompile "junit:junit:4.12" - testCompile "org.assertj:assertj-core:1.7.1" -} diff --git a/soundcloud/soundcloud_problem_statement/java/gradlew.bat b/soundcloud/soundcloud_problem_statement/java/gradlew.bat deleted file mode 100644 index e95643d..0000000 --- a/soundcloud/soundcloud_problem_statement/java/gradlew.bat +++ /dev/null @@ -1,84 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/soundcloud/soundcloud_problem_statement/java/src/main/java/com/soundcloud/maze/Main.java b/soundcloud/soundcloud_problem_statement/java/src/main/java/com/soundcloud/maze/Main.java deleted file mode 100644 index ac45a13..0000000 --- a/soundcloud/soundcloud_problem_statement/java/src/main/java/com/soundcloud/maze/Main.java +++ /dev/null @@ -1,153 +0,0 @@ -package com.soundcloud.maze; - -import java.io.*; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -import static java.util.Collections.emptySet; - -public class Main { - - private final static int EVENT_PORT = 9090; - private final static int CLIENT_PORT = 9099; - - private static long lastSeqNo = 0L; - - public static void main(String[] args) { - - Map clientPool = new ConcurrentHashMap<>(); - Map> seqNoToMessage = new HashMap<>(); - - Map> followRegistry = new HashMap<>(); - - new Thread(() -> { - System.out.println("Listening for events on " + EVENT_PORT); - try (Socket eventSocket = new ServerSocket(EVENT_PORT).accept()) { - try (BufferedReader reader = new BufferedReader(new InputStreamReader(eventSocket.getInputStream()))) { - reader.lines().forEach(payload -> { - System.out.println("Message received: " + payload); - - List payloadParts = Arrays.asList(payload.split("\\|")); - seqNoToMessage.put(Long.parseLong(payloadParts.get(0)), payloadParts); - - while (seqNoToMessage.containsKey(lastSeqNo + 1)) { - List nextMessage = seqNoToMessage.get(lastSeqNo + 1); - String nextPayload = String.join("|", nextMessage); - - long seqNo = Long.parseLong(nextMessage.get(0)); - String kind = nextMessage.get(1); - - switch (kind) { - case "F": { - long fromUserId = Long.parseLong(nextMessage.get(2)); - long toUserId = Long.parseLong(nextMessage.get(3)); - - Set followers = followRegistry.getOrDefault(toUserId, new HashSet<>()); - followers.add(fromUserId); - followRegistry.put(toUserId, followers); - - try { - Socket socket = clientPool.get(toUserId); - if (socket != null) { - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); - writer.write(nextPayload + "\n"); - writer.flush(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - break; - - case "U": { - long fromUserId = Long.parseLong(nextMessage.get(2)); - long toUserId = Long.parseLong(nextMessage.get(3)); - - Set followers = followRegistry.getOrDefault(toUserId, new HashSet<>()); - followers.remove(fromUserId); - followRegistry.put(toUserId, followers); - } - break; - - case "P": { - long toUserId = Long.parseLong(nextMessage.get(3)); - - try { - Socket socket = clientPool.get(toUserId); - if (socket != null) { - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); - writer.write(nextPayload + "\n"); - writer.flush(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - break; - - case "B": { - clientPool.values().forEach(socket -> { - try { - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); - writer.write(nextPayload + "\n"); - writer.flush(); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } - break; - - case "S": { - long fromUserId = Long.parseLong(nextMessage.get(2)); - - Set followers = followRegistry.getOrDefault(fromUserId, emptySet()); - - followers.forEach(follower -> { - try { - Socket socket = clientPool.get(follower); - if (socket != null) { - BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); - writer.write(nextPayload + "\n"); - writer.flush(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - } - break; - } - - lastSeqNo = seqNo; - } - }); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - }).start(); - - new Thread(() -> { - System.out.println("Listening for client requests on " + CLIENT_PORT); - try { - ServerSocket serverSocket = new ServerSocket(CLIENT_PORT); - Socket clientSocket = serverSocket.accept(); - while (clientSocket != null) { - BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); - String userId = reader.readLine(); - if (userId != null) { - clientPool.put(Long.parseLong(userId), clientSocket); - System.out.println("User connected: " + userId + " (" + clientPool.size() + " total)"); - } - clientSocket = serverSocket.accept(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - }).start(); - - } -} diff --git a/soundcloud/soundcloud_problem_statement/js/index.js b/soundcloud/soundcloud_problem_statement/js/index.js deleted file mode 100644 index 3f4e06f..0000000 --- a/soundcloud/soundcloud_problem_statement/js/index.js +++ /dev/null @@ -1,124 +0,0 @@ -const net = require("net"); -const readline = require("readline"); - -const EVENT_PORT = 9090; -const CLIENT_PORT = 9099; - -const clientPool = {}; -const followRegistry = {}; - -let lastSeqNo = 0; - -net - .createServer(socket => { - const seqNoToMessage = {}; - const readInterface = readline.createInterface({ input: socket }); - readInterface.on("line", payload => { - console.log(`Message received: ${payload}`); - - const payloadParts = payload.split("|"); - seqNoToMessage[parseInt(payloadParts[0])] = payloadParts; - - while (seqNoToMessage[lastSeqNo + 1]) { - const nextMessage = seqNoToMessage[lastSeqNo + 1]; - const nextPayload = nextMessage.join("|"); - - const seqNo = parseInt(nextMessage[0]); - const kind = nextMessage[1]; - - switch (kind) { - case "F": - { - const fromUserId = parseInt(nextMessage[2]); - const toUserId = parseInt(nextMessage[3]); - - const followers = followRegistry[toUserId] || new Set([]); - followers.add(fromUserId); - followRegistry[toUserId] = followers; - - const socket = clientPool[toUserId]; - if (socket) { - socket.write(nextPayload + "\n"); - } - } - break; - - case "U": - { - const fromUserId = parseInt(nextMessage[2]); - const toUserId = parseInt(nextMessage[3]); - - const followers = followRegistry[toUserId] || new Set([]); - followers.delete(fromUserId); - followRegistry[toUserId] = followers; - } - break; - - case "P": - { - const toUserId = parseInt(nextMessage[3]); - - const socket = clientPool[toUserId]; - if (socket) { - socket.write(nextPayload + "\n"); - } - } - break; - - case "B": - { - for (let toUserId in clientPool) { - const socket = clientPool[toUserId]; - if (socket) { - socket.write(nextPayload + "\n"); - } - } - } - break; - - case "S": - { - const fromUserId = parseInt(nextMessage[2]); - - const followers = followRegistry[fromUserId] || new Set([]); - - followers.forEach(follower => { - const socket = clientPool[follower]; - if (socket) { - socket.write(nextPayload + "\n"); - } - }); - } - break; - } - - lastSeqNo = seqNo; - } - }); - }) - .listen(EVENT_PORT, "127.0.0.1", err => { - if (err) { - throw err; - } - console.log(`Listening for events on ${EVENT_PORT}`); - }); - -net - .createServer(clientSocket => { - const readInterface = readline.createInterface({ input: clientSocket }); - readInterface.on("line", userIdString => { - if (userIdString != null) { - clientPool[parseInt(userIdString)] = clientSocket; - - console.log( - `User connected: ${userIdString} (${clientPool.length} total)` - ); - } - }); - }) - .listen(CLIENT_PORT, "127.0.0.1", err => { - if (err) { - throw err; - } - console.log(`Listening for client requests on ${CLIENT_PORT}`); - }); diff --git a/soundcloud/soundcloud_problem_statement/js/package-lock.json b/soundcloud/soundcloud_problem_statement/js/package-lock.json deleted file mode 100644 index 659c758..0000000 --- a/soundcloud/soundcloud_problem_statement/js/package-lock.json +++ /dev/null @@ -1,988 +0,0 @@ -{ - "name": "js", - "version": "0.0.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - } - }, - "acorn": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.0.0.tgz", - "integrity": "sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ==", - "dev": true - }, - "acorn-jsx": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.2.tgz", - "integrity": "sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw==", - "dev": true - }, - "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", - "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", - "dev": true - }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true - }, - "cli-cursor": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", - "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", - "dev": true, - "requires": { - "restore-cursor": "^2.0.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.2.2.tgz", - "integrity": "sha512-mf0elOkxHbdyGX1IJEUsNBzCDdyoUgljF3rRlgfyYh0pwGnreLc0jjD6ZuleOibjmnUWZLY2eXwSooeOgGJ2jw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.2", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.1", - "esquery": "^1.0.1", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^11.7.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "inquirer": "^6.4.1", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", - "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", - "natural-compare": "^1.4.0", - "optionator": "^0.8.2", - "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - } - }, - "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.2.tgz", - "integrity": "sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.0.0" - } - }, - "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", - "dev": true - }, - "espree": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.1.tgz", - "integrity": "sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ==", - "dev": true, - "requires": { - "acorn": "^7.0.0", - "acorn-jsx": "^5.0.2", - "eslint-visitor-keys": "^1.1.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", - "dev": true, - "requires": { - "estraverse": "^4.0.0" - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - } - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "figures": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", - "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - } - }, - "flatted": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", - "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.0.0.tgz", - "integrity": "sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.1.0.tgz", - "integrity": "sha512-PpuksHKGt8rXfWEr9m9EHIpgyyaltBy8+eF6GJM0QCAxMgxCfucMF3mjecK2QsJr0amJW7gTqh5/wht0z2UhEQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "inquirer": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", - "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", - "dev": true, - "requires": { - "ansi-escapes": "^3.2.0", - "chalk": "^2.4.2", - "cli-cursor": "^2.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^2.0.0", - "lodash": "^4.17.12", - "mute-stream": "0.0.7", - "run-async": "^2.2.0", - "rxjs": "^6.4.0", - "string-width": "^2.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true - }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", - "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", - "dev": true, - "requires": { - "mimic-fn": "^1.0.0" - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.4", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "wordwrap": "~1.0.0" - } - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prettier": { - "version": "1.18.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz", - "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", - "dev": true - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "restore-cursor": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", - "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", - "dev": true, - "requires": { - "onetime": "^2.0.0", - "signal-exit": "^3.0.2" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "rxjs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", - "integrity": "sha512-HUb7j3kvb7p7eCUHE3FqjoDsC1xfZQ4AHFWfTKSpZ+sAhhz5X1WX0ZuUqWbzB2QhSLp3DoLUG+hMdEDKqWo2Zg==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } - } - }, - "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "v8-compile-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", - "dev": true - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - } - } -} diff --git a/soundcloud/soundcloud_problem_statement/js/package.json b/soundcloud/soundcloud_problem_statement/js/package.json deleted file mode 100644 index e6ef17e..0000000 --- a/soundcloud/soundcloud_problem_statement/js/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "js", - "version": "0.0.1", - "description": "follower maze refactoring challenge in javascript", - "main": "index.js", - "author": "SoundCloud", - "devDependencies": { - "prettier": "1.18.2", - "eslint": "6.2.2" - } -} diff --git a/soundcloud/soundcloud_problem_statement/python/README_new.md b/soundcloud/soundcloud_problem_statement/python/README_new.md deleted file mode 100644 index e69de29..0000000 diff --git a/soundcloud/soundcloud_problem_statement/python/main.py b/soundcloud/soundcloud_problem_statement/python/main.py deleted file mode 100644 index 6d17ccb..0000000 --- a/soundcloud/soundcloud_problem_statement/python/main.py +++ /dev/null @@ -1,98 +0,0 @@ -import socket -import threading - -EVENT_PORT = 9090 -CLIENT_PORT = 9099 -client_pool = {} -follow_registry = {} -seq_no_to_message = {} - -def event_server(): - last_seq_no = 0 - - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server: - server.bind(("127.0.0.1", EVENT_PORT)) - server.listen() - print("Listening for events on %d" % EVENT_PORT) - while True: - event_socket, address = server.accept() - print('Accepted connection from {}:{}'.format(address[0], address[1])) - with event_socket: - with event_socket.makefile() as socket_file: - for payload_line in socket_file: - payload = payload_line.strip() - print("Message received: %s" % payload) - payload_parts = payload.split("|") - - seq_no_to_message[int(payload_parts[0])] = payload_parts - - while last_seq_no + 1 in seq_no_to_message: - next_seq_no = last_seq_no + 1 - next_message = seq_no_to_message[next_seq_no] - del seq_no_to_message[next_seq_no] - - kind = next_message[1] - next_payload = "|".join(next_message) - - if kind == "F": - from_user_id = int(next_message[2]) - to_user_id = int(next_message[3]) - - if to_user_id not in follow_registry: - follow_registry[to_user_id] = set([]) - follow_registry[to_user_id].add(from_user_id) - - #if to_user_id in client_pool: - # client_pool[to_user_id].sendall(bytes(next_payload + "\n", "UTF-8")) - - elif kind == "U": - from_user_id = int(next_message[2]) - to_user_id = int(next_message[3]) - - if to_user_id not in follow_registry: - follow_registry[to_user_id] = set([]) - follow_registry[to_user_id].remove(from_user_id) - - elif kind == "P": - to_user_id = int(next_message[3]) - - if to_user_id in client_pool: - client_pool[to_user_id].sendall(bytes(next_payload + "\n", "UTF-8")) - - elif kind == "B": - for client_id in client_pool: - client_pool[client_id].sendall(bytes(next_payload + "\n", "UTF-8")) - - elif kind == "S": - from_user_id = int(next_message[2]) - - if from_user_id in follow_registry: - for follower in follow_registry[from_user_id]: - if follower in client_pool: - client_pool[follower].sendall(bytes(next_payload + "\n", "UTF-8")) - - last_seq_no = next_seq_no - -def client_connection_server(): - with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server: - server.bind(("127.0.0.1", CLIENT_PORT)) - server.listen() - print("Listening for client requests on %d" % CLIENT_PORT) - while True: - client_socket, address = server.accept() - with client_socket.makefile() as f: - user_id_string = f.readline() - if user_id_string: - user_id = int(user_id_string) - client_pool[user_id] = client_socket - print("User connected: %d (%d total)" % (user_id, len(client_pool))) - -if __name__ == "__main__": - thread1 = threading.Thread(target=event_server) - thread1.start() - - thread2 = threading.Thread(target=client_connection_server) - thread2.start() - - thread1.join() - thread2.join() diff --git a/soundcloud/soundcloud_problem_statement/python/main_new.py b/soundcloud/soundcloud_problem_statement/python/main_new.py deleted file mode 100644 index e69de29..0000000 diff --git a/soundcloud/soundcloud_problem_statement/ruby/main.rb b/soundcloud/soundcloud_problem_statement/ruby/main.rb deleted file mode 100644 index 40f1475..0000000 --- a/soundcloud/soundcloud_problem_statement/ruby/main.rb +++ /dev/null @@ -1,106 +0,0 @@ -require 'set' -require 'socket' - -EVENT_PORT = 9090 -CLIENT_PORT = 9099 - -client_pool = {} -seq_no_to_message = {} -follow_registry = {} -last_seq_no = 0 - -thread1 = Thread.new do - puts("Listening for events on #{EVENT_PORT}") - server = TCPServer.open(EVENT_PORT) - loop do - Thread.fork(server.accept) do |event_socket| - event_socket.each_line do |payload| - puts("Message received: #{payload}") - - payload_parts = payload.split('|') - seq_no_to_message[payload_parts[0].to_i] = payload_parts - - while seq_no_to_message[last_seq_no + 1] - next_message = seq_no_to_message[last_seq_no + 1] - next_payload = next_message.join('|') - - seq_no = next_message[0].to_i - kind = next_message[1].strip - - case kind - when 'F' - from_user_id = next_message[2].to_i - to_user_id = next_message[3].to_i - - followers = follow_registry[to_user_id] || Set.new - followers << from_user_id - follow_registry[to_user_id] = followers - - socket = client_pool[to_user_id] - if socket - socket.puts(next_payload) - socket.flush - end - - when 'U' - from_user_id = next_message[2].to_i - to_user_id = next_message[3].to_i - - followers = follow_registry[to_user_id] || Set.new - followers.delete(from_user_id) - follow_registry[to_user_id] = followers - - when 'P' - to_user_id = next_message[3].to_i - - socket = client_pool[to_user_id] - if socket - socket.puts(next_payload) - socket.flush - end - - when 'B' - client_pool.values.each do |socket| - socket.puts(next_payload) - socket.flush - end - - when 'S' - from_user_id = next_message[2].to_i - - followers = follow_registry[from_user_id] || Set.new - followers.each do |follower| - socket = client_pool[follower] - - if socket - socket.puts(next_payload) - socket.flush - end - end - end - - last_seq_no = seq_no - end - end - event_socket.close - end - end -end - -thread2 = Thread.new do - puts("Listening for client requests on #{CLIENT_PORT}") - server = TCPServer.open(CLIENT_PORT) - loop do - Thread.fork(server.accept) do |socket| - user_id_string = socket.gets - if user_id_string - user_id = user_id_string.to_i - client_pool[user_id] = socket - puts("User connected: #{user_id} (#{client_pool.size} total)") - end - end - end -end - -thread1.join -thread2.join diff --git a/soundcloud/soundcloud_problem_statement/scala/build.sbt b/soundcloud/soundcloud_problem_statement/scala/build.sbt deleted file mode 100644 index 3425b57..0000000 --- a/soundcloud/soundcloud_problem_statement/scala/build.sbt +++ /dev/null @@ -1,3 +0,0 @@ -name := "maze" - -scalaVersion := "2.12.8" diff --git a/soundcloud/soundcloud_problem_statement/scala/project/build.properties b/soundcloud/soundcloud_problem_statement/scala/project/build.properties deleted file mode 100644 index 7609b47..0000000 --- a/soundcloud/soundcloud_problem_statement/scala/project/build.properties +++ /dev/null @@ -1 +0,0 @@ -sbt.version = 1.2.8 \ No newline at end of file diff --git a/soundcloud/soundcloud_problem_statement/scala/src/main/scala/com/soundcloud/maze/Main.scala b/soundcloud/soundcloud_problem_statement/scala/src/main/scala/com/soundcloud/maze/Main.scala deleted file mode 100644 index c3405ed..0000000 --- a/soundcloud/soundcloud_problem_statement/scala/src/main/scala/com/soundcloud/maze/Main.scala +++ /dev/null @@ -1,140 +0,0 @@ -package com.soundcloud.maze - -import java.io._ -import java.net.{ServerSocket, Socket} - -import scala.collection.concurrent.TrieMap -import scala.collection.mutable -import scala.concurrent.duration.Duration -import scala.concurrent.{Await, ExecutionContext, Future} -import scala.io.Source -import scala.util.Try - -import scala.collection.JavaConverters._ - -object Main { - - private val EventPort = 9090 - private val ClientPort = 9099 - - private var lastSeqNo = 0L - - def main(args: Array[String]): Unit = { - - val clientPool = new TrieMap[Long, Socket] - - val messagesBySeqNo = new mutable.HashMap[Long, List[String]] - val followRegistry = new mutable.HashMap[Long, Set[Long]] - - implicit val ec = ExecutionContext.global - - val eventsAsync = Future { - - println(s"Listening for events on $EventPort") - val eventSocket = new ServerSocket(EventPort).accept() - - Try { - val reader = new BufferedReader(new InputStreamReader(eventSocket.getInputStream())) - - Try { - reader.lines().iterator().asScala.foreach { payload => - println(s"Message received: $payload") - val message = payload.split("\\|").toList - - messagesBySeqNo += message(0).toLong -> message - - while (messagesBySeqNo.get(lastSeqNo + 1L).isDefined) { - val nextMessage = messagesBySeqNo(lastSeqNo + 1) - - messagesBySeqNo -= lastSeqNo + 1L - - val nextPayload = nextMessage.mkString("|") - val seqNo = nextMessage(0).toLong - val kind = nextMessage(1) - - kind match { - case "F" => - val fromUserId = nextMessage(2).toLong - val toUserId = nextMessage(3).toLong - val followers = followRegistry.getOrElse(toUserId, Set.empty) - val newFollowers = followers + fromUserId - - followRegistry.put(toUserId, newFollowers) - - clientPool.get(toUserId).foreach { socket => - val writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) - writer.write(s"$nextPayload\n") - writer.flush() - } - - case "U" => - val fromUserId = nextMessage(2).toLong - val toUserId = nextMessage(3).toLong - val followers = followRegistry.getOrElse(toUserId, Set.empty) - val newFollowers = followers - fromUserId - - followRegistry.put(toUserId, newFollowers) - - case "P" => - val toUserId = nextMessage(3).toLong - - clientPool.get(toUserId).foreach { socket => - val writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) - writer.write(s"$nextPayload\n") - writer.flush() - } - - case "B" => - clientPool.values.foreach { socket => - val writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) - writer.write(s"$nextPayload\n") - writer.flush() - } - - case "S" => - val fromUserId = nextMessage(2).toLong - val followers = followRegistry.getOrElse(fromUserId, Set.empty) - - followers.foreach { follower => - clientPool.get(follower).foreach { socket => - val writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())) - writer.write(s"$nextPayload\n") - writer.flush() - } - } - } - - lastSeqNo = seqNo - } - } - } - if (reader != null) reader.close() - } - if (eventSocket != null) eventSocket.close() - } - - val clientsAsync = Future { - - println(s"Listening for client requests on $ClientPort") - val serverSocket = new ServerSocket(ClientPort) - var maybeClientSocket = Option(serverSocket.accept()) - - while (maybeClientSocket.nonEmpty) { - maybeClientSocket.foreach { clientSocket => - val bufferedSource = Source.fromInputStream(clientSocket.getInputStream()) - val userId = bufferedSource.bufferedReader().readLine() - - if (userId != null) { - clientPool.put(userId.toLong, clientSocket) - println(s"User connected: $userId (${clientPool.size} total)") - } - - maybeClientSocket = Option(serverSocket.accept()) - } - } - } - - Await.result(Future.sequence(Seq(eventsAsync, clientsAsync)), Duration.Inf) - } - -} diff --git a/soundcloud/soundcloud_problem_statement/tester/follower-maze-2.0.jar b/soundcloud/soundcloud_problem_statement/tester/follower-maze-2.0.jar deleted file mode 100644 index a2876a6..0000000 Binary files a/soundcloud/soundcloud_problem_statement/tester/follower-maze-2.0.jar and /dev/null differ diff --git a/soundcloud/soundcloud_problem_statement/tester/run100k.sh b/soundcloud/soundcloud_problem_statement/tester/run100k.sh deleted file mode 100755 index 4c97b1f..0000000 --- a/soundcloud/soundcloud_problem_statement/tester/run100k.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -ex - -this_dir=$(dirname $0) -export totalEvents=100000 -export concurrencyLevel=1 -time java -server -Xmx1G -jar "${this_dir}/follower-maze-2.0.jar" diff --git a/soundcloud/soundcloud_problem_statement/tester/run30.sh b/soundcloud/soundcloud_problem_statement/tester/run30.sh deleted file mode 100755 index 62ea2cf..0000000 --- a/soundcloud/soundcloud_problem_statement/tester/run30.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -ex - -this_dir=$(dirname $0) -export totalEvents=30 -export concurrencyLevel=1 -time java -server -Xmx1G -jar "${this_dir}/follower-maze-2.0.jar"