In this assignment you will be creating a small url-shortening and bookmarking service. The idea of this service is as follows:
4A23GG. Buckets can also have a description of what kinds of "links" they are meant to contain.4A23GG, then they can ask to add to this bucket the link https://en.wikipedia.org/wiki/List_of_mountains_by_elevation with "name" wikiMountains. Then someone accessing the site <ourService>/4A23GG/wikiMountains will be redirected to that wikipedia page.You can get the start files for this project by "cloning" the repository at https://github.com/skiadas/linky.git. There are start files there for you to work with. You can clone this repository by going to an appropriate location in your terminal, and run the commands:
git clone https://github.com/skiadas/linky.git
cd linkyHere are the files included in this folder, named linky:
db.py or some similar name.main.py or some similar name.tests.py or something similar.When you are ready to submit your assignment, you should create a zip file of the linky folder, and email it to me. To create a zip file you can go to the parent directory of the linky folder in the terminal, and run the command:
zip -r yourFavoriteName.zip linkyYour database file should follow the structure of the sample db.py, and use ORM to introduce needed classes, followed by a Db class that represents a connection to the database. A start on these has been done for you. Here are the classes you should have:
Shortcut is a class representing a link's shortcut. It contains the following table fields:
linkHash is of String type, and must not be null. It is a primary key, together with the bucketId.bucketId is of String type, and must also not be null. It is a primary key, together with the linkHash. It is also a foreign key pointing to the bucket id field. When a bucket is deleted, all the corresponding shortcuts that were stored in it must also be deleted, so make sure to set the ondelete setting correctly.link is of String type, and must be not null.description is of String type, it is allowed to be nullable.Shortcut class also has a relationship bucket to the Bucket class, whose opposite via back_populates is shortcuts.Bucket is a class representing a bucket of links. It contains the following table fields:
id is of String type, must not be null, and is the primary key.description is of String type, and it is allowed to be nullable.passwordHash is of String type, and must be not null.Bucket class also has a relationship shortcuts with the Shortcut class, whose opposite via back_populates is bucket.Tasks, part 1: Add the suitable lines to db.py to set up the above classes. Tasks, part 2: Your Db class should provide at least the following methods (there are tests provided for these methods in the tests.py file, make sure you can pass those tests, and you should add your own as well):
getBuckets(self) that returns a list of all buckets in the system. The result should be a (possibly empty) list of Bucket objects.addBucket(self, id, passwordHash, description=None) that can be used to create a new bucket in the database. The method should return the created bucket object. This method does NOT need to check if a bucket with that id already exists, that's the job of its caller.getBucket(self, id) that is given a bucket id and searches in the database for a bucket with that id. Returns either the Bucket object with that id, or None.deleteBucket(self, bucket) that can be used to delete an existing bucket. It expects to be given a bucket object, and deletes it from the database.addShortcut(self, linkHash, bucket, link, description=None) which creates a new shortcut entry in the database. It expects to take a shortcut string, a Bucket object, a link string and optionally a description string. It does NOT need to check if an entry with the same primary keys exists, that's the job of its caller.getShortcut(self, linkHash, bucket) that searches for a shortcut with a given hash string and belonging to a given bucket, and returns it if one exists (or returns None otherwise).deleteShortcut(self, shortcut) that is given a Shortcut object and deletes it.You will implement the following web service routes (details on them follow):
| Route Scheme | Function Name | Role | 
|---|---|---|
| GET '/' | bucket_list | List of all buckets | 
| GET '/<bucketId>' | bucket_contents | Contents of a bucket | 
| POST '/' | bucket_create | Create a new bucket | 
| PUT '/<bucketId>' | bucket_create_with_id | Create a new bucket | 
| DELETE '/<bucketId>' | bucket_delete | Delete a bucket | 
| GET '/<bucketId>/<hash>' | shortcut_get_link | Get a shortcut link | 
| POST '/<bucketId>' | shortcut_create | Create a shortcut link | 
| PUT '/<bucketId>/<hash>' | shortcut_create_with_hash | Create a shortcut link | 
| DELETE '/<bucketId>/<hash>' | shortcut_delete | Delete a shortcut link | 
Add tests for these in the tests.py file. The file has some initial entries created for you
Tasks, part 3: You must implement the following services. You may create any helper functions you like, similar to what we did in the banking-flask project.
GET '/' is meant to return a list of all the available buckets, and it is implemented in the function bucket_list. You should return a payload that looks as follows:
{
    "buckets": [
        {
            "link": "/<bucketID>",
            "description": "the bucket description"
        },
        ...
    ]
}url_for to create the links.GET '/<bucketId>' returns information about a particular bucket. It is implemented in the function bucket_contents. Its behavior should be as follows:
bucketId does not match a bucket in the database, return a 404 status code (use abort for this and other error behaviors, helper error handlers are provided at the top of the file).password field. If there isn't one, return a 403 status with a message that a password is required.getHash function from the utils library to convert this password value into a hash, and compare it to the stored passwordHash in the bucket. If they don't match, return a 403 status with a message about incorrect password.If the password does match, then you must return a normal response. Your payload should have the following structure:
{
    "id": "the bucket id",
    "link": "the link back to this bucket, use `url_for`",
    "description": "the bucket's description",
    "shortcuts": [
        {
            "linkHash": "the linkHash of the shortcut",
            "link": "link to the shortcut, use `url_for`",
            "description": "the shortcut description"
        },
        ... more shortcuts here
    ]
}PUT '/<bucketId>' is used to create a new bucket with a given id provided by the user. It is implemented in the bucket_create_with_id function. Its behavior should be as follows:
getHash function from the utils library to convert it into a linkHash value. You should also look for a "description" field in the contents of the submitted payload. Then you should use the suitable db method to create a bucket with the provided id, linkHash, and description if one was provided, and ask for a commit from the db object. You should then return a 201 status, along with a Location header whose value should be the url_for link to this newly created bucket's GET method. Your body can be an empty JSON ({}) or one that has an "ok" entry.POST '/' is used to create a new bucket with an automatically-generated id. It is implemented by the bucket_create function. It behaves similarly to the PUT version above, with a small variation:
makeId function from the utils module to generate a random bucket id.bucket_create_with_id function, passing to it the generated id. This will ensure that the rest of the method's behavior follows that of the PUT version of creating a bucket.DELETE '/<bucketId>' is used to delete a bucket. It is implemented in the bucket_delete function. Its behavior should be as follows:
getHash function from the utils module to convert the password into a passwordHash. Then check whether this passwordHash matches the one stored in the retrieved bucket. If they do not match, return a 403 error, with a message about an incorrect password.GET '/<bucketId>/<hash>' is a method used to retrieve a shortcut. It is implemented in the function shortcut_get_link. Its behavior should be as follows:
If such a shortcut is found, then return a 307 response code, indicating a redirect. Set the Location header of your response to the link value from the shortcut. You must also include a payload with your response, containing:
{
    "hash": "the linkHash",
    "link": "the shortcut target link",
    "description": "the shortcut description"
}PUT '/<bucketId>/<hash>' is used to create new shortcuts. It is implemented by the function shortcut_create_with_hash. Its behavior should be as follows:
getHash to convert it to a passwordHash and test it against the one stored with the bucket. If they don't match, return a 403 status, with a message about an incorrect password.passwordHash matches the one for the bucket, then create a new shortcut entry using the appropriate db method, issue a commit, and return a 201 status code along with a Location header pointing to the url_for entry for a GET for this shortcut.POST '/<bucketId>' can also be used to create new shortcuts, where the client expects the linkHash to be generated by the server. It is implemented by the function shortcut_create. Its behavior should be as follows:
makeId to generate a value for linkHash.bucketId and the generated linkHash, then keep generating new linkHashs until that's no longer the case.shortcut_create_with_hash function, passing to it the provided bucketId and the generated linkHash.DELETE '/<bucketId>/<linkHash>' is used to delete a shortcut. It is implemented by the shortcut_delete function. Its behavior should be as follows:
linkHash. Return a 404 if it doesn't exist.password entry. Return a 403 with a message that a password is required.makeHash to convert it to a passwordHash and compare it to the bucket's passwordHash. If they don't match, return a 403 with a message that the password is incorrect.passwordHash matches the value in the bucket, then use a suitable db method to delete the shortcut, make a commit, and issue a 204 response.