Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New Resource: azurerm_bot_channel_alexa (#12682)
- Loading branch information
Neil Ye
committed
Jul 29, 2021
1 parent
8b2b4e0
commit bef2a68
Showing
5 changed files
with
436 additions
and
0 deletions.
There are no files selected for viewing
234 changes: 234 additions & 0 deletions
234
azurerm/internal/services/bot/bot_channel_alexa_resource.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
package bot | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"log" | ||
"strconv" | ||
"time" | ||
|
||
"github.com/Azure/azure-sdk-for-go/services/botservice/mgmt/2021-03-01/botservice" | ||
"github.com/hashicorp/go-azure-helpers/response" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/location" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/bot/parse" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/bot/validate" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/validation" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" | ||
) | ||
|
||
func resourceBotChannelAlexa() *pluginsdk.Resource { | ||
return &pluginsdk.Resource{ | ||
Create: resourceBotChannelAlexaCreate, | ||
Read: resourceBotChannelAlexaRead, | ||
Delete: resourceBotChannelAlexaDelete, | ||
Update: resourceBotChannelAlexaUpdate, | ||
|
||
Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { | ||
_, err := parse.BotChannelID(id) | ||
return err | ||
}), | ||
|
||
Timeouts: &pluginsdk.ResourceTimeout{ | ||
Create: pluginsdk.DefaultTimeout(30 * time.Minute), | ||
Read: pluginsdk.DefaultTimeout(5 * time.Minute), | ||
Update: pluginsdk.DefaultTimeout(30 * time.Minute), | ||
Delete: pluginsdk.DefaultTimeout(30 * time.Minute), | ||
}, | ||
|
||
Schema: map[string]*pluginsdk.Schema{ | ||
"resource_group_name": azure.SchemaResourceGroupName(), | ||
|
||
"location": azure.SchemaLocation(), | ||
|
||
"bot_name": { | ||
Type: pluginsdk.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
ValidateFunc: validate.BotName, | ||
}, | ||
|
||
"skill_id": { | ||
Type: pluginsdk.TypeString, | ||
Required: true, | ||
ValidateFunc: validation.StringIsNotEmpty, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceBotChannelAlexaCreate(d *pluginsdk.ResourceData, meta interface{}) error { | ||
client := meta.(*clients.Client).Bot.ChannelClient | ||
subscriptionId := meta.(*clients.Client).Account.SubscriptionId | ||
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) | ||
defer cancel() | ||
|
||
id := parse.NewBotChannelID(subscriptionId, d.Get("resource_group_name").(string), d.Get("bot_name").(string), string(botservice.ChannelNameAlexaChannel)) | ||
if d.IsNewResource() { | ||
existing, err := client.Get(ctx, id.ResourceGroup, id.BotServiceName, id.ChannelName) | ||
if err != nil { | ||
if !utils.ResponseWasNotFound(existing.Response) { | ||
return fmt.Errorf("checking for presence of existing %s: %+v", id, err) | ||
} | ||
} | ||
if existing.ID != nil && *existing.ID != "" { | ||
return tf.ImportAsExistsError("azurerm_bot_channel_alexa", id.ID()) | ||
} | ||
} | ||
|
||
channel := botservice.BotChannel{ | ||
Properties: botservice.AlexaChannel{ | ||
Properties: &botservice.AlexaChannelProperties{ | ||
AlexaSkillID: utils.String(d.Get("skill_id").(string)), | ||
IsEnabled: utils.Bool(true), | ||
}, | ||
ChannelName: botservice.ChannelNameBasicChannelChannelNameAlexaChannel, | ||
}, | ||
Location: utils.String(azure.NormalizeLocation(d.Get("location").(string))), | ||
Kind: botservice.KindBot, | ||
} | ||
|
||
if _, err := client.Create(ctx, id.ResourceGroup, id.BotServiceName, botservice.ChannelNameAlexaChannel, channel); err != nil { | ||
return fmt.Errorf("creating %s: %+v", id, err) | ||
} | ||
|
||
// Issue: https://github.com/Azure/azure-rest-api-specs/issues/15298 | ||
// There is a long running issue on updating the Alexa Channel. | ||
// The Bot Channel API cannot update Skill ID immediately after the Alexa Channel is created. | ||
// It has to wait a while after created. Then the skill ID can be updated successfully. | ||
stateConf := &pluginsdk.StateChangeConf{ | ||
Delay: 5 * time.Minute, | ||
Pending: []string{"204", "404"}, | ||
Target: []string{"200", "202"}, | ||
Refresh: botChannelAlexaStateRefreshFunc(ctx, client, id), | ||
MinTimeout: 15 * time.Second, | ||
Timeout: d.Timeout(pluginsdk.TimeoutCreate), | ||
} | ||
|
||
if _, err := stateConf.WaitForStateContext(ctx); err != nil { | ||
return fmt.Errorf("waiting for creation of %s: %+v", id, err) | ||
} | ||
|
||
d.SetId(id.ID()) | ||
return resourceBotChannelAlexaRead(d, meta) | ||
} | ||
|
||
func resourceBotChannelAlexaRead(d *pluginsdk.ResourceData, meta interface{}) error { | ||
client := meta.(*clients.Client).Bot.ChannelClient | ||
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) | ||
defer cancel() | ||
|
||
id, err := parse.BotChannelID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resp, err := client.Get(ctx, id.ResourceGroup, id.BotServiceName, string(botservice.ChannelNameAlexaChannel)) | ||
if err != nil { | ||
if utils.ResponseWasNotFound(resp.Response) { | ||
log.Printf("[DEBUG] %s was not found - removing from state!", id) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("retrieving %s: %+v", id, err) | ||
} | ||
|
||
d.Set("bot_name", id.BotServiceName) | ||
d.Set("resource_group_name", id.ResourceGroup) | ||
d.Set("location", location.NormalizeNilable(resp.Location)) | ||
|
||
if props := resp.Properties; props != nil { | ||
if channel, ok := props.AsAlexaChannel(); ok { | ||
if channelProps := channel.Properties; channelProps != nil { | ||
d.Set("skill_id", channelProps.AlexaSkillID) | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func resourceBotChannelAlexaUpdate(d *pluginsdk.ResourceData, meta interface{}) error { | ||
client := meta.(*clients.Client).Bot.ChannelClient | ||
ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) | ||
defer cancel() | ||
|
||
id, err := parse.BotChannelID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
channel := botservice.BotChannel{ | ||
Properties: botservice.AlexaChannel{ | ||
Properties: &botservice.AlexaChannelProperties{ | ||
AlexaSkillID: utils.String(d.Get("skill_id").(string)), | ||
IsEnabled: utils.Bool(true), | ||
}, | ||
ChannelName: botservice.ChannelNameBasicChannelChannelNameAlexaChannel, | ||
}, | ||
Location: utils.String(azure.NormalizeLocation(d.Get("location").(string))), | ||
Kind: botservice.KindBot, | ||
} | ||
|
||
_, err = client.Update(ctx, id.ResourceGroup, id.BotServiceName, botservice.ChannelNameAlexaChannel, channel) | ||
if err != nil { | ||
return fmt.Errorf("updating %s: %+v", *id, err) | ||
} | ||
|
||
// Issue: https://github.com/Azure/azure-rest-api-specs/issues/15298 | ||
// There is a long running issue on updating the Alexa Channel. | ||
// The Bot Channel API cannot update Skill ID immediately after the Alexa Channel is updated. | ||
// It has to wait a while after updated. Then the skill ID can be updated successfully. | ||
stateConf := &pluginsdk.StateChangeConf{ | ||
Delay: 5 * time.Minute, | ||
Pending: []string{"204", "404"}, | ||
Target: []string{"200", "202"}, | ||
Refresh: botChannelAlexaStateRefreshFunc(ctx, client, *id), | ||
MinTimeout: 15 * time.Second, | ||
Timeout: d.Timeout(pluginsdk.TimeoutUpdate), | ||
} | ||
|
||
if _, err := stateConf.WaitForStateContext(ctx); err != nil { | ||
return fmt.Errorf("waiting for update of %s: %+v", id, err) | ||
} | ||
|
||
return resourceBotChannelAlexaRead(d, meta) | ||
} | ||
|
||
func resourceBotChannelAlexaDelete(d *pluginsdk.ResourceData, meta interface{}) error { | ||
client := meta.(*clients.Client).Bot.ChannelClient | ||
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) | ||
defer cancel() | ||
|
||
id, err := parse.BotChannelID(d.Id()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
resp, err := client.Delete(ctx, id.ResourceGroup, id.BotServiceName, string(botservice.ChannelNameAlexaChannel)) | ||
if err != nil { | ||
if !response.WasNotFound(resp.Response) { | ||
return fmt.Errorf("deleting %s: %+v", *id, err) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func botChannelAlexaStateRefreshFunc(ctx context.Context, client *botservice.ChannelsClient, id parse.BotChannelId) pluginsdk.StateRefreshFunc { | ||
return func() (interface{}, string, error) { | ||
resp, err := client.Get(ctx, id.ResourceGroup, id.BotServiceName, id.ChannelName) | ||
if err != nil { | ||
if !utils.ResponseWasNotFound(resp.Response) { | ||
return nil, "", fmt.Errorf("retrieving %s: %+v", id, err) | ||
} | ||
} | ||
|
||
return resp, strconv.Itoa(resp.StatusCode), nil | ||
} | ||
} |
124 changes: 124 additions & 0 deletions
124
azurerm/internal/services/bot/bot_channel_alexa_resource_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package bot_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/Azure/azure-sdk-for-go/services/botservice/mgmt/2021-03-01/botservice" | ||
"github.com/google/uuid" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance/check" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/bot/parse" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/pluginsdk" | ||
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" | ||
) | ||
|
||
type BotChannelAlexaResource struct { | ||
} | ||
|
||
func testAccBotChannelAlexa_basic(t *testing.T) { | ||
data := acceptance.BuildTestData(t, "azurerm_bot_channel_alexa", "test") | ||
r := BotChannelAlexaResource{} | ||
|
||
data.ResourceSequentialTest(t, r, []acceptance.TestStep{ | ||
{ | ||
Config: r.basic(data), | ||
Check: acceptance.ComposeTestCheckFunc( | ||
check.That(data.ResourceName).ExistsInAzure(r), | ||
), | ||
}, | ||
data.ImportStep(), | ||
}) | ||
} | ||
|
||
func testAccBotChannelAlexa_requiresImport(t *testing.T) { | ||
data := acceptance.BuildTestData(t, "azurerm_bot_channel_alexa", "test") | ||
r := BotChannelAlexaResource{} | ||
|
||
data.ResourceTest(t, r, []acceptance.TestStep{ | ||
{ | ||
Config: r.basic(data), | ||
Check: acceptance.ComposeTestCheckFunc( | ||
check.That(data.ResourceName).ExistsInAzure(r), | ||
), | ||
}, | ||
data.RequiresImportErrorStep(r.requiresImport), | ||
}) | ||
} | ||
|
||
func testAccBotChannelAlexa_update(t *testing.T) { | ||
data := acceptance.BuildTestData(t, "azurerm_bot_channel_alexa", "test") | ||
r := BotChannelAlexaResource{} | ||
|
||
data.ResourceSequentialTest(t, r, []acceptance.TestStep{ | ||
{ | ||
Config: r.basic(data), | ||
Check: acceptance.ComposeTestCheckFunc( | ||
check.That(data.ResourceName).ExistsInAzure(r), | ||
), | ||
}, | ||
data.ImportStep(), | ||
{ | ||
Config: r.update(data), | ||
Check: acceptance.ComposeTestCheckFunc( | ||
check.That(data.ResourceName).ExistsInAzure(r), | ||
), | ||
}, | ||
data.ImportStep(), | ||
}) | ||
} | ||
|
||
func (t BotChannelAlexaResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { | ||
id, err := parse.BotChannelID(state.ID) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
resp, err := clients.Bot.ChannelClient.Get(ctx, id.ResourceGroup, id.BotServiceName, string(botservice.ChannelNameAlexaChannel)) | ||
if err != nil { | ||
return nil, fmt.Errorf("retrieving %s: %v", id.String(), err) | ||
} | ||
|
||
return utils.Bool(resp.Properties != nil), nil | ||
} | ||
|
||
func (BotChannelAlexaResource) basic(data acceptance.TestData) string { | ||
return fmt.Sprintf(` | ||
%s | ||
resource "azurerm_bot_channel_alexa" "test" { | ||
bot_name = azurerm_bot_channels_registration.test.name | ||
location = azurerm_bot_channels_registration.test.location | ||
resource_group_name = azurerm_resource_group.test.name | ||
skill_id = "amzn1.ask.skill.%s" | ||
} | ||
`, BotChannelsRegistrationResource{}.basicConfig(data), uuid.New().String()) | ||
} | ||
|
||
func (r BotChannelAlexaResource) requiresImport(data acceptance.TestData) string { | ||
return fmt.Sprintf(` | ||
%s | ||
resource "azurerm_bot_channel_alexa" "import" { | ||
bot_name = azurerm_bot_channel_alexa.test.bot_name | ||
location = azurerm_bot_channel_alexa.test.location | ||
resource_group_name = azurerm_bot_channel_alexa.test.resource_group_name | ||
skill_id = azurerm_bot_channel_alexa.test.skill_id | ||
} | ||
`, r.basic(data)) | ||
} | ||
|
||
func (BotChannelAlexaResource) update(data acceptance.TestData) string { | ||
return fmt.Sprintf(` | ||
%s | ||
resource "azurerm_bot_channel_alexa" "test" { | ||
bot_name = azurerm_bot_channels_registration.test.name | ||
location = azurerm_bot_channels_registration.test.location | ||
resource_group_name = azurerm_resource_group.test.name | ||
skill_id = "amzn1.ask.skill.%s" | ||
} | ||
`, BotChannelsRegistrationResource{}.basicConfig(data), uuid.New().String()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.