API для создания и просмотра профилей в социальной сети
Приложение можно запустить локально с помощью утилиты Docker Compose.
docker compose up -d
Для полноценной работы приложения необходимо указать учетные данные для базы данных, и ключ подписи JWT токенов. Это можно сделать через переменные окружения, добавив их непосредственно перед запуском команды docker compose up
. Можно использовать следующие переменные:
POSTGRES_USER
- определяет имя пользователя, который будет автоматически создан при первом запуске базы данных.POSTGRES_PASSWORD
- определяет пароль пользователя, который будет автоматически создан при первом запуске базы данных.POSTGRES_DB
- определяет имя базы данных, которая будет автоматически создана при первом запуске.POSTGRES_REPLICATOR_PASSWORD
- определяет пароль пользователя, который будет использоватся для репликации.AUTH_SIGNING_KEY
- секретный ключ для подписи JWT токенов, выдаваемых приложением при аутентификации пользователя. Задается в формате Base64. Для подписи используется метод HMAC, поэтому в качестве ключа можно использовать случайный набор байт произвольной длины. На Linux можно использовать такую команду, чтобы получить случайный набор размером 256 байт в формате Base64:dd if=/dev/urandom bs=256 count=1 2>/dev/null | base64
Для удобства, рекомендуется добавить в корень проекта файл .env
, где и хранить все эти переменные окружения в виде списка ИМЯ=ЗНАЧЕНИЕ
. Docker Compose будет использовать этот файл при запуске команд в корне проекта.
POSTGRES_USER=connectnow
POSTGRES_PASSWORD=N6f5ygYPScYT
POSTGRES_DB=connectnow
POSTGRES_REPLICATOR_PASSWORD=replicator_password
AUTH_SIGNING_KEY=XKmXeEWjLZSKvE2MbJ2wPy4C7PVP1oFyDxHaUaaJkWE=
Note
Настройки учетной записи пользователя базы данных читаются из переменных окружения только при первом запуске. При последующих запусках базы данных эти настройки игнорируются, т.к. учетные данные хранятся в томе postgres_data
. Однако эти переменные окружения будет использоваться при перезапуске REST API сервиса, поэтому значения переменных окружения следует всегда задавать одни и те же. При необходимости изменить значения переменных POSTGRES_USER
, POSTGRES_PASSWORD
, и POSTGRES_DB
, нужно вручную синхронизировать учетную запись в базе данных либо выполнив соотвествующие SQL команды, либо через интерфейс управления adminer
(доступен через браузер по адресу http://localhost:8080).
Перед запуском отладчика, необходимо его настроить. Для этого, сойдайте в корне проекта папку .vscode, и создайте в ней файл launch.json со следующим содержимым:
{
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}",
"env": {
"CONNECT_POSTGRES_USER": "connect-now",
"CONNECT_POSTGRES_PASSWORD": "123456",
"CONNECT_POSTGRES_DB": "connect-now",
},
"cwd": "${workspaceFolder}"
}
]
}
Отредактируйте параметры в разделе env, чтобы их значения соответствовали настройкам базы данных.
Для запуска отладчика необходимо открыть файл cmd/api/main.go, и нажать клавишу F5.
{
"info": {
"_postman_id": "23546d69-8bea-416f-9f6d-f044aeccfee7",
"name": "Creating Profile",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"_exporter_id": "5350211",
"_collection_link": "https://www.postman.com/uriahas/workspace/highload-architect/collection/5350211-23546d69-8bea-416f-9f6d-f044aeccfee7?action=share&source=collection_link&creator=5350211"
},
"item": [
{
"name": "Creating a User",
"event": [
{
"listen": "prerequest",
"script": {
"exec": [
"const first_name = pm.variables.replaceIn('{{$randomFirstName}}');\r",
"pm.variables.set(\"first_name\", first_name);\r",
"\r",
"const last_name = pm.variables.replaceIn('{{$randomLastName}}');\r",
"pm.variables.set(\"last_name\", last_name);\r",
"\r",
"const datePastStr = pm.variables.replaceIn('{{$randomDatePast}}');\r",
"const birthdate = new Date(Date.parse(datePastStr)).toISOString().split('T')[0];\r",
"pm.variables.set(\"birthdate\", birthdate);\r",
"\r",
"const genders = ['male', 'female'];\r",
"const gender = genders[Math.floor(Math.random() * genders.length)];\r",
"pm.variables.set(\"gender\", gender);\r",
"\r",
"const biography = pm.variables.replaceIn('{{randomPhrase}}');\r",
"pm.variables.set(\"biography\", biography);\r",
"\r",
"const city = pm.variables.replaceIn('{{$randomCity}}');\r",
"pm.variables.set('city', city);\r",
"\r",
"const password = pm.variables.replaceIn('{{$randomPassword}}');\r",
"pm.variables.set('password', password);\r",
""
],
"type": "text/javascript",
"packages": {}
}
},
{
"listen": "test",
"script": {
"exec": [
"const schema = {\r",
" \"properties\": {\r",
" \"user_id\": {\r",
" \"type\": \"string\"\r",
" }\r",
" }\r",
"};\r",
"\r",
"pm.test(\"response is ok\", function() {\r",
" pm.response.to.have.status(200);\r",
"});\r",
"\r",
"pm.test(\"response body has valid schema\", function() {\r",
" pm.response.to.have.jsonSchema(schema);\r",
"});\r",
"\r",
"const responseJson = pm.response.json();\r",
"pm.variables.set(\"user_id\", responseJson.user_id);"
],
"type": "text/javascript",
"packages": {}
}
}
],
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"first_name\": \"{{first_name}}\",\r\n \"second_name\": \"{{last_name}}\",\r\n \"birthdate\": \"{{birthdate}}\",\r\n \"gender\": \"{{gender}}\",\r\n \"biography\": \"{{biography}}\",\r\n \"city\": \"{{city}}\",\r\n \"password\": \"{{password}}\"\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost/user/register",
"protocol": "http",
"host": [
"localhost"
],
"path": [
"user",
"register"
]
}
},
"response": []
},
{
"name": "Authenticating a User",
"event": [
{
"listen": "test",
"script": {
"exec": [
"const schema = {\r",
" \"properties\": {\r",
" \"token\": {\r",
" \"type\": \"string\"\r",
" }\r",
" }\r",
"};\r",
"\r",
"pm.test(\"response is ok\", function() {\r",
" pm.response.to.have.status(200);\r",
"});\r",
"\r",
"pm.test(\"response body has valid schema\", function() {\r",
" pm.response.to.have.jsonSchema(schema);\r",
"});\r",
"\r",
"const responseJson = pm.response.json();\r",
"pm.variables.set(\"token\", responseJson.token);\r",
""
],
"type": "text/javascript",
"packages": {}
}
}
],
"request": {
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"id\": \"{{user_id}}\",\r\n \"password\": \"{{password}}\"\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost/login",
"protocol": "http",
"host": [
"localhost"
],
"path": [
"login"
]
}
},
"response": []
},
{
"name": "Getting a User Profile",
"event": [
{
"listen": "test",
"script": {
"exec": [
"const schema = {\r",
" \"properties\": {\r",
" \"first_name\": {\r",
" \"type\": \"string\"\r",
" },\r",
" \"second_name\": {\r",
" \"type\": \"string\"\r",
" },\r",
" \"birthdate\": {\r",
" \"type\": \"string\",\r",
" \"format\": \"date\"\r",
" },\r",
" \"gender\": {\r",
" \"type\": \"string\",\r",
" \"enum\": [\"male\", \"female\"]\r",
" },\r",
" \"biography\": {\r",
" \"type\": \"string\"\r",
" },\r",
" \"city\": {\r",
" \"type\": \"string\"\r",
" }\r",
" }\r",
"};\r",
"\r",
"pm.test(\"response is ok\", function() {\r",
" pm.response.to.have.status(200);\r",
"});\r",
"\r",
"pm.test(\"response body has valid schema\", function() {\r",
" pm.response.to.have.jsonSchema(schema);\r",
"});\r",
"\r",
"pm.test(\"response body has expected values\", function() {\r",
" pm.response.to.have.jsonBody('first_name', pm.variables.get('first_name'))\r",
" .and.have.jsonBody('second_name', pm.variables.get('last_name'))\r",
" .and.have.jsonBody('birthdate', pm.variables.get('birthdate'))\r",
" .and.have.jsonBody('gender', pm.variables.get('gender'))\r",
" .and.have.jsonBody('biography', pm.variables.get('biography'))\r",
" .and.have.jsonBody('city', pm.variables.get('city'));\r",
"});\r",
"\r",
""
],
"type": "text/javascript",
"packages": {}
}
}
],
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost/user/get/{{user_id}}",
"protocol": "http",
"host": [
"localhost"
],
"path": [
"user",
"get",
"{{user_id}}"
]
}
},
"response": []
}
],
"event": [
{
"listen": "prerequest",
"script": {
"type": "text/javascript",
"exec": [
"pm.request.headers.add({ \r",
" key: \"Accept-Language\",\r",
" value: \"ru\" \r",
"});\r",
""
]
}
},
{
"listen": "test",
"script": {
"type": "text/javascript",
"exec": [
""
]
}
}
],
"variable": [
{
"key": "user_id",
"value": "1",
"type": "string"
},
{
"key": "base_url",
"value": "arch.homework",
"type": "string"
}
]
}