Updated readme with instructions

This commit is contained in:
Nathan Anderson 2024-10-09 19:16:35 -06:00
parent 670cb24095
commit 72d33c21ca
5 changed files with 43 additions and 65 deletions

View File

@ -1,64 +1,46 @@
# URL Shortener Take-Home Exercise (Fullstack) # Url Shortener
## 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.
---
## Implementation Details ## 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 ## How to Run
<!-- ### Running with Docker (Simpler)
- 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.
--> 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 ## 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 ## Tools Used
<!-- 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).
- 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 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.
Good luck, and we look forward to reviewing your submission!

View File

@ -4,7 +4,7 @@ enum Status { ok, error }
class ShrinkRay { class ShrinkRay {
static const shrinkLength = 6; static const shrinkLength = 6;
static const String shrinkChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._'; static const String shrinkChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
static ({Status status, String content}) shrinkUrl(String urlStr) { static ({Status status, String content}) shrinkUrl(String urlStr) {
try { try {

View File

@ -21,7 +21,6 @@ Future<Response> onRequest(RequestContext context) async {
if (url == null) { if (url == null) {
return Response(statusCode: 401, body: 'Missing url'); return Response(statusCode: 401, body: 'Missing url');
} }
await Future<void>.delayed(const Duration(seconds: 2));
final res = ShrinkRay.shrinkUrl(url); final res = ShrinkRay.shrinkUrl(url);
switch (res.status) { switch (res.status) {
case Status.ok: case Status.ok:
@ -40,6 +39,13 @@ Future<Response> onRequest(RequestContext context) async {
return Response(statusCode: 500, body: 'Unexpected error occurred'); return Response(statusCode: 500, body: 'Unexpected error occurred');
} }
case HttpMethod.get: 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')); final file = File(path.join(Directory.current.path, 'public', 'index.html'));
if (!file.existsSync()) { if (!file.existsSync()) {
return Response(body: 'Index Not found'); return Response(body: 'Index Not found');

View File

@ -1,5 +1,3 @@
import 'dart:convert';
import 'package:dart_frog/dart_frog.dart'; import 'package:dart_frog/dart_frog.dart';
import 'package:new_backend/shrink_ray.dart'; import 'package:new_backend/shrink_ray.dart';
@ -18,7 +16,7 @@ Future<Response> onRequest(RequestContext context, String urlPath) async {
case null: case null:
return Response(statusCode: 404); return Response(statusCode: 404);
default: default:
return Response(statusCode: 302, headers: {'Location': maybeUrl}); return Response(statusCode: 308, headers: {'Location': maybeUrl});
} }
} }
} }

View File

@ -8,14 +8,6 @@ class _MockRequestContext extends Mock implements RequestContext {}
void main() { void main() {
group('GET /', () { group('GET /', () {
test('responds with a 200 and "Welcome to Dart Frog!".', () { 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!')),
// );
});
}); });
} }