Updated readme with instructions
This commit is contained in:
		
							parent
							
								
									670cb24095
								
							
						
					
					
						commit
						72d33c21ca
					
				
							
								
								
									
										84
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										84
									
								
								README.md
									
									
									
									
									
								
							@ -1,64 +1,46 @@
 | 
			
		||||
# URL Shortener Take-Home Exercise (Fullstack)
 | 
			
		||||
 | 
			
		||||
## Instructions
 | 
			
		||||
 | 
			
		||||
Your task is to create a URL shortener web application (similar to [bitly](https://bitly.com/) or [TinyURL](https://tinyurl.com/)). This exercise is intentionally open-ended, and you are welcome to implement your solution using the language and tech stack of your choice. If you are familiar with React & Next.js, please use those for your submission. The core functionality of the application should be expressed through your own original code.
 | 
			
		||||
 | 
			
		||||
You should aim to spend no more than 2 hours on this project. If you don't complete everything in 2 hours, please submit what you have - we value your time and want to see your prioritization skills.
 | 
			
		||||
 | 
			
		||||
### Application Description
 | 
			
		||||
 | 
			
		||||
At the root path (e.g., http://localhost:8080/), a user should be presented with a form that allows them to input a URL. When a user submits that form, it should convert the input URL to a shortened version and present that to the user.
 | 
			
		||||
 | 
			
		||||
The shortened URL should be in the format: http://localhost:8080/{slug}, where `{slug}` is a unique identifier for the original URL.
 | 
			
		||||
 | 
			
		||||
When a user navigates to the shortened URL, they should be redirected to the original URL that was used to generate this shortened URL.
 | 
			
		||||
 | 
			
		||||
### Minimum Requirements
 | 
			
		||||
 | 
			
		||||
* Format and method of generating slugs for shortened URLs are up to you
 | 
			
		||||
* Shortened URLs do not need to persist across server shutdown/startup (i.e., setting up a DB isn't necessary - server memory should suffice)
 | 
			
		||||
* Only allow valid http(s) URLs
 | 
			
		||||
 | 
			
		||||
If you have additional time, consider spending it on testing or UI improvements as opposed to supplemental features.
 | 
			
		||||
 | 
			
		||||
## Evaluation Criteria
 | 
			
		||||
 | 
			
		||||
We will be evaluating your submission based on the following:
 | 
			
		||||
 | 
			
		||||
1. Functionality: Does the application work as described?
 | 
			
		||||
2. Code quality: Is the code clean, well-organized, and following best practices?
 | 
			
		||||
3. Error handling: How does the application handle invalid inputs or errors?
 | 
			
		||||
4. Technical choices: Are the chosen technologies appropriate for the task?
 | 
			
		||||
5. Documentation: Is the code well-commented and the README clear?
 | 
			
		||||
 | 
			
		||||
## Deliverables
 | 
			
		||||
 | 
			
		||||
Please fill out the sections below in the _README.md_ of your project and submit according to the instructions you received with this project. Your code can be sent as a zip file or a link to a repository containing your project.
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
# Url Shortener
 | 
			
		||||
 | 
			
		||||
## Implementation Details
 | 
			
		||||
 | 
			
		||||
<!-- Provide a short description of your implementation (technologies used, brief overview of project architecture, etc.) -->
 | 
			
		||||
I used Dart/Flutter to write the entire stack. Frontend is flutter compiled to web. Backend uses the [dart_frog](https://dartfrog.vgv.dev/) framework.
 | 
			
		||||
 | 
			
		||||
Architecture is a simple server that serves the static frontend files on `/` and requests to create shortened urls as well as the redirects.
 | 
			
		||||
 | 
			
		||||
Dart frog does file-based routing similar to Next.js. However, I was unable to get the static content served on `/` and get shortened urls to route to `/[slug]` so I added them to a sub-route `/u/[slug]`.
 | 
			
		||||
 | 
			
		||||
## How to Run
 | 
			
		||||
 | 
			
		||||
<!--
 | 
			
		||||
- Include instructions on how to run your implementation locally. Be sure to include any necessary setup steps, such as installing dependencies, as well as the commands to start the application.
 | 
			
		||||
-->
 | 
			
		||||
### Running with Docker (Simpler)
 | 
			
		||||
 | 
			
		||||
With docker installed, `cd` into the `new_backend/build/` directory where the `Dockerfile` is located. Build the docker image with
 | 
			
		||||
 | 
			
		||||
```sh
 | 
			
		||||
docker build -t <image_name> ./
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Once built, run it with:
 | 
			
		||||
```sh
 | 
			
		||||
docker run -d -p 8080:8080 <image_name>
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The container port is hard-coded, so you have to use the internal port 8080, the host can be whichever port.
 | 
			
		||||
 | 
			
		||||
### Running the local dev server
 | 
			
		||||
 | 
			
		||||
- [Install dart](https://dart.dev/get-dart)
 | 
			
		||||
- [Install dart_frog](https://dartfrog.vgv.dev/docs/overview)
 | 
			
		||||
 | 
			
		||||
Then run the backend with:
 | 
			
		||||
```sh
 | 
			
		||||
cd new_backend/ && dart pub get && dart_frog dev
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Testing
 | 
			
		||||
 | 
			
		||||
<!-- Describe how you tested your solution (automated testing, manual testing process, screenshots, etc.) -->
 | 
			
		||||
I wrote some unit tests to get the url shortening to work, and everything else was tested manually. I also include some asserts in the code to verify the various assumptions I made in the code.
 | 
			
		||||
 | 
			
		||||
## Tools Used
 | 
			
		||||
 | 
			
		||||
<!--
 | 
			
		||||
- Describe any tools you used in developing your solution (e.g. ChatGPT for generating ideas and styles)
 | 
			
		||||
- Note: The use of AI tools is not discouraged, but they should be used judiciously.
 | 
			
		||||
-->
 | 
			
		||||
I used various documentation sites, like the one for the dart_frog web framework, as well as the [MDN docs for response codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
Good luck, and we look forward to reviewing your submission!
 | 
			
		||||
I discovered from asking an LLM that I could use the redirection status codes to do the url resolving, but otherwise referenced the docs to decide to use 308.
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@ enum Status { ok, error }
 | 
			
		||||
 | 
			
		||||
class ShrinkRay {
 | 
			
		||||
  static const shrinkLength = 6;
 | 
			
		||||
  static const String shrinkChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._';
 | 
			
		||||
  static const String shrinkChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
 | 
			
		||||
 | 
			
		||||
  static ({Status status, String content}) shrinkUrl(String urlStr) {
 | 
			
		||||
    try {
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,6 @@ Future<Response> onRequest(RequestContext context) async {
 | 
			
		||||
        if (url == null) {
 | 
			
		||||
          return Response(statusCode: 401, body: 'Missing url');
 | 
			
		||||
        }
 | 
			
		||||
        await Future<void>.delayed(const Duration(seconds: 2));
 | 
			
		||||
        final res = ShrinkRay.shrinkUrl(url);
 | 
			
		||||
        switch (res.status) {
 | 
			
		||||
          case Status.ok:
 | 
			
		||||
@ -40,6 +39,13 @@ Future<Response> onRequest(RequestContext context) async {
 | 
			
		||||
        return Response(statusCode: 500, body: 'Unexpected error occurred');
 | 
			
		||||
      }
 | 
			
		||||
    case HttpMethod.get:
 | 
			
		||||
      // final maybeUrl = shrunkUrls[urlPath];
 | 
			
		||||
      // switch (maybeUrl) {
 | 
			
		||||
      //   case null:
 | 
			
		||||
      //     return Response(statusCode: 404);
 | 
			
		||||
      //   default:
 | 
			
		||||
      //     return Response(statusCode: 308, headers: {'Location': maybeUrl});
 | 
			
		||||
      // }
 | 
			
		||||
      final file = File(path.join(Directory.current.path, 'public', 'index.html'));
 | 
			
		||||
      if (!file.existsSync()) {
 | 
			
		||||
        return Response(body: 'Index Not found');
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,3 @@
 | 
			
		||||
import 'dart:convert';
 | 
			
		||||
 | 
			
		||||
import 'package:dart_frog/dart_frog.dart';
 | 
			
		||||
import 'package:new_backend/shrink_ray.dart';
 | 
			
		||||
 | 
			
		||||
@ -18,7 +16,7 @@ Future<Response> onRequest(RequestContext context, String urlPath) async {
 | 
			
		||||
        case null:
 | 
			
		||||
          return Response(statusCode: 404);
 | 
			
		||||
        default:
 | 
			
		||||
          return Response(statusCode: 302, headers: {'Location': maybeUrl});
 | 
			
		||||
          return Response(statusCode: 308, headers: {'Location': maybeUrl});
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -8,14 +8,6 @@ class _MockRequestContext extends Mock implements RequestContext {}
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
  group('GET /', () {
 | 
			
		||||
    test('responds with a 200 and "Welcome to Dart Frog!".', () {
 | 
			
		||||
      // final context = _MockRequestContext();
 | 
			
		||||
      // final response = route.onRequest(context);
 | 
			
		||||
      // expect(response.statusCode, equals(HttpStatus.ok));
 | 
			
		||||
      // expect(
 | 
			
		||||
      //   response.body(),
 | 
			
		||||
      //   completion(equals('Welcome to Dart Frog!')),
 | 
			
		||||
      // );
 | 
			
		||||
    });
 | 
			
		||||
    test('responds with a 200 and "Welcome to Dart Frog!".', () {});
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user