Skip to content

Commit 3db79be

Browse files
committed
resource/gitlab_topic: Add title argument support for GitLab 15.0
Closes: #1043
1 parent 50d9f1e commit 3db79be

File tree

4 files changed

+130
-23
lines changed

4 files changed

+130
-23
lines changed

docs/resources/topic.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ The `gitlab_topic` resource allows to manage the lifecycle of topics that are th
2323

2424
```terraform
2525
resource "gitlab_topic" "functional_programming" {
26-
name = "Functional Programming"
26+
name = "functional-programming"
27+
title = "Functional Programming"
2728
description = "In computer science, functional programming is a programming paradigm where programs are constructed by applying and composing functions."
2829
avatar = "${path.module}/avatar.png"
2930
avatar_hash = filesha256("${path.module}/avatar.png")
@@ -43,6 +44,7 @@ resource "gitlab_topic" "functional_programming" {
4344
- `avatar_hash` (String) The hash of the avatar image. Use `filesha256("path/to/avatar.png")` whenever possible. **Note**: this is used to trigger an update of the avatar. If it's not given, but an avatar is given, the avatar will be updated each time.
4445
- `description` (String) A text describing the topic.
4546
- `soft_destroy` (Boolean, Deprecated) Empty the topics fields instead of deleting it.
47+
- `title` (String) The topic's description. Requires at least GitLab 15.0 for which it's a required argument.
4648

4749
### Read-Only
4850

examples/resources/gitlab_topic/resource.tf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
resource "gitlab_topic" "functional_programming" {
2-
name = "Functional Programming"
2+
name = "functional-programming"
3+
title = "Functional Programming"
34
description = "In computer science, functional programming is a programming paradigm where programs are constructed by applying and composing functions."
45
avatar = "${path.module}/avatar.png"
56
avatar_hash = filesha256("${path.module}/avatar.png")

internal/provider/resource_gitlab_topic.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ var _ = registerResource("gitlab_topic", func() *schema.Resource {
3737
Type: schema.TypeString,
3838
Required: true,
3939
},
40+
"title": {
41+
Description: "The topic's description. Requires at least GitLab 15.0 for which it's a required argument.",
42+
Type: schema.TypeString,
43+
Optional: true,
44+
},
4045
"soft_destroy": {
4146
Description: "Empty the topics fields instead of deleting it.",
4247
Type: schema.TypeBool,
@@ -81,10 +86,18 @@ var _ = registerResource("gitlab_topic", func() *schema.Resource {
8186

8287
func resourceGitlabTopicCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
8388
client := meta.(*gitlab.Client)
89+
if err := resourceGitlabTopicEnsureTitleSupport(ctx, client, d); err != nil {
90+
return diag.FromErr(err)
91+
}
92+
8493
options := &gitlab.CreateTopicOptions{
8594
Name: gitlab.String(d.Get("name").(string)),
8695
}
8796

97+
if v, ok := d.GetOk("title"); ok {
98+
options.Title = gitlab.String(v.(string))
99+
}
100+
88101
if v, ok := d.GetOk("description"); ok {
89102
options.Description = gitlab.String(v.(string))
90103
}
@@ -129,6 +142,7 @@ func resourceGitlabTopicRead(ctx context.Context, d *schema.ResourceData, meta i
129142

130143
d.SetId(fmt.Sprintf("%d", topic.ID))
131144
d.Set("name", topic.Name)
145+
d.Set("title", topic.Title)
132146
d.Set("description", topic.Description)
133147
d.Set("avatar_url", topic.AvatarURL)
134148
return nil
@@ -137,11 +151,18 @@ func resourceGitlabTopicRead(ctx context.Context, d *schema.ResourceData, meta i
137151
func resourceGitlabTopicUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
138152
client := meta.(*gitlab.Client)
139153
options := &gitlab.UpdateTopicOptions{}
154+
if err := resourceGitlabTopicEnsureTitleSupport(ctx, client, d); err != nil {
155+
return diag.FromErr(err)
156+
}
140157

141158
if d.HasChange("name") {
142159
options.Name = gitlab.String(d.Get("name").(string))
143160
}
144161

162+
if d.HasChange("title") {
163+
options.Title = gitlab.String(d.Get("title").(string))
164+
}
165+
145166
if d.HasChange("description") {
146167
options.Description = gitlab.String(d.Get("description").(string))
147168
}
@@ -230,3 +251,18 @@ func resourceGitlabTopicGetAvatar(avatarPath string) (*gitlab.TopicAvatar, error
230251
Image: avatarFile,
231252
}, nil
232253
}
254+
255+
func resourceGitlabTopicEnsureTitleSupport(ctx context.Context, client *gitlab.Client, d *schema.ResourceData) error {
256+
isTitleSupported, err := isGitLabVersionAtLeast(ctx, client, "15.0")()
257+
if err != nil {
258+
return err
259+
}
260+
261+
if _, ok := d.GetOk("title"); isTitleSupported && !ok {
262+
return fmt.Errorf("title is a required attribute for GitLab 15.0 and newer. Please specify it in the configuration.")
263+
} else if !isTitleSupported && ok {
264+
return fmt.Errorf("title is not supported by your version of GitLab. At least GitLab 15.0 is required")
265+
}
266+
267+
return nil
268+
}

internal/provider/resource_gitlab_topic_test.go

Lines changed: 89 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
package provider
55

66
import (
7+
"context"
78
"fmt"
89
"os"
10+
"regexp"
911
"strconv"
1012
"testing"
1113

@@ -25,7 +27,7 @@ func TestAccGitlabTopic_basic(t *testing.T) {
2527
Steps: []resource.TestStep{
2628
// Create a topic with default options
2729
{
28-
Config: testAccGitlabTopicRequiredConfig(rInt),
30+
Config: testAccGitlabTopicRequiredConfig(t, rInt),
2931
Check: resource.ComposeTestCheckFunc(
3032
testAccCheckGitlabTopicExists("gitlab_topic.foo", &topic),
3133
testAccCheckGitlabTopicAttributes(&topic, &testAccGitlabTopicExpectedAttributes{
@@ -44,7 +46,7 @@ func TestAccGitlabTopic_basic(t *testing.T) {
4446
},
4547
// Update the topics values
4648
{
47-
Config: testAccGitlabTopicFullConfig(rInt),
49+
Config: testAccGitlabTopicFullConfig(t, rInt),
4850
Check: resource.ComposeTestCheckFunc(
4951
testAccCheckGitlabTopicExists("gitlab_topic.foo", &topic),
5052
testAccCheckGitlabTopicAttributes(&topic, &testAccGitlabTopicExpectedAttributes{
@@ -66,7 +68,7 @@ func TestAccGitlabTopic_basic(t *testing.T) {
6668
},
6769
// Update back to the default topics avatar
6870
{
69-
Config: testAccGitlabTopicFullConfig(rInt),
71+
Config: testAccGitlabTopicFullConfig(t, rInt),
7072
Check: resource.ComposeTestCheckFunc(
7173
testAccCheckGitlabTopicExists("gitlab_topic.foo", &topic),
7274
testAccCheckGitlabTopicAttributes(&topic, &testAccGitlabTopicExpectedAttributes{
@@ -88,7 +90,7 @@ func TestAccGitlabTopic_basic(t *testing.T) {
8890
},
8991
// Update the topics avatar
9092
{
91-
Config: testAccGitlabTopicFullUpdatedAvatarConfig(rInt),
93+
Config: testAccGitlabTopicFullUpdatedAvatarConfig(t, rInt),
9294
Check: resource.ComposeTestCheckFunc(
9395
testAccCheckGitlabTopicExists("gitlab_topic.foo", &topic),
9496
testAccCheckGitlabTopicAttributes(&topic, &testAccGitlabTopicExpectedAttributes{
@@ -110,7 +112,7 @@ func TestAccGitlabTopic_basic(t *testing.T) {
110112
},
111113
// Update back to the default topics avatar
112114
{
113-
Config: testAccGitlabTopicFullConfig(rInt),
115+
Config: testAccGitlabTopicFullConfig(t, rInt),
114116
Check: resource.ComposeTestCheckFunc(
115117
testAccCheckGitlabTopicExists("gitlab_topic.foo", &topic),
116118
testAccCheckGitlabTopicAttributes(&topic, &testAccGitlabTopicExpectedAttributes{
@@ -132,7 +134,7 @@ func TestAccGitlabTopic_basic(t *testing.T) {
132134
},
133135
// Update the avatar image, but keep the filename to test the `CustomizeDiff` function
134136
{
135-
Config: testAccGitlabTopicFullConfig(rInt),
137+
Config: testAccGitlabTopicFullConfig(t, rInt),
136138
PreConfig: func() {
137139
// overwrite the avatar image file
138140
if err := copyFile("testdata/gitlab_topic/avatar.png", "testdata/gitlab_topic/avatar.png.bak"); err != nil {
@@ -168,7 +170,7 @@ func TestAccGitlabTopic_basic(t *testing.T) {
168170
},
169171
// Update the topics values back to their initial state
170172
{
171-
Config: testAccGitlabTopicRequiredConfig(rInt),
173+
Config: testAccGitlabTopicRequiredConfig(t, rInt),
172174
Check: resource.ComposeTestCheckFunc(
173175
testAccCheckGitlabTopicExists("gitlab_topic.foo", &topic),
174176
testAccCheckGitlabTopicAttributes(&topic, &testAccGitlabTopicExpectedAttributes{
@@ -189,7 +191,7 @@ func TestAccGitlabTopic_basic(t *testing.T) {
189191
},
190192
// Updating the topic to have a description before it is deleted
191193
{
192-
Config: testAccGitlabTopicFullConfig(rInt),
194+
Config: testAccGitlabTopicFullConfig(t, rInt),
193195
Check: resource.ComposeTestCheckFunc(
194196
testAccCheckGitlabTopicExists("gitlab_topic.foo", &topic),
195197
testAccCheckGitlabTopicAttributes(&topic, &testAccGitlabTopicExpectedAttributes{
@@ -221,7 +223,7 @@ func TestAccGitlabTopic_withoutAvatarHash(t *testing.T) {
221223
Steps: []resource.TestStep{
222224
// Create a topic with avatar, but without giving a hash
223225
{
224-
Config: testAccGitlabTopicAvatarWithoutHashConfig(rInt),
226+
Config: testAccGitlabTopicAvatarWithoutHashConfig(t, rInt),
225227
Check: resource.ComposeTestCheckFunc(
226228
testAccCheckGitlabTopicExists("gitlab_topic.foo", &topic),
227229
resource.TestCheckResourceAttrSet("gitlab_topic.foo", "avatar_url"),
@@ -230,7 +232,7 @@ func TestAccGitlabTopic_withoutAvatarHash(t *testing.T) {
230232
},
231233
// Update the avatar image, but keep the filename to test the `CustomizeDiff` function
232234
{
233-
Config: testAccGitlabTopicAvatarWithoutHashConfig(rInt),
235+
Config: testAccGitlabTopicAvatarWithoutHashConfig(t, rInt),
234236
ExpectNonEmptyPlan: true,
235237
},
236238
},
@@ -247,7 +249,7 @@ func TestAccGitlabTopic_softDestroy(t *testing.T) {
247249
Steps: []resource.TestStep{
248250
// Create a topic with soft_destroy enabled
249251
{
250-
Config: testAccGitlabTopicSoftDestroyConfig(rInt),
252+
Config: testAccGitlabTopicSoftDestroyConfig(t, rInt),
251253
Check: resource.ComposeTestCheckFunc(
252254
testAccCheckGitlabTopicExists("gitlab_topic.foo", &topic),
253255
),
@@ -256,6 +258,46 @@ func TestAccGitlabTopic_softDestroy(t *testing.T) {
256258
})
257259
}
258260

261+
func TestAccGitlabTopic_titleSupport(t *testing.T) {
262+
rInt := acctest.RandInt()
263+
264+
resource.Test(t, resource.TestCase{
265+
ProviderFactories: providerFactories,
266+
CheckDestroy: testAccCheckGitlabTopicDestroy,
267+
Steps: []resource.TestStep{
268+
{
269+
SkipFunc: isGitLabVersionAtLeast(context.TODO(), testGitlabClient, "15.0"),
270+
Config: fmt.Sprintf(`
271+
resource "gitlab_topic" "this" {
272+
name = "foo-%d"
273+
title = "Foo-%d"
274+
}
275+
`, rInt, rInt),
276+
ExpectError: regexp.MustCompile(`title is not supported by your version of GitLab. At least GitLab 15.0 is required`),
277+
},
278+
{
279+
SkipFunc: isGitLabVersionLessThan(context.TODO(), testGitlabClient, "15.0"),
280+
Config: fmt.Sprintf(`
281+
resource "gitlab_topic" "this" {
282+
name = "foo-%d"
283+
}
284+
`, rInt),
285+
ExpectError: regexp.MustCompile(`title is a required attribute for GitLab 15.0 and newer. Please specify it in the configuration.`),
286+
},
287+
{
288+
SkipFunc: isGitLabVersionLessThan(context.TODO(), testGitlabClient, "15.0"),
289+
Config: fmt.Sprintf(`
290+
resource "gitlab_topic" "this" {
291+
name = "foo-%d"
292+
title = "Foo-%d"
293+
}
294+
`, rInt, rInt),
295+
Check: resource.TestCheckResourceAttr("gitlab_topic.this", "title", fmt.Sprintf("Foo-%d", rInt)),
296+
},
297+
},
298+
})
299+
}
300+
259301
func testAccCheckGitlabTopicExists(n string, assign *gitlab.Topic) resource.TestCheckFunc {
260302
return func(s *terraform.State) (err error) {
261303

@@ -369,47 +411,73 @@ func testAccCheckGitlabTopicSoftDestroy(s *terraform.State) (err error) {
369411
return nil
370412
}
371413

372-
func testAccGitlabTopicRequiredConfig(rInt int) string {
414+
func testAccGitlabTopicRequiredConfig(t *testing.T, rInt int) string {
415+
var titleConfig string
416+
if testAccIsRunningAtLeast(t, "15.0") {
417+
titleConfig = fmt.Sprintf(`title = "Foo Req %d"`, rInt)
418+
}
419+
373420
return fmt.Sprintf(`
374421
resource "gitlab_topic" "foo" {
375422
name = "foo-req-%d"
376-
}`, rInt)
423+
%s
424+
}`, rInt, titleConfig)
377425
}
378426

379-
func testAccGitlabTopicFullConfig(rInt int) string {
427+
func testAccGitlabTopicFullConfig(t *testing.T, rInt int) string {
428+
var titleConfig string
429+
if testAccIsRunningAtLeast(t, "15.0") {
430+
titleConfig = fmt.Sprintf(`title = "Foo Req %d"`, rInt)
431+
}
380432
return fmt.Sprintf(`
381433
resource "gitlab_topic" "foo" {
382434
name = "foo-full-%d"
435+
%s
383436
description = "Terraform acceptance tests"
384437
avatar = "${path.module}/testdata/gitlab_topic/avatar.png"
385438
avatar_hash = filesha256("${path.module}/testdata/gitlab_topic/avatar.png")
386-
}`, rInt)
439+
}`, rInt, titleConfig)
387440
}
388441

389-
func testAccGitlabTopicFullUpdatedAvatarConfig(rInt int) string {
442+
func testAccGitlabTopicFullUpdatedAvatarConfig(t *testing.T, rInt int) string {
443+
var titleConfig string
444+
if testAccIsRunningAtLeast(t, "15.0") {
445+
titleConfig = fmt.Sprintf(`title = "Foo Req %d"`, rInt)
446+
}
390447
return fmt.Sprintf(`
391448
resource "gitlab_topic" "foo" {
392449
name = "foo-full-%d"
450+
%s
393451
description = "Terraform acceptance tests"
394452
avatar = "${path.module}/testdata/gitlab_topic/avatar-update.png"
395453
avatar_hash = filesha256("${path.module}/testdata/gitlab_topic/avatar-update.png")
396-
}`, rInt)
454+
}`, rInt, titleConfig)
397455
}
398456

399-
func testAccGitlabTopicAvatarWithoutHashConfig(rInt int) string {
457+
func testAccGitlabTopicAvatarWithoutHashConfig(t *testing.T, rInt int) string {
458+
var titleConfig string
459+
if testAccIsRunningAtLeast(t, "15.0") {
460+
titleConfig = fmt.Sprintf(`title = "Foo Req %d"`, rInt)
461+
}
400462
return fmt.Sprintf(`
401463
resource "gitlab_topic" "foo" {
402464
name = "foo-%d"
465+
%s
403466
avatar = "${path.module}/testdata/gitlab_topic/avatar.png"
404-
}`, rInt)
467+
}`, rInt, titleConfig)
405468
}
406469

407-
func testAccGitlabTopicSoftDestroyConfig(rInt int) string {
470+
func testAccGitlabTopicSoftDestroyConfig(t *testing.T, rInt int) string {
471+
var titleConfig string
472+
if testAccIsRunningAtLeast(t, "15.0") {
473+
titleConfig = fmt.Sprintf(`title = "Foo Req %d"`, rInt)
474+
}
408475
return fmt.Sprintf(`
409476
resource "gitlab_topic" "foo" {
410477
name = "foo-soft-destroy-%d"
478+
%s
411479
description = "Terraform acceptance tests"
412480
413481
soft_destroy = true
414-
}`, rInt)
482+
}`, rInt, titleConfig)
415483
}

0 commit comments

Comments
 (0)