This was created as a reply to a challenge issued by Mottu (https://mottu.com.br/).
Here's the original challenge (in Portuguese):
Imagine um cenário de uma startup que precisa criar um novo serviço para resolver um problema da Mottu. Por aqui, trabalhamos com aplicações em REST API que disponibilizam informações para outros sistemas. Voc&e será avaliado em vários conceitos importantes de desenvolvimento.
-
O desafio proposto consiste no desenvolvimento de um encurtador de URL que deve ser implementado como back-end juntamente com um validador capaz de indicar se o link gerado está funcionando. Esse desenvolvimento é baseado em API`s REST.
-
É esperado, através da implementação, que seja possível além de encurtar a URL e ao acessá-la, o número de acessos incremente-se para contagem de acessos.
-
Para cada criação de uma URL será necessário disparar um evento que futuramente poderá ser integrado em algum sistema de mensageria, inicialmente você pode disparar esse evento para um cache local ou para alguma fila, se a URL já existir não há necessidade de disparo do evento.
-
Todas as informações devem ser salvas em um banco de dados PostGres e desenvolvido em C#
Toda entrega será realizada através de uma PR para um GIT da Mottu ou envio de um link do git pessoal contendo que deve estar público para facilitar o acesso
Critérios de aceite
Alguns critérios de aceitação serão validados para conclusão do projeto.
-
[Fluxo] - Execução completa de um fluxo considerando as API’s de cadastro e consulta de URL’s encurtadas
-
[Código] - O c�digo compartilhado dever� funcionar (rodar) e representar os itens listados
-
[Docker] imagens devem estar acess�veis para valida��o.
-
Garanta um README para rodar seu projeto, importante que ele funcione ao ser executado
-
Não temos um prazo estipulado para a solução desse desafio, mas acreditamos que você não deveria gastar mais do que 10 horas nele. É importante que você alinhe o prazo de entrega com seu contato na Mottu.
-
SE NOTARMOS QUE O CÓDIGO FOI COPIADO E COLADO, SERÁ DESQUALIFICADO AUTOMATICAMENTE.
-
Boa documentação (como rodar o projeto e outras observações)
-
Estrutura do código, facilidade de leitura, boas práticas de desenvolvimento
-
Consumir o JSON urls.json no back-end para a seção TOP 5 como o estado inicial da aplicação;
-
Criar outros endpoints importantes para o encurtador que voc� julgar necessário
Boa sorte! =]
[
{
"id": "23094",
"hits": 5,
"url": "http://globo.com",
"shortUrl": "http://chr.dc/9dtr4"
},
{
"id": "76291",
"hits": 4,
"url": "http://google.com",
"shortUrl":
"http://chr.dc/aUx71"
},
{
"id": "66761",
"hits": 7,
"url":
"http://terra.com.br",
"shortUrl": "http://chr.dc/u9jh3"
},
{
"id": "70001",
"hits": 1,
"url": "http://facebook.com",
"shortUrl": "http://chr.dc/qy61p"
},
{
"id": "21220",
"hits": 2,
"url": "http://diariocatarinense.com.br",
"shortUrl": "http://chr.dc/87itr"
},
{
"id": "10743",
"hits": 0,
"url":
"http://uol.com.br",
"shortUrl": "http://chr.dc/y81xc"
},
{
"id": "19122",
"hits": 2,
"url": "http://chaordic.com.br",
"shortUrl": "http://chr.dc/qy5k9" },
{
"id": "55324",
"hits": 4,
"url": "http://youtube.com",
"shortUrl": "http://chr.dc/1w5tg"
},
{
"id": "70931",
"hits": 5,
"url": "http://twitter.com",
"shortUrl": "http://chr.dc/7tmv1"
},
{
"id": "87112",
"hits": 2,
"url": "http://bing.com",
"shortUrl": "http://chr.dc/9opw2"
}
]
This URL shortener was developed using Visual Studio 2022 Community Edition, configured to run in Docker Desktop (configured as Linux).
The Docker configuration is the default for a WebAPI project. No change was made to the Dockerfile
.
In the folder named Data
there are four files:
Database Schema.dbml
ShortURLs.MySQL.sql
ShortURLs.PostgreSQL.sql
ShortURLs.SQL Server.sql
The first file is writen in the Database Markup Language, used by dbdiagrams.io, which describes a database schema without biding it to a single DBMS.
The next three files describe the same database schema for different DBMS.
Choose the appropriated one for your DBMS (or create a new one using the DBML file) and execute in your favorite SQL management program.
A Connection String is a text containing parameters and values that specifies how the project will connect to the database, including details like the server address, authentication credentials, and database name.
You'll need to inform, that is configure, a Connection String for the project to work correctly.
To do this you’ll have to edit a file named appsettings.json
, located at the project root folder. This file contains all the configuration for the project to run.
Open this file in your favorite simple text editor (I recommend Notepad++ ➨ https://notepad-plus-plus.org), locate the section named ConnectionStrings
, and within it locate the name-value pair named db
.
Once you find this, change its value to the appropriated Connection String for your database.
In this project I used NHibernate (https://nhibernate.info/) for database connection. It's a personal preference due the fact that I've been using it for a long time now.
In the appsettings.json
file, locate the section named Hibernate
, and within it there are two values that need to be changed, according to the following table:
DBMS | Driver | Dialect |
---|---|---|
MySQL | NHibernate.Driver.MySqlDataDriver | NHibernate.Dialect.MySQLDialect |
PostgreSQL | NHibernate.Driver.NpgsqlDriver | NHibernate.Dialect.PostgreSQLDialect |
SQL Server | NHibernate.Driver.SqlClientDriver | NHibernate.Dialect.MsSql2012Dialect |
Choose the correct one for your DBMS, and change the values accordingly.
The file NHibernate.map.xml
contains the mapping configuration for a PostgreSQL database.
If you're using a different database, you will need to change the generator
element accordingly.
MySQL and SQL Server
<generator class="identity"/>
PostgreSQL
<generator class="sequence">
<param name="sequence">"ShortUrls_id_seq"</param>
</generator>
Further reference can be found here.
The urls.json
file, which will be used to configure the initial state of the database, if used, MUST be placed in the Data
folder.
At the beginning of the execution, if this file is located at the proper folder, it will be consumed and all valid urls will be added to the database, if possible.
Any exception or impossibility of adding any URL to the database will be logged to the file urls_json.log
, in the same directory.
After consuming the JSON file, it will be renamed to urls.old
with a sequential number if the .old
file already exists.
This project depends on the Docker Desktop, that needs to be installed in the machine that will execute this project.
Once the project has been loaded in the Visual Studio, simply start the execution, Visual Studio will take care of the Docker installation, configuration, and execution.
Afterwards your default browser will open with an instance of Swagger
that allows you to try executing each one of the endpoints.
Calling the Get /{shortUrl}
from the Swagger
with or without a valid short URL will result in an error, as this call assumes a user is calling from a browser.
In a browser that call will redirect the user to the proper URL, and add the hits counter for that short URL.
All other end points call be executed from Swagger
.
Note: All end points that need data passed through the call URL require that said data to be URLEncoded.
Verb | Entry Point | Description |
---|---|---|
GET | /{shortUrl} |
Redirects to the long URL. |
POST | /Add |
Adds a long URL to the repository Parameters: url : string : Any Uniform Resource Locator Returns: ShortUrlDTO : JSON : A Data Transfer Object that represents the added URL |
PATCH | /Update |
Updates the URL and short URL of the specified ShortUrlDTO . Parameters: model : ShortUrlDTO : A Data Transfer Object containg the data to be updated. Returns: ShortUrlDTO : JSON : A Data Transfer Object that represents the updated URL. Obs: The API will first search for the short URL by the shortUrl property of the DTO, if no short URL exists then it will search for the id property of the DTO, if no short URL exists with the specified id then it will return a status code 404 . |
GET | /List |
Returns a list of ShortUrlDTO |
GET | /ById/{id} |
Returns a ShortUrlDTO with the specified id. Parameters: id : int : The id of the short URL to retrieve the information |
DELETE | /ById/{id} |
Deletes the short URL with the specified id. Parameters: id : int : The id of the short URL to be deleted. |
DELETE | /ByShortUrl/{shortUrl} |
Deletes the short URL with the specified short URL. Parameters: shortUrl : string : The short URL to be deleted. |
GET | /Validate/{shortUrl} |
Checks if the specified short URL is working. Parameters: shortUrl : string : The short URL to be verified. Returns: OK (Status Code 200) if the long URL exists and is active. NotFound (Status Code 404) if the long URL cannot be found OK if the specified short URL is not in the repository. Other (Status Code varies) whatever status code was returned by the long URL. |
This API uses the following Data Transfer Object:
{
"id" // int : The identifier of the short URL.
"url" // string : The Uniform Resource Locator that the short URL points to.
"hits" // int : The number of hits the shoert URL has.
"shortUrl" // string : The short URL that points to the long URL.
"dateCreated" // int : (Optional) The date when the short URL instance was creted.
}
The URL Shortener API uses SignalR to broadcast event messages each time a short URL is created, updated, or deleted, as shown in the table below.
Message | Payload | Sent when a short URL is... |
---|---|---|
ItemAdded |
JSON of the added item | ... added to the repository. |
ItemUpdated |
JSON of the old item and of the new item | ... updated in the repository. |
ItemDeleted |
JSON of the deleted item | ... deleted from the repository. |
The ShortUrlRepository
also have plain old events, if it's the case of plugging event handlers directly to it.
Within the solution there are two other projects:
URLShortenerValidator
: uses the endpoints of the URLShortner API to list all short URL and check the status of each one.UrlShortenerEventConsumer
: uses SignalR to llisten from messages emitted by the URLShortner API and print it to the console.