diff --git a/modules/structs/user_app.go b/modules/structs/user_app.go index 357f3aed0a..5e27e5b0f4 100644 --- a/modules/structs/user_app.go +++ b/modules/structs/user_app.go @@ -11,11 +11,12 @@ import ( // AccessToken represents an API access token. // swagger:response AccessToken type AccessToken struct { - ID int64 `json:"id"` - Name string `json:"name"` - Token string `json:"sha1"` - TokenLastEight string `json:"token_last_eight"` - Scopes []string `json:"scopes"` + ID int64 `json:"id"` + Name string `json:"name"` + Token string `json:"sha1"` + TokenLastEight string `json:"token_last_eight"` + Scopes []string `json:"scopes"` + Created time.Time `json:"created_at"` // Indicates that an access token only has access to the specified repositories. Will be null if the access token // is not limited to a set of specified repositories. Repositories []*RepositoryMeta `json:"repositories"` diff --git a/routers/api/v1/utils/access_token.go b/routers/api/v1/utils/access_token.go index a0f6b7449b..532ba1646e 100644 --- a/routers/api/v1/utils/access_token.go +++ b/routers/api/v1/utils/access_token.go @@ -117,6 +117,7 @@ func ListAccessTokens(ctx *context.APIContext) { Name: tokens[i].Name, TokenLastEight: tokens[i].TokenLastEight, Scopes: tokens[i].Scope.StringSlice(), + Created: tokens[i].CreatedUnix.AsTime(), Repositories: reposByTokenID[tokens[i].ID], } // Provide a consistent sort order on repositories, helpful for test consistency. Hard to do any earlier @@ -229,6 +230,7 @@ func CreateAccessToken(ctx *context.APIContext) { ID: t.ID, TokenLastEight: t.TokenLastEight, Scopes: t.Scope.StringSlice(), + Created: t.CreatedUnix.AsTime(), Repositories: tokenRepositories, }) } diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index e70ed513fb..be81056880 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -22792,6 +22792,11 @@ "type": "object", "title": "AccessToken represents an API access token.", "properties": { + "created_at": { + "type": "string", + "format": "date-time", + "x-go-name": "Created" + }, "id": { "type": "integer", "format": "int64", diff --git a/tests/integration/api_admin_user_token_test.go b/tests/integration/api_admin_user_token_test.go index 5f9f26208b..a12eac131d 100644 --- a/tests/integration/api_admin_user_token_test.go +++ b/tests/integration/api_admin_user_token_test.go @@ -43,6 +43,7 @@ func TestAPIAdminCreateUserAccessToken(t *testing.T) { assert.NotEmpty(t, newToken.Token) assert.NotEmpty(t, newToken.TokenLastEight) assert.Contains(t, newToken.Scopes, "all") + assert.NotZero(t, newToken.Created) // Verify the token exists in DB unittest.AssertExistsAndLoadBean(t, &auth_model.AccessToken{ @@ -143,6 +144,7 @@ func TestAPIAdminListUserAccessTokens(t *testing.T) { if tk.Name == "list-test-token" { found = true assert.NotEmpty(t, tk.TokenLastEight) + assert.NotZero(t, tk.Created) break } } diff --git a/tests/integration/api_token_test.go b/tests/integration/api_token_test.go index 2a66bc7a98..6d83d8f33a 100644 --- a/tests/integration/api_token_test.go +++ b/tests/integration/api_token_test.go @@ -57,6 +57,7 @@ func TestAPIGetTokens(t *testing.T) { assert.Equal(t, []string{""}, at.Scopes) assert.Empty(t, at.Token) assert.Equal(t, "69d28c91", at.TokenLastEight) + assert.NotZero(t, at.Created) assert.Nil(t, at.Repositories) // not repo-specific access token - nil expected, not an empty array }) @@ -753,6 +754,7 @@ func TestAPITokenCreation(t *testing.T) { resp := MakeRequest(t, req, http.StatusCreated) var token api.AccessToken DecodeJSON(t, resp, &token) + assert.NotZero(t, token.Created) }) t.Run("repo-specific", func(t *testing.T) {