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" }
}