Go API for tracking time.
Manage time entries for tasks that are associated with projects. Built with Golang and PostgreSQL.
See the React Time Tracking App for a reference UI.
You can use Docker to get started quickly or run the Go server locally using a PostgreSQL instance.
To run the Go server and PostgreSQL database in a container you can start both using Docker Compose:
docker-compose up
The API will be available at http://localhost:8000 and the PostgreSQL instance will be exposed on port 5432.
Ensure you have PostgreSQL 12 or higher installed and running.
Create a timetracker database and role using the bootstrap SQL in:
./database/bootstrap.sql
Then create the schema in the timetracker database using:
./database/schema-1.sql
To run the Go API server use the run Makefile target:
make run
which will start the server on the port configured in config/dev.yml. By default the API will be available at http://localhost:8000
Unit tests can be run using:
make unit_test
Integration tests are managed under the integration_test root folder and can be run using:
make int_test
Additional functional tests are available using the Postman tool. These tests require the newman Postman command-line runner. Install using:
npm install -g newman
The test rely on the database/bootstrap.sql data to be present. To run the Postman tests locally, first start the web server:
make run
then run the Postman tests:
make postman
Most of the REST endpoints require an authentication token which can be supplied as a custom header:
Authorization: Bearer {token}
Endpoints that do not require a token are noted below.
To validate the local or Docker instance is working you can hit the /_ping endpoint and you should get back an OK response:
curl http://localhost:8000/_ping
| Method | Path | Request | Response | Notes |
|---|---|---|---|---|
| POST | /api/auth/login | LoginRequest | AuthResponse | Does not require an authentication token |
| POST | /api/auth/token | AuthResponse | ||
| POST | /api/auth/logout | {} |
||
| POST | /api/auth/forgot | EmailRequest | {} |
Does not require an authentication token. Sends a forgot password validation email. |
| Method | Path | Request | Response | Notes |
|---|---|---|---|---|
| GET | /api/profile/ | ProfileResponse | ||
| PUT | /api/profile/ | ProfileRequest | ProfileResponse | |
| PUT | /api/profile/password | PasswordChangeRequest | {} |
| Method | Path | Request | Response | Notes |
|---|---|---|---|---|
| POST | /api/account | AccountRequest | ProfileResponse | Does not require an authentication token |
| PUT | /api/account | AccountUpdateRequest | ProfileResponse | |
| GET | /api/account | AccountResponse | ||
| GET | /api/account/users | []ProfileResponse | ||
| POST | /api/account/user | AddUserRequest | ProfileResponse |
| Method | Path | Request | Response | Notes |
|---|---|---|---|---|
| GET | /api/client/{client_id} | string | ClientResponse | |
| GET | /api/client/all | []ClientResponse | ||
| GET | /api/client/archived | []ClientResponse | ||
| POST | /api/client/ | ClientRequest | ClientResponse | |
| PUT | /api/client/ | ClientRequest | {} |
|
| DELETE | /api/client/ | ClientRequest | {} |
|
| PUT | /api/client/archive | ClientRequest | {} |
|
| PUT | /api/client/restore | ClientRequest | {} |
| Method | Path | Request | Response | Notes |
|---|---|---|---|---|
| GET | /api/project/{project_id} | string | ProjectResponse | |
| GET | /api/project/all | []ProjectResponse | ||
| GET | /api/project/archived | []ProjectResponse | ||
| POST | /api/project/ | ProjectContainerRequest | ProjectResponse | |
| PUT | /api/project/ | ProjectContainerRequest | {} |
|
| DELETE | /api/project/ | ProjectIdRequest | {} |
|
| PUT | /api/project/archive | ProjectIdRequest | {} |
|
| PUT | /api/project/restore | ProjectIdRequest | {} |
|
| POST | /api/project/copy/last/week | StartAndEndDateRequest | {} or TimeRangeResponse |
Empty if no records the prior week |
| Method | Path | Request | Response | Notes |
|---|---|---|---|---|
| GET | /api/time/week | TimeRangeResponse | ||
| GET | /api/time/week/{startDate} | string | TimeRangeResponse | Date must be in the ISOShortDateFormat (e.g. "2006-01-02") |
| PUT | /api/time/ | TimeEntryRangeRequest | {} |
|
| POST | /api/time/project/week | ProjectWeekRequest | {} |
|
| DELETE | /api/time/project/week | ProjectDeleteRequest | {} |
| Method | Path | Request | Response | Notes |
|---|---|---|---|---|
| GET | /api/task/{taskId} | string | TaskResponse | |
| GET | /api/task/all | []TaskResponse | ||
| GET | /api/task/archived | []TaskResponse | ||
| POST | /api/task/ | TaskRequest | TaskResponse | |
| PUT | /api/task/ | TaskRequest | {} |
|
| PUT | /api/task/archive | TaskRequest | {} |
|
| PUT | /api/task/restore | TaskRequest | {} |
|
| DELETE | /api/task/ | TaskRequest | {} |
| Method | Path | Request | Response | Notes |
|---|---|---|---|---|
| GET | /api/report/time/client | query parameters: from, to, page |
[]ClientReportResponse | from and to are date strings in the ISOShortDateFormat format |
| GET | /api/report/time/project | query parameters: from, to, page |
[]ProjectReportResponse | from and to are date strings in the ISOShortDateFormat format |
| GET | /api/report/time/task | query parameters: from, to, page |
[]TaskReportResponse | from and to are date strings in the ISOShortDateFormat format |
| GET | /api/report/time/person | query parameters: from, to, page |
[]PersonReportResponse | from and to are date strings in the ISOShortDateFormat format |
| GET | /api/report/time/export/client | query parameters: from, to |
CSV file with content type text/csv |
from and to are date strings in the ISOShortDateFormat format |
| GET | /api/report/time/export/project | query parameters: from, to |
CSV file with content type text/csv |
from and to are date strings in the ISOShortDateFormat format |
| GET | /api/report/time/export/task | query parameters: from, to |
CSV file with content type text/csv |
from and to are date strings in the ISOShortDateFormat format |
| GET | /api/report/time/export/person | query parameters: from, to |
CSV file with content type text/csv |
from and to are date strings in the ISOShortDateFormat format |
| Method | Path | Request | Response | Notes |
|---|---|---|---|---|
| GET | /_ping | Text ok with content type text/plain |
If an API fails, the HTTP status code will reflect the type of error response. Common error status codes are:
| Status Code | Error |
|---|---|
| 400 | http.StatusBadRequest |
| 401 | http.StatusUnauthorized |
| 404 | http.StatusNotFound |
| 405 | http.StatusMethodNotAllowed |
| 500 | http.StatusInternalServerError |
Errors will produce a JSON response that contains a status field set to error.
Error details will be included as part of the serialized Error struct.
For example the error response below shows the JSON response for a 400 error when an email value is invalid:
{
"status": "error",
"error": "invalid input",
"message": "Invalid email",
"code": "InvalidEmail",
"detail": {"field": "email" }
}