https://github.com/qaware/study-project-url-shortener
https://github.com/qaware/study-project-url-shortener
Last synced: 5 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/qaware/study-project-url-shortener
- Owner: qaware
- Created: 2025-02-06T10:41:25.000Z (12 months ago)
- Default Branch: main
- Last Pushed: 2025-02-25T13:23:47.000Z (11 months ago)
- Last Synced: 2025-04-19T23:29:05.499Z (9 months ago)
- Language: TypeScript
- Size: 449 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# UrlShortener
This project demonstrates a URL shortener application built with Angular and Python. The project is intended for educational and demonstrative purposes, providing a hands-on example of how to work with Angular, Python, Docker and some further tools (Taskfile, ...).
## Requirements
To use this project locally, the following dependencies have to be installed:
- Node.js version 22 including npm
- Python version 3.13
- Taskfile version 3.40
- Docker
## Tasks
The task for you is split into two categories:
1. Developing the application itself
2. Deploying it
But BEFORE you change and commit any code, create a new Git branch with a name in the format `student/your-name` by executing command:
```bash
git checkout -b
```
For more information about Git, visit section [Git](#git).
### 1. Application Development
The application which is to be completed is an URL shortener. A URL shortener is a tool that converts a long URL into a shorter, more manageable link. When entered, the shortened URL redirects users to the original destination, often while tracking click analytics.
In our scenario, a user can enter a long URL into an input field, which is then converted into a shorter URL. That shorter URL consists of the base-URL of your website, where the UI can be viewed (e.g. "localhost"), and a path appendix.
A short example:
```bash
http://base-url/appendix
```
Store the appendix key on the backend. This key serves as a shorthand for a full URL. When a user enters `http://base-url/appendix` in their browser, they will be redirected to the associated full URL. For example, the full URL might be “www.youtube.com”.
#### Primary Task
The backend of the application is not complete yet. The required endpoints which are called by the frontend are not implemented (take a look at file `src/backend/main.py`).
The following two Python-endpoints (in file `src/backend/main.py`) have to be implemented for this task:
- `shorten_url`
- `get_long_url`
You can test the functionality of those endpoints locally by utilizing the Taskfile task `execute-shorten-local` to shorten an URL and task `execute-get-long-url-local` to retrieve the original long version of a short URL.
Also, there already are working test-endpoints within `main.py` with corresponding test-tasks (enter `task` in the terminal to list all tasks) which you can use to get a better understanding of how the unimplemented endpoints could be implemented.
#### Optional QR Code Implementation
Optionally, the QR-Code endpoint `get_qr_code` can be implemented. It should take a URL parameter, generate a QR code from it, encode that QR code as a base64 string, and then return the result.
### 2. Platform Engineering
We want to deploy our application to the cloud somewhere, preferably somewhere more permanent than a temporary Cloud IDE instance.
For the purposes of our workshop, we'll be deploying containers as Google Cloud Run services.
First, we should verify that our application is running as expected locally using Docker (ctrl+f "docker build" and "docker run" below, and that we can access the application on localhost:80 (or is it localhost:8080?)).
Next, we'll push our image to our Google "Artifact Registry" (GAR), which is a home for container images. The address of our GAR is stored in the environment variable GAR.
```bash
docker build $GAR/my-unique-image-name .
docker push $GAR/my-unique-image-name
```
Then, we'll create a Google Cloud Run service based on our image.
```bash
gcloud run deploy --image $GAR/my-unique-image-name --allow-unauthenticated --port 8080
```
Extra credit: Can you figure out
1. What images have been pushed to the registry? (see https://cloud.google.com/sdk/gcloud/reference/artifacts)
1. What Cloud Run Services are running in our project? Are any of them broken?
Finally, let's create a short URL for our service:
```bash
gcloud beta run domain-mappings create --service my-service-name --domain=my-fancy-domain.0qa.pw
```
(Warning: This takes at least 15 minutes).
## Usage
This section contains basic information about how the project shall be used.
You can list predefined tasks by running `task` in the terminal. This lists all defined Taskfile tasks.
### Git
Git is a distributed version control system that allows you to track changes in your code, collaborate with others, and maintain a complete history of your project. This allows, for example, to come back to a previous code version late ron. It is an essential tool for modern software development. Here are some basic commands and best practices to get you started:
#### Create a Branch
Always create a new branch for your work to keep changes isolated:
```bash
git checkout -b some-branch-name/comes-also-with-slashes
```
#### Make Changes and Commit
After editing files, stage and commit your changes with a meaningful message:
```bash
git add .
git commit -m "Describe your changes here"
```
#### Push Your Branch
Share your branch with others or just back up your changes by pushing it to the remote repository:
```bash
git push origin your-branch-name
```
#### Update branch
Get the newest version of your branch:
```bash
git pull origin main
```
#### List Commits
You might want to list commits to apply actions based on previous commits (like resetting to a specific commit):
```bash
git log
```
#### Resetting a Branch to a Commit
If you might have implemented some trash and want to reset to an earlier state:
```bash
git reset
```
The `commit-hash` can be taken from listed commits. See [List Commits](#list-commits).
### Build & Run the Application
During application development, multiple environments are typically established — one or more for testing and development, and another for production. The “production” environment refers to the configuration in which the final end user interacts with the application.
Also, the production build of the application includes optimizations and security steps which make sure that the application runs smoothly. The development-build on the other hand, is less performant but offers advantages such as easier debugging.
Distinct workflows are defined for each stage — development and production — to build and run the application:
#### Development
For development, the application runs in your local environment. Also, the build step of the application is skipped, as it automatically re-builds every time a change in the code space is recognized.
Both the frontend and backend have to be running if you want to test your application in a whole. Execute the following commands to start both:
- `task run-frontend-dev` to start the frontend
- `task run-backend-dev` to start the backend
Execute the commands in separate terminals since the daemons are not attached by default.
(Opening a new terminal in VS Code or Github Codespaces is possible by pressing `Cmd + Shift + P` and then selecting "Terminal: Create new Terminal")
After the application is running, the UI can be viewed via the browser by visiting `http://localhost:80` and the backend can be reached via `http://localhost:8000`.
#### Production
For production, the application-image will be running within a Docker container on the machine of your choice.
##### Docker Images and Container
A Docker image is like a recipe or blueprint. It contains everything needed to run an application: the code, libraries, environment variables, and configuration files. However, the image itself is just a static file. It doesn’t run or do anything on its own.
A Docker container, on the other hand, is what you get when you “bake” the image into a running instance. Think of the image as a recipe for a cake, and the container as the actual cake you bake. You can use the same recipe (image) to bake many cakes (containers), and each one can run independently. Containers are the live, running environments where your application is executed, and they can have temporary storage and runtime state.
#### Using Docker
##### Building Images
To build the Docker image, execute the following command:
```bash
docker build -t url-shortener .
```
This command utilizes the `./Dockerfile` to create an image and tag it (`-t`) with the name "url-shortener".
##### Run Container
That image can then be used to start a docker container using the command
```bash
docker run -d -p 8080:8080 -p 8000:8000 --name url-shortener url-shortener
```
Below is a brief explanation of each part of the command.
- `-d`: Runs the container in detached mode, meaning that the terminal remains available
- `-p :`: Maps a specific port on your host machine to a different port inside the Docker container, allowing you to access the containerized service from outside the container. Essentially, any traffic sent to the host’s `` will be forwarded to the container’s ``.
- `--name url-shortener`: Applies the name "url-shortener" to the container. That allows us to easily reference the container later on when we want to apply actions to it, like stopping the container or viewing its logs.
##### Stop Container
To stop a container, execute the following command:
```bash
docker stop
```
The container-name references to the name we previously assigned when starting a docker container.
Keep in mind that only ONE container with the same name can exist at the same time. To re-start your application, you first have to stop AND delete the container.
##### Delete Container
To delete a container, execute the following command:
```bash
docker rm
```
##### Restart stopped Container
If you might want to restart a previously stopped container, you can do that by executing command:
```bash
docker start
```
##### Get information about Container
There are multiple ways to get information about running container.
You can access logs of a container by executing command
```bash
docker logs
```
You can also attach to the command line of a running container by executing command
```bash
docker exec -it sh
```
Here, `sh` defines the kind of terminal you want to use. In that case "shell".
To depict the overall stats of all running containers, regarding usage of memory and cpu computing power, execute command
```bash
docker stats -a
```
### Testing the Backend
The following Taskfile tasks have been designed to test the endpoints for shortening URLs and retrieving the corresponding long version of a previously shortened URL:
- `task execute-shorten-local`: Shortens an URL. To shortened you presonal URL, update the corresponding task within file `Request.yml` by replacing the string `` with your URL.
- `task execute-get-long-url-local`: Change the task within file `Request.yml` to use a previously shortened URL. For that, simply replace the string `` with the shortened code.
### Github Codespace
A working environment for Github Codespaces is defined within `./.devcontainer`. It includes all listed requirements in section [Requirements](#requirements)