{"id":19816983,"url":"https://github.com/geoadmin/service-shortlink","last_synced_at":"2026-01-20T16:01:05.292Z","repository":{"id":37984361,"uuid":"279530561","full_name":"geoadmin/service-shortlink","owner":"geoadmin","description":"Microservice to shorten URL","archived":false,"fork":false,"pushed_at":"2025-11-19T14:24:31.000Z","size":417,"stargazers_count":1,"open_issues_count":4,"forks_count":0,"subscribers_count":14,"default_branch":"develop","last_synced_at":"2025-11-19T16:16:34.907Z","etag":null,"topics":["managed-by-tf"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/geoadmin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-07-14T08:42:15.000Z","updated_at":"2025-10-13T12:36:09.000Z","dependencies_parsed_at":"2022-08-17T06:25:25.214Z","dependency_job_id":"7301c657-439b-48e6-906b-0cc114fb0c52","html_url":"https://github.com/geoadmin/service-shortlink","commit_stats":null,"previous_names":[],"tags_count":67,"template":false,"template_full_name":"geoadmin/template-service-flask","purl":"pkg:github/geoadmin/service-shortlink","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geoadmin%2Fservice-shortlink","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geoadmin%2Fservice-shortlink/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geoadmin%2Fservice-shortlink/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geoadmin%2Fservice-shortlink/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/geoadmin","download_url":"https://codeload.github.com/geoadmin/service-shortlink/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/geoadmin%2Fservice-shortlink/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28606287,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-20T14:45:23.139Z","status":"ssl_error","status_checked_at":"2026-01-20T14:44:16.929Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["managed-by-tf"],"created_at":"2024-11-12T10:11:20.324Z","updated_at":"2026-01-20T16:01:05.283Z","avatar_url":"https://github.com/geoadmin.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# service-shortlink\n\n| Branch  | Status                                                                                                                                                                                                                                                                                                                      |\n| ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| develop | ![Build Status](https://codebuild.eu-central-1.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiOGt4dm5YZ05Yd2NGQmlIS01qSEZHQkFGV1UxblFyc0owY1dvek9MdUM4dWE1LzIwdTJ1WTB3WExHQ0RrK1d3WmY1M0FPUlJueUxyN3FTRlNpdjBaY2Y0PSIsIml2UGFyYW1ldGVyU3BlYyI6Ild2aS9qL0k3U1VzSVRsdGkiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D\u0026branch=develop) |\n| master  | ![Build Status](https://codebuild.eu-central-1.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiOGt4dm5YZ05Yd2NGQmlIS01qSEZHQkFGV1UxblFyc0owY1dvek9MdUM4dWE1LzIwdTJ1WTB3WExHQ0RrK1d3WmY1M0FPUlJueUxyN3FTRlNpdjBaY2Y0PSIsIml2UGFyYW1ldGVyU3BlYyI6Ild2aS9qL0k3U1VzSVRsdGkiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D\u0026branch=master)  |\n\n## Table of content\n\n- [Description](#description)\n- [Dependencies](#dependencies)\n- [Service API](#service-api)\n- [Local Development](#local-development)\n- [Docker helpers](#docker-helpers)\n- [Versioning](#versioning)\n- [Deployment](#deployment)\n\n## Description\n\nA REST Microservice which creates and returns short urls, using Flask and Gunicorn, with docker containers as a mean of deployment.\n\n## dependencies\n\nThis service needs an external dynamodb database.\n\n## Service API\n\nThis service has three endpoints :\n\n- [Checker GET](#checker-get)\n- [Shortlink Creation POST](#shortlinks-creation)\n- [URL recuperation GET](#url-get)\n\nYou can find a more detailed description of the endpoints in the [OpenAPI Spec](openapi.yaml)\n\n### Staging Environments\n\n| Environment | URL                                                      |\n| ----------- | -------------------------------------------------------- |\n| DEV         | [https://sys-s.dev.bgdi.ch/](https://sys-s.dev.bgdi.ch/) |\n| INT         | [https://sys-s.int.bgdi.ch/](https://sys-s.int.bgdi.ch/) |\n| PROD        | [https://s.geo.admin.ch/](https://s.geo.admin.ch/)       |\n\n### Checker GET\n\nThis is a simple route meant to test if the server is up.\n\n| Path     | Method | Argument | Response Type    |\n| -------- | ------ | -------- | ---------------- |\n| /checker | GET    | None     | application/json |\n\n\n### Shortlink Creation POST\n\nThis route takes a json containing an url as a payload. It checks if the hostname and domain are part of the allowed names and domains,\nthen create a shortened url that is stored in a dynamodb database. If the given url already exists, within dynamodb, it returns\nthe already existing shortened url instead.\n\n\n| Path | Method | Argument | Content Type     | Content                              | Response Type    |\n| ---- | ------ | -------- | ---------------- | ------------------------------------ | ---------------- |\n| /    | POST   | None     | application/json | `{\"url\": \"https://map.geo.admin.ch}` | application/json |\n\n### URL recuperation GET\n\nThis routes search the database for the given ID and returns a json containing the corresponding url if found.\nThe redirect parameter redirect the user to the corresponding url instead if set to true.\n\n| Path             | Method | Argument                              | Response Type                   |\n| ---------------- | ------ | ------------------------------------- | ------------------------------- |\n| /\u003cshortlinks_id\u003e | GET    | optional : redirect ('true', 'false') | application/json or redirection |\n\n## Local Development\n\n### Dependencies\n\nThe **Make** targets assume you have **bash**, **curl**, **tar**, **docker** and **docker-compose-plugin** installed.\n\n### Setting up to work\n\nFirst, you'll need to clone the repo\n\n    git clone git@github.com:geoadmin/service-name\n\nThen, you can run the setup target to ensure you have everything needed to develop, test and serve locally\n\n    make setup\n\nThe other service that is used (DynamoDB local) is wrapped in a docker compose. Starting DynamoDB local is done with a simple\n\n    docker compose up\n\nThat's it, you're ready to work.\n\n### Linting and formatting your work\n\nIn order to have a consistent code style the code should be formatted using `yapf`. Also to avoid syntax errors and non\npythonic idioms code, the project uses the `pylint` linter. Both formatting and linter can be manually run using the\nfollowing command:\n\n    make lint\n\n**Formatting and linting should be at best integrated inside the IDE, for this look at\n[Integrate yapf and pylint into IDE](https://github.com/geoadmin/doc-guidelines/blob/master/PYTHON.md#yapf-and-pylint-ide-integration)**\n\n### Test your work\n\nTesting if what you developed work is made simple. You have four targets at your disposal. **test, serve, gunicornserve, dockerrun**\n\n    make test\n\nThis command run the integration and unit tests.\n\nFor testing the locally served application with the commands below, be sure to set\nENV_FILE to .env.default and start a local DynamoDB image beforehand with:\n\n    docker compose up \u0026\n    export ENV_FILE=.env.default\n\nThe following three make targets will serve the application locally:\n\n    make serve\n\nThis will serve the application through Flask without any wsgi in front.\n\n    make gunicornserve\n\nThis will serve the application with the Gunicorn layer in front of the application\n\n    make dockerrun\n\nThis will serve the application with the wsgi server, inside a container.\n\nTo stop serving through containers,\n\n    make shutdown\n\nIs the command you're looking for.\n\nA curl example for testing the generation of shortlinks on the local db is:\n\n    curl -X POST -H \"Content-Type: application/json\" -H \"Origin: https://map.geo.admin.ch\" -d '{\"url\":\"https://map.geo.admin.ch\"}' http://localhost:5000\n\n### Docker helpers\n\nFrom each github PR that is merged into `master` or into `develop`, one Docker image is built and pushed on AWS ECR with the following tag:\n\n- `vX.X.X` for tags on master\n- `vX.X.X-beta.X` for tags on develop\n\nEach image contains the following metadata:\n\n- author\n- git.branch\n- git.hash\n- git.dirty\n- version\n\nThese metadata can be read with the following command\n\n```bash\nmake dockerlogin\ndocker pull 974517877189.dkr.ecr.eu-central-1.amazonaws.com/service-shortcut:develop.latest\n\n# NOTE: jq is only used for pretty printing the json output,\n# you can install it with `apt install jq` or simply enter the command without it\ndocker image inspect --format='{{json .Config.Labels}}' 974517877189.dkr.ecr.eu-central-1.amazonaws.com/service-shortcut:develop.latest | jq\n```\n\nYou can also check these metadata on a running container as follows\n\n```bash\ndocker ps --format=\"table {{.ID}}\\t{{.Image}}\\t{{.Labels}}\"\n```\n\nTo build a local docker image tagged as `service-shortcut:local-${USER}-${GIT_HASH_SHORT}` you can\nuse\n\n```bash\nmake dockerbuild\n```\n\nTo push the image on the ECR repository use the following two commands\n\n```bash\nmake dockerlogin\nmake dockerpush\n```\n\n\n## Deployment\n\nWhen creating a PR, terraform should run a codebuild job to test, build and push automatically your PR as a tagged container.\n\nThis service is to be delployed to the Kubernetes cluster once it is merged.\n\n### Deployment Configuration\n\nThe service is configured by Environment Variable:\n\n| Env Variable                  | Default                                   | Description                                                                                                                                                                      |\n| ----------------------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| LOGGING_CFG                   | `logging-cfg-local.yml`                   | Logging configuration file to use.                                                                                                                                               |\n| AWS_ACCESS_KEY_ID             |                                           | Necessary credential to access dynamodb                                                                                                                                          |\n| AWS_SECRET_ACCESS_KEY         |                                           | AWS_SECRET_ACCESS_KEY                                                                                                                                                            |\n| AWS_DYNAMODB_TABLE_NAME       |                                           | The dynamodb table name                                                                                                                                                          |\n| AWS_DEFAULT_REGION            | eu-central-1                              | The AWS region in which the table is hosted.                                                                                                                                     |\n| AWS_ENDPOINT_URL              |                                           | The AWS endpoint url to use                                                                                                                                                      |\n| ALLOWED_DOMAINS               | `.*`                                      | A comma separated list of allowed domains names                                                                                                                                  |\n| FORWARED_ALLOW_IPS            | `*`                                       | Sets the gunicorn `forwarded_allow_ips` (see https://docs.gunicorn.org/en/stable/settings.html#forwarded-allow-ips). This is required in order to `secure_scheme_headers` works. |\n| FORWARDED_PROTO_HEADER_NAME   | `X-Forwarded-Proto`                       | Sets gunicorn `secure_scheme_headers` parameter to `{FORWARDED_PROTO_HEADER_NAME: 'https'}`, see https://docs.gunicorn.org/en/stable/settings.html#secure-scheme-headers.        |\n| CACHE_CONTROL                 | `public, max-age=31536000`                | Cache Control header value of the `GET /\u003cshortlink\u003e` endpoint                                                                                                                    |\n| CACHE_CONTROL_4XX             | `public, max-age=3600`                    | Cache Control header for 4XX responses                                                                                                                                           |\n| GUNICORN_KEEPALIVE            | `2`                                       | The [`keepalive`](https://docs.gunicorn.org/en/stable/settings.html#keepalive) setting passed to gunicorn.                                                                       |\n| GUNICORN_WORKER_TMP_DIR       |                                           | This should be set to an tmpfs file system for better performance. See https://docs.gunicorn.org/en/stable/settings.html#worker-tmp-dir.                                         |\n| SHORT_ID_SIZE                 | `12`                                      | The size (number of characters) of the shortloink id's                                                                                                                           |\n| SHORT_ID_ALPHABET             | `0123456789abcdefghijklmnopqrstuvwxyz`    | The alphabet (characters) used by the shortlink. Allowed chars `[0-9][A-Z][a-z]-_`                                                                                               |\n\n## OTEL\n\n[OpenTelemetry instrumentation](https://opentelemetry.io/docs/concepts/instrumentation/) can be done in many different ways, from fully automated zero-code instrumentation (otel-operator) to purely manual instrumentation.\nWe use the so called `OTEL programmatical instrumentation` approach where we import the specific instrumentation libraries and initialize them with the instrument() method of each library.\n\n### Environment variables\n\nThe following env variables can be used to configure OTEL\n\n| Env Variable                                              | Default                    | Description                                                                                                                                          |\n| --------------------------------------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |\n| OTEL_SDK_DISABLED                                         | false                      | If set to \"true\", OTEL is disabled. See: https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#general-sdk-configuration |\n| OTEL_ENABLE_BOTO                                          | false                      | If opentelemetry-instrumentation-botocore should be enabled or not.                                                                                  |\n| OTEL_ENABLE_FLASK                                         | false                      | If opentelemetry-instrumentation-django should be enabled or not.                                                                                    |\n| OTEL_ENABLE_LOGGING                                       | false                      | If opentelemetry-instrumentation-logging should be enabled or not.                                                                                   |\n| OTEL_EXPERIMENTAL_RESOURCE_DETECTORS                      |                            | OTEL resource detectors, adding resource attributes to the OTEL output. e.g. `os,process`                                                            |\n| OTEL_EXPORTER_OTLP_ENDPOINT                               | http://localhost:4317      | The OTEL Exporter endpoint, e.g. `opentelemetry-kube-stack-gateway-collector.opentelemetry-operator-system:4317`                                     |\n| OTEL_EXPORTER_OTLP_HEADERS                                |                            | A list of key=value headers added in outgoing data. https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/#header-configuration    |\n| OTEL_EXPORTER_OTLP_INSECURE                               | false                      | If exporter ssl certificates should be checked or not.                                                                                               |\n| OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST  |                            | A comma separated list of request headers added in outgoing data. Regex supported. Use '.*' for all headers                                          |\n| OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE |                            | A comma separated list of request headers added in outgoing data. Regex supported. Use '.*' for all headers                                          |\n| OTEL_PYTHON_FLASK_EXCLUDED_URLS                           |                            | A comma separated list of url's to exclude, e.g. `checker`                                                                                           |\n| OTEL_RESOURCE_ATTRIBUTES                                  |                            | A comma separated list of custom OTEL resource attributes, Must contain at least the service-name `service.name=service-shortlink`                   |\n| OTEL_TRACES_SAMPLER                                       | parentbased_always_on      | Sampler to be used, see https://opentelemetry-python.readthedocs.io/en/latest/sdk/trace.sampling.html#module-opentelemetry.sdk.trace.sampling.       |\n| OTEL_TRACES_SAMPLER_ARG                                   |                            | Optional additional arguments for sampler.                                                                                                           |\n| OTEL_TRACES_SAMPLER                                       | parentbased_always_on      | Sampler to be used, see https://opentelemetry-python.readthedocs.io/en/latest/sdk/trace.sampling.html#module-opentelemetry.sdk.trace.sampling.       |\n| OTEL_TRACES_SAMPLER_ARG                                   |                            | Optional additional arguments for sampler.                                                                                                           |\n\n### Adding a New Instrumentation\n\n1. Use `edot-bootstrap --action=requirements` to get a list of possible instrumentation libraries\n2. Add all or the desired ones to the Pipfile.\n3. Add the initalization to [otel.py](app/helpers/otel.py) together with a feature flag\n\nNote: `edot-bootstrap` should be already installed via `infra-ansible-bgdi-dev`. If not, install it with `pipx install elastic-opentelemetry`.\n\n### Log Correlation\n\nThe OpenTelemetry logging integration automatically injects tracing context into log statements. The following keys are injected into log record objects:\n\n- otelSpanID\n- otelTraceID\n- otelTraceSampled\n\nNote that although otelServiceName is injected, it will be empty. This is because the logging integration tries to read the service name from the trace provider, but our trace provider instance does not contain this resource attribute.\n\n### Sampling\n\nThe python SDK supports ratio based [head sampling](https://opentelemetry.io/docs/concepts/sampling/#head-sampling). To enable, set\n\n- OTEL_TRACES_SAMPLER=parentbased_traceidratio|traceidratio\n- and OTEL_TRACES_SAMPLER_ARG=[0.0,1.0]\n\n### Local Telemetry\n\nLocal telemetry can be tested by using one of the serve commands that use gunicorn, either `make gunicornserve`  or `make dockerrun`, and visiting the Zipkin dashboard at [http://localhost:9411](http://localhost:9411).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeoadmin%2Fservice-shortlink","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgeoadmin%2Fservice-shortlink","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgeoadmin%2Fservice-shortlink/lists"}