{"id":23062253,"url":"https://github.com/taluu/media-api","last_synced_at":"2025-04-03T07:26:35.912Z","repository":{"id":267211508,"uuid":"886323797","full_name":"Taluu/media-api","owner":"Taluu","description":"media api exercice","archived":false,"fork":false,"pushed_at":"2024-11-16T18:56:57.000Z","size":35,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-02T23:09:33.898Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Taluu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-11-10T18:21:38.000Z","updated_at":"2024-12-03T11:12:50.000Z","dependencies_parsed_at":"2024-12-09T04:04:33.789Z","dependency_job_id":"9ca4dc89-c611-4537-a74e-4e9e2663e9c1","html_url":"https://github.com/Taluu/media-api","commit_stats":null,"previous_names":["taluu/media-api"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Taluu%2Fmedia-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Taluu%2Fmedia-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Taluu%2Fmedia-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Taluu%2Fmedia-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Taluu","download_url":"https://codeload.github.com/Taluu/media-api/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246955045,"owners_count":20860203,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-16T03:25:17.427Z","updated_at":"2025-04-03T07:26:35.897Z","avatar_url":"https://github.com/Taluu.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Media Uploader\n\nThis is a simple media uploader, allowing you to upload a media on a server\n(file, memory, s3, ...) with a few metadata such as a name or some tags.\n\n## How to build\n\n### From Source\n\n```bash\ngit clone https://github.com/Taluu/media-api.git\ncd media-api\ngo build -o build/media-api ./app\nbuild/media-api\n```\n\nAvailable flags are `-domain` and `-port`, with respective default values\nbeing `localhost` and `8080`.\n\n### From binary release\n\nBinaries should be released on the Releases page on the github repo.\n\n## Running tests\n\nTests are packaged in the repository, to run them, just run the following\ncommand :\n\n```bash\ngo test ./pkg/...\n```\n\nYou can also check the code coverage with usual flags from the `go test` and\n`go tool` commands :\n\n```bash\n go test ./pkg/... -coverprofile /tmp/coverage.out -covermode count\n go tool cover -html=/tmp/coverage.out -o /tmp/cover.html\n```\n\n## Endpoints\n\nNote : all example are hitting as if the domain is `localhost` and the port\n`8080`, and the key `test`.\n\n### Creating a media\n\nThis is one special endpoint, as instead of sending a plain json body as the\nother endpoints, you have to send a multipart/form-data with a `POST /medias`\nrequest as follows :\n\n```bash\ncurl -X POST -H \"Content-Type: multipart/form-data\" -F \"media=@/path/to/file.ext\" -F \"data={\\\"tags\\\": [\\\"foo\\\", \\\"bar\\\"]};type=application/json\" http://localhost:8080/medias\n```\n\nYou should then get a 201 json response like the following :\n\n```json\n{\n  \"id\": \"121a7a2c-5777-40e8-8c27-425c3777f378\",\n  \"name\": \"file.ext\",\n  \"file\": \"http://localhost:8080/viewer/121a7a2c-5777-40e8-8c27-425c3777f378\",\n  \"tags\": [\"foo\", \"bar\"]\n}\n```\n\nThe `data` field is optionnal, while being a json object (encoded as a string) ;\nit can contain a `name` string field, and a `tags` string array. In the example\nabove, only the `tags` property was provided, resulting in the filename used as\na name. Another example with only a `name` :\n\n```bash\ncurl -X POST -H \"Content-Type: multipart/form-data\" -F \"media=@/path/to/file.ext\" -F \"data={\\\"name\\\": \\\"my media\\\"};type=application/json\" http://localhost:8080/medias\n```\n\nYou should then get a 201 json response like the following :\n\n```json\n{\n  \"id\": \"1986600e-d65c-4c04-b2df-2cca4299ff62\",\n  \"name\": \"my media\",\n  \"file\": \"http://localhost:8080/viewer/1986600e-d65c-4c04-b2df-2cca4299ff62\",\n  \"tags\": []\n}\n```\n\nNote that provided tags in the request will be created if they do not already\nexist.\n\n### Creating a tag\n\nTo create a new tag, just send the following json to the `POST /tags` endpoint :\n\n```json\n{\n  \"name\": \"foo\"\n}\n```\n\nSo with a curl command :\n\n```bash\ncurl -X POST http://localhost:8080/tags -H \"Content-type: application/json\" -d \"{\\\"name\\\": \\\"foo\\\"}\"\n```\n\nYou will then have a 201, with the following response :\n\n```json\n{\n  \"name\": \"foo\"\n}\n```\n\nYou will have a 400 if the json body is malformed or no name are provided.\n\n### Listing available tags\n\nYou can get all the tags currently available and registered with a call to the\n`GET /tags` endpoint :\n\n```bash\ncurl http://localhost:8080/tags -H \"Content-type: application/json\"\n```\n\nYou will then receive a 200 response with the following content :\n\n```json\n{\n  \"tags\": [{ \"name\": \"foo\" }, { \"name\": \"bar\" }]\n}\n```\n\n### Searching a media by a tag\n\nYou can search all medias that are tagged with a specific tag by sending a\nrequest to the `GET /medias/{tagName}` endpoint. For example, with a `foo` tag :\n\n```bash\ncurl http://localhost:8080/medias/foo -H \"Content-type: application/json\"\n```\n\nYou will then get a 200 response returning the list of medias that match the\nrequest :\n\n```json\n{\n  \"medias\": [\n    {\n      \"id\": \"121a7a2c-5777-40e8-8c27-425c3777f378\",\n      \"name\": \"file.ext\",\n      \"file\": \"http://localhost:8080/viewer/121a7a2c-5777-40e8-8c27-425c3777f378\",\n      \"tags\": [\"foo\", \"bar\"]\n    }\n  ]\n}\n```\n\nIf the tag doesn't exist or no medias are associated with it, it will still\nreturn a 200 but with an empty `medias` array.\n\n### Downloading a media\n\nEven if this was not asked in the test, I added an endpoint to be able to\ndownload an uploaded media on the `GET /viewer/{mediaID}` endpoint :\n\n```bash\ncurl http://localhost:8080/viewer/121a7a2c-5777-40e8-8c27-425c3777f378 -H \"Content-type: application/json\" --output /tmp/file.ext\n```\n\nYou will then receive the file content as a 200 response. If the media is not\nfound, or for some reasons its corresponding file can't be found, you will then\nhave a 404 with the following json body :\n\n```json\n{\n  \"code\": 404,\n  \"error\": \"media not found\"\n}\n```\n\n## Feedback\n\nOverall, this exercice was particularly enjoyable, as I didn't work with file\nupload in a long time (the challenge was mostly on how to pass some metadata\nwith the file to upload, and also how to curl it the proper way).\n\nI also made the deliberate choice to go the simple option with not bothering on\na few things that should be taken care of in a prod environment, such as\n\n- Checking on media upload the media type and its size, putting some restrictions\n  to avoid pitfalls such as upload a malicious file or way too big files as\n  \"medias\".\n- Having a proper storage. Currently all this program does is storing in memory\n  the data, which means that if the program is stopped and re-executed, the data\n  that was there will be gone. This is easily fixable by implementing the proper\n  interfaces `media.MediaRepository`, `media.TagRegistry` and\n  `media.MediaUploader` ; a `file uploader` is available as an example, even if\n  it is not used in the application, and all this setup is within the\n  `app/main.go` file.\n\nFor the tests, I only made unit tests ; for integration tests, I usually like\nto work with cucumber (or equivalent), but I do not have the time to put it\nthere (and I also think it's a bit out of scope for this test).\n\nNote that I tried to approach the exercise with a DDD approach, even though I\nhad only one domain to bother with, which was the medias handling.\nBut still, I decided to add the intefaces to try to keep an open mind on how\nto extend what I did there, such as stated above with using another uploader\nrather the one that stores everything in memory (it could be writing on disk\nas provided as an example in this repository, a s3 uploader instead, ... pick\nyour poison !)\n\nYou will also notice that I split what \"launches\" the application (what is in\nthe `app` directory at the root) so that it's the control tower than knows\nhow to instanciate providers, listen to the server, ... for a future\nimprovment, something like a DIC could be added so no modifications or almost\nis needed when adding or removing things. But again, I felt this was out of\nscope for this test.\n\nI also added a middleware, purely as an example to log requests. I'm pretty\nsure there are other and more meaningful ways of achieving that, but for\nsimplicity sake, \"it works\" (tm)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaluu%2Fmedia-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftaluu%2Fmedia-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaluu%2Fmedia-api/lists"}