From b46c6522039e723def22ab232ef2788bf31a32ff Mon Sep 17 00:00:00 2001 From: "Lingling Ye (from Dev Box)" Date: Mon, 29 Sep 2025 17:02:08 +0800 Subject: [PATCH 1/5] Add go example apps --- examples/Go/ConsoleApp/Example/README.md | 60 ++++++ examples/Go/ConsoleApp/Example/main.go | 79 ++++++++ examples/Go/ConsoleApp/Refresh/README.md | 67 +++++++ examples/Go/ConsoleApp/Refresh/main.go | 137 +++++++++++++ examples/Go/WebApp/Example/README.md | 62 ++++++ examples/Go/WebApp/Example/main.go | 105 ++++++++++ .../Go/WebApp/Example/templates/about.html | 38 ++++ .../Go/WebApp/Example/templates/index.html | 38 ++++ examples/Go/WebApp/FeatureFlag/README.md | 51 +++++ examples/Go/WebApp/FeatureFlag/main.go | 162 ++++++++++++++++ .../Go/WebApp/FeatureFlag/templates/404.html | 37 ++++ .../Go/WebApp/FeatureFlag/templates/beta.html | 34 ++++ .../WebApp/FeatureFlag/templates/index.html | 39 ++++ examples/Go/WebApp/Refresh/README.md | 70 +++++++ examples/Go/WebApp/Refresh/main.go | 183 ++++++++++++++++++ .../Go/WebApp/Refresh/templates/about.html | 70 +++++++ .../Go/WebApp/Refresh/templates/index.html | 38 ++++ examples/README.md | 28 ++- 18 files changed, 1297 insertions(+), 1 deletion(-) create mode 100644 examples/Go/ConsoleApp/Example/README.md create mode 100644 examples/Go/ConsoleApp/Example/main.go create mode 100644 examples/Go/ConsoleApp/Refresh/README.md create mode 100644 examples/Go/ConsoleApp/Refresh/main.go create mode 100644 examples/Go/WebApp/Example/README.md create mode 100644 examples/Go/WebApp/Example/main.go create mode 100644 examples/Go/WebApp/Example/templates/about.html create mode 100644 examples/Go/WebApp/Example/templates/index.html create mode 100644 examples/Go/WebApp/FeatureFlag/README.md create mode 100644 examples/Go/WebApp/FeatureFlag/main.go create mode 100644 examples/Go/WebApp/FeatureFlag/templates/404.html create mode 100644 examples/Go/WebApp/FeatureFlag/templates/beta.html create mode 100644 examples/Go/WebApp/FeatureFlag/templates/index.html create mode 100644 examples/Go/WebApp/Refresh/README.md create mode 100644 examples/Go/WebApp/Refresh/main.go create mode 100644 examples/Go/WebApp/Refresh/templates/about.html create mode 100644 examples/Go/WebApp/Refresh/templates/index.html diff --git a/examples/Go/ConsoleApp/Example/README.md b/examples/Go/ConsoleApp/Example/README.md new file mode 100644 index 00000000..537836f6 --- /dev/null +++ b/examples/Go/ConsoleApp/Example/README.md @@ -0,0 +1,60 @@ +# Azure App Configuration Console Example + +This example demonstrates how to use Azure App Configuration in a console/command-line application. + +## Overview + +This simple console application: + +1. Loads configuration values from Azure App Configuration +2. Binds them to target configuration struct + +## Running the Example + +### Prerequisites + +You need [an Azure subscription](https://azure.microsoft.com/free/) and the following Azure resources to run the examples: + +- [Azure App Configuration store](https://learn.microsoft.com/en-us/azure/azure-app-configuration/quickstart-azure-app-configuration-create?tabs=azure-portal) + +The examples retrieve credentials to access your App Configuration store from environment variables. +Alternatively, edit the source code to include the appropriate credentials. + +### Add key-values + +Add the following key-values to the App Configuration store and leave **Label** and **Content Type** with their default values. For more information about how to add key-values to a store using the Azure portal or the CLI, go to [Create a key-value](./quickstart-azure-app-configuration-create.md#create-a-key-value). + +| Key | Value | +|------------------------|----------------| +| *Config.Message* | *Hello World!* | +| *Config.Font.Color* | *blue* | +| *Config.Font.Size* | *12* | + +### Setup + +1. Initialize a new Go module. + + ```bash + go mod init console-example-app + ``` +1. Add the Azure App Configuration provider as a dependency. + + ```bash + go get github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration + ``` + +1. Set the connection string as an environment variable: + + ```bash + # Windows + set AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string + + # Linux/macOS + export AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string + ``` + +### Run the Application + +```bash +go run main.go +``` \ No newline at end of file diff --git a/examples/Go/ConsoleApp/Example/main.go b/examples/Go/ConsoleApp/Example/main.go new file mode 100644 index 00000000..e639909e --- /dev/null +++ b/examples/Go/ConsoleApp/Example/main.go @@ -0,0 +1,79 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "time" + + "github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration" +) + +type Config struct { + Font Font + Message string +} + +type Font struct { + Color string + Size int +} + +// loadConfiguration handles loading the configuration from Azure App Configuration +func loadConfiguration() (Config, error) { + // Get connection string from environment variable + connectionString := os.Getenv("AZURE_APPCONFIG_CONNECTION_STRING") + + // Configuration setup + options := &azureappconfiguration.Options{ + Selectors: []azureappconfiguration.Selector{ + { + KeyFilter: "Config.*", + }, + }, + // Remove the prefix when mapping to struct fields + TrimKeyPrefixes: []string{"Config."}, + } + + authOptions := azureappconfiguration.AuthenticationOptions{ + ConnectionString: connectionString, + } + + // Create configuration provider with timeout + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + appCfgProvider, err := azureappconfiguration.Load(ctx, authOptions, options) + if err != nil { + return Config{}, err + } + + // Parse configuration into struct + var config Config + err = appCfgProvider.Unmarshal(&config, nil) + if err != nil { + return Config{}, err + } + + return config, nil +} + +func main() { + fmt.Println("Azure App Configuration - Console Example") + fmt.Println("----------------------------------------") + + // Load configuration + fmt.Println("Loading configuration from Azure App Configuration...") + config, err := loadConfiguration() + if err != nil { + log.Fatalf("Error loading configuration: %s", err) + } + + // Display the configuration values + fmt.Println("\nConfiguration Values:") + fmt.Println("--------------------") + fmt.Printf("Font Color: %s\n", config.Font.Color) + fmt.Printf("Font Size: %d\n", config.Font.Size) + fmt.Printf("Message: %s\n", config.Message) +} \ No newline at end of file diff --git a/examples/Go/ConsoleApp/Refresh/README.md b/examples/Go/ConsoleApp/Refresh/README.md new file mode 100644 index 00000000..170198e3 --- /dev/null +++ b/examples/Go/ConsoleApp/Refresh/README.md @@ -0,0 +1,67 @@ +# Azure App Configuration Console Refresh Example + +This example demonstrates how to use the refresh functionality of Azure App Configuration in a console/command-line application. + +## Overview + +This console application: + +1. Loads configuration values from Azure App Configuration +2. Binds them to target configuration struct +3. Automatically refreshes the configuration when changed in Azure App Configuration + +## Running the Example + +### Prerequisites + +You need [an Azure subscription](https://azure.microsoft.com/free/) and the following Azure resources to run the examples: + +- [Azure App Configuration store](https://learn.microsoft.com/en-us/azure/azure-app-configuration/quickstart-azure-app-configuration-create?tabs=azure-portal) + +The examples retrieve credentials to access your App Configuration store from environment variables. + +### Add key-values + +Add the following key-values to the App Configuration store and leave **Label** and **Content Type** with their default values: + +| Key | Value | +|------------------------|----------------| +| *Config.Message* | *Hello World!* | +| *Config.Font.Color* | *blue* | +| *Config.Font.Size* | *12* | + +### Setup + +1. Initialize a new Go module. + + ```bash + go mod init console-example-refresh + ``` +1. Add the Azure App Configuration provider as a dependency. + + ```bash + go get github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration + ``` + +1. Set the connection string as an environment variable: + + ```bash + # Windows + set AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string + + # Linux/macOS + export AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string + ``` + +### Run the Application + +```bash +go run main.go +``` + +### Testing the Refresh Functionality + +1. Start the application +2. While it's running, modify the values in your Azure App Configuration store +3. Within 10 seconds (the configured refresh interval), the application should detect and apply the changes +4. You don't need to restart the application to see the updated values \ No newline at end of file diff --git a/examples/Go/ConsoleApp/Refresh/main.go b/examples/Go/ConsoleApp/Refresh/main.go new file mode 100644 index 00000000..dda74681 --- /dev/null +++ b/examples/Go/ConsoleApp/Refresh/main.go @@ -0,0 +1,137 @@ +package main + +import ( + "context" + "fmt" + "log" + "os" + "os/signal" + "syscall" + "time" + + "github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration" +) + +type Config struct { + Font Font + Message string +} + +type Font struct { + Color string + Size int +} + +// initializeAppConfiguration handles loading the configuration from Azure App Configuration +func initializeAppConfiguration() (*azureappconfiguration.AzureAppConfiguration, error) { + // Get connection string from environment variable + connectionString := os.Getenv("AZURE_APPCONFIG_CONNECTION_STRING") + + // Options setup + options := &azureappconfiguration.Options{ + Selectors: []azureappconfiguration.Selector{ + { + KeyFilter: "Config.*", + }, + }, + // Remove the prefix when mapping to struct fields + TrimKeyPrefixes: []string{"Config."}, + // Enable refresh every 10 seconds + RefreshOptions: azureappconfiguration.KeyValueRefreshOptions{ + Enabled: true, + Interval: 10 * time.Second, + }, + } + + authOptions := azureappconfiguration.AuthenticationOptions{ + ConnectionString: connectionString, + } + + // Create configuration provider with timeout + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + return azureappconfiguration.Load(ctx, authOptions, options) +} + +// displayConfig prints the current configuration values +func displayConfig(config Config) { + fmt.Println("\nCurrent Configuration Values:") + fmt.Println("--------------------") + fmt.Printf("Font Color: %s\n", config.Font.Color) + fmt.Printf("Font Size: %d\n", config.Font.Size) + fmt.Printf("Message: %s\n", config.Message) + fmt.Println("--------------------") +} + +func main() { + fmt.Println("Azure App Configuration - Console Refresh Example") + fmt.Println("----------------------------------------") + + // Load configuration + fmt.Println("Loading configuration from Azure App Configuration...") + appCfgProvider, err := initializeAppConfiguration() + if err != nil { + log.Fatalf("Error loading configuration: %s", err) + } + + // Parse initial configuration into struct + var config Config + err = appCfgProvider.Unmarshal(&config, nil) + if err != nil { + log.Fatalf("Error unmarshalling configuration: %s", err) + } + + // Display the initial configuration + displayConfig(config) + + // Register refresh callback to update and display the configuration + appCfgProvider.OnRefreshSuccess(func() { + fmt.Println("\n🔄 Configuration changed! Updating values...") + + // Re-unmarshal the configuration + var updatedConfig Config + err := appCfgProvider.Unmarshal(&updatedConfig, nil) + if err != nil { + log.Printf("Error unmarshalling updated configuration: %s", err) + return + } + + // Update our working config + config = updatedConfig + + // Display the updated configuration + displayConfig(config) + }) + + // Setup a channel to listen for termination signals + done := make(chan os.Signal, 1) + signal.Notify(done, syscall.SIGINT, syscall.SIGTERM) + + fmt.Println("\nWaiting for configuration changes...") + fmt.Println("(Update values in Azure App Configuration to see refresh in action)") + fmt.Println("Press Ctrl+C to exit") + + // Start a ticker to periodically trigger refresh + ticker := time.NewTicker(5 * time.Second) + defer ticker.Stop() + + // Keep the application running until terminated + for { + select { + case <-ticker.C: + // Trigger refresh in background + go func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + if err := appCfgProvider.Refresh(ctx); err != nil { + log.Printf("Error refreshing configuration: %s", err) + } + }() + case <-done: + fmt.Println("\nExiting...") + return + } + } +} \ No newline at end of file diff --git a/examples/Go/WebApp/Example/README.md b/examples/Go/WebApp/Example/README.md new file mode 100644 index 00000000..531d6675 --- /dev/null +++ b/examples/Go/WebApp/Example/README.md @@ -0,0 +1,62 @@ +# Azure App Configuration Gin Web Example + +This example demonstrates how to use Azure App Configuration in a web application built with the Gin framework. + +## Overview + +This web application: + +1. Loads configuration values from Azure App Configuration +2. Configures the Gin web framework based on those values + +## Running the Example + +### Prerequisites + +You need [an Azure subscription](https://azure.microsoft.com/free/) and the following Azure resources to run the examples: + +- [Azure App Configuration store](https://learn.microsoft.com/en-us/azure/azure-app-configuration/quickstart-azure-app-configuration-create?tabs=azure-portal) + +The examples retrieve credentials to access your App Configuration store from environment variables. + +### Add key-values + +Add the following key-values to the App Configuration store and leave **Label** and **Content Type** with their default values: + +| Key | Value | +|------------------------|--------------------| +| *Config.Message* | *Hello World!* | +| *Config.App.Name* | *Gin Web App* | +| *Config.App.DebugMode* | *true* | + +### Setup + +1. Initialize a new Go module. + + ```bash + go mod init gin-example-app + ``` +1. Add the required dependencies. + + ```bash + go get github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration + go get github.com/gin-gonic/gin + ``` + +1. Set the connection string as an environment variable: + + ```bash + # Windows + set AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string + + # Linux/macOS + export AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string + ``` + +### Run the Application + +```bash +go run main.go +``` + +Then navigate to `http://localhost:8080` in your web browser. \ No newline at end of file diff --git a/examples/Go/WebApp/Example/main.go b/examples/Go/WebApp/Example/main.go new file mode 100644 index 00000000..a9f956d5 --- /dev/null +++ b/examples/Go/WebApp/Example/main.go @@ -0,0 +1,105 @@ +package main + +import ( + "context" + "log" + "os" + "time" + + "github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration" + "github.com/gin-gonic/gin" +) + +type Config struct { + App App + Message string +} + +type App struct { + Name string + DebugMode bool +} + +// loadConfiguration handles loading the configuration from Azure App Configuration +func loadConfiguration() (Config, error) { + // Get connection string from environment variable + connectionString := os.Getenv("AZURE_APPCONFIG_CONNECTION_STRING") + + // Configuration setup + options := &azureappconfiguration.Options{ + Selectors: []azureappconfiguration.Selector{ + { + KeyFilter: "Config.*", + }, + }, + // Remove the prefix when mapping to struct fields + TrimKeyPrefixes: []string{"Config."}, + } + + authOptions := azureappconfiguration.AuthenticationOptions{ + ConnectionString: connectionString, + } + + // Create configuration provider with timeout + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + appCfgProvider, err := azureappconfiguration.Load(ctx, authOptions, options) + if err != nil { + return Config{}, err + } + + // Parse configuration into struct + var config Config + err = appCfgProvider.Unmarshal(&config, nil) + if err != nil { + return Config{}, err + } + + return config, nil +} + +func main() { + // Load configuration + config, err := loadConfiguration() + if err != nil { + log.Fatalf("Error loading configuration: %s", err) + } + + if config.App.DebugMode { + // Set Gin to debug mode + gin.SetMode(gin.DebugMode) + log.Println("Running in DEBUG mode") + } else { + // Set Gin to release mode for production + gin.SetMode(gin.ReleaseMode) + log.Println("Running in RELEASE mode") + } + + // Initialize Gin router + r := gin.Default() + + // Load HTML templates + r.LoadHTMLGlob("templates/*") + + // Define a route for the homepage + r.GET("/", func(c *gin.Context) { + c.HTML(200, "index.html", gin.H{ + "Title": "Home", + "Message": config.Message, + "App": config.App.Name, + }) + }) + + // Define a route for the About page + r.GET("/about", func(c *gin.Context) { + c.HTML(200, "about.html", gin.H{ + "Title": "About", + }) + }) + + // Start the server + if err := r.Run(":8080"); err != nil { + log.Fatalf("Error starting server: %s", err) + } +} \ No newline at end of file diff --git a/examples/Go/WebApp/Example/templates/about.html b/examples/Go/WebApp/Example/templates/about.html new file mode 100644 index 00000000..b3eb5288 --- /dev/null +++ b/examples/Go/WebApp/Example/templates/about.html @@ -0,0 +1,38 @@ + + + + + + {{.Title}} + + + + + + +
+

About Page

+

This is the about page where you can add information about your website or app.

+
+ + \ No newline at end of file diff --git a/examples/Go/WebApp/Example/templates/index.html b/examples/Go/WebApp/Example/templates/index.html new file mode 100644 index 00000000..ec23ad24 --- /dev/null +++ b/examples/Go/WebApp/Example/templates/index.html @@ -0,0 +1,38 @@ + + + + + + {{.Title}} + + + + + + +
+

{{.Message}}

+

{{.App}}

+
+ + \ No newline at end of file diff --git a/examples/Go/WebApp/FeatureFlag/README.md b/examples/Go/WebApp/FeatureFlag/README.md new file mode 100644 index 00000000..de2ae79e --- /dev/null +++ b/examples/Go/WebApp/FeatureFlag/README.md @@ -0,0 +1,51 @@ +# Gin Feature Flags Web App + +This is a Gin web application using a feature flag in Azure App Configuration to dynamically control the availability of a new web page without restarting or redeploying it. + +## Prerequisites + +- An Azure account with an active subscription +- An Azure App Configuration store +- A feature flag named "Beta" in your App Configuration store +- Go 1.23 or later + +## Running the Example + +1. **Create a feature flag in Azure App Configuration:** + + Add a feature flag called *Beta* to the App Configuration store and leave **Label** and **Description** with their default values. For more information about how to add feature flags to a store using the Azure portal or the CLI, go to [Create a feature flag](https://learn.microsoft.com/azure/azure-app-configuration/manage-feature-flags?tabs=azure-portal#create-a-feature-flag). + +2. **Set environment variable:** + + **Windows PowerShell:** + ```powershell + $env:AZURE_APPCONFIG_CONNECTION_STRING = "your-connection-string" + ``` + + **Windows Command Prompt:** + ```cmd + setx AZURE_APPCONFIG_CONNECTION_STRING "your-connection-string" + ``` + + **Linux/macOS:** + ```bash + export AZURE_APPCONFIG_CONNECTION_STRING="your-connection-string" + ``` + +## Running the Application + +```bash +go run main.go +``` + +Open http://localhost:8080 in your browser. + +## Testing the Feature Flag + +1. **Start the application** - Beta menu item should be hidden (feature disabled) +2. **Go to Azure portal** → App Configuration → Feature manager +3. **Enable the "Beta" feature flag** +4. **Wait up to 30 seconds** and refresh the page +5. **Observe the Beta menu item** appears in navigation +6. **Click the Beta menu** to access the Beta page +7. **Disable the flag again** and test that `/beta` returns 404 \ No newline at end of file diff --git a/examples/Go/WebApp/FeatureFlag/main.go b/examples/Go/WebApp/FeatureFlag/main.go new file mode 100644 index 00000000..e43e2d6e --- /dev/null +++ b/examples/Go/WebApp/FeatureFlag/main.go @@ -0,0 +1,162 @@ +package main + +import ( + "context" + "fmt" + "log" + "net/http" + "os" + + "github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration" + "github.com/gin-gonic/gin" + "github.com/microsoft/Featuremanagement-Go/featuremanagement" + "github.com/microsoft/Featuremanagement-Go/featuremanagement/providers/azappconfig" +) + +type WebApp struct { + featureManager *featuremanagement.FeatureManager + appConfig *azureappconfiguration.AzureAppConfiguration +} + +func loadAzureAppConfiguration(ctx context.Context) (*azureappconfiguration.AzureAppConfiguration, error) { + connectionString := os.Getenv("AZURE_APPCONFIG_CONNECTION_STRING") + if connectionString == "" { + return nil, fmt.Errorf("AZURE_APPCONFIG_CONNECTION_STRING environment variable is not set") + } + + authOptions := azureappconfiguration.AuthenticationOptions{ + ConnectionString: connectionString, + } + + options := &azureappconfiguration.Options{ + FeatureFlagOptions: azureappconfiguration.FeatureFlagOptions{ + Enabled: true, + Selectors: []azureappconfiguration.Selector{ + { + KeyFilter: "*", + LabelFilter: "", + }, + }, + RefreshOptions: azureappconfiguration.RefreshOptions{ + Enabled: true, + }, + }, + } + + appConfig, err := azureappconfiguration.Load(ctx, authOptions, options) + if err != nil { + return nil, fmt.Errorf("failed to load configuration: %w", err) + } + + return appConfig, nil +} + +func (app *WebApp) featureMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + // Refresh configuration to get latest feature flags + ctx := context.Background() + if err := app.appConfig.Refresh(ctx); err != nil { + log.Printf("Error refreshing configuration: %v", err) + } + + // Check if Beta feature is enabled + betaEnabled, err := app.featureManager.IsEnabled("Beta") + if err != nil { + log.Printf("Error checking Beta feature: %v", err) + betaEnabled = false + } + + // Store feature flag status for use in templates + c.Set("betaEnabled", betaEnabled) + c.Next() + } +} + +func (app *WebApp) setupRoutes(r *gin.Engine) { + // Apply feature middleware to all routes + r.Use(app.featureMiddleware()) + + // Load HTML templates + r.LoadHTMLGlob("templates/*.html") + + // Routes + r.GET("/", app.homeHandler) + r.GET("/beta", app.betaHandler) +} + +// Home page handler +func (app *WebApp) homeHandler(c *gin.Context) { + betaEnabled := c.GetBool("betaEnabled") + + c.HTML(http.StatusOK, "index.html", gin.H{ + "title": "Feature Management Demo", + "betaEnabled": betaEnabled, + }) +} + +// Beta page handler +func (app *WebApp) betaHandler(c *gin.Context) { + betaEnabled := c.GetBool("betaEnabled") + + // Feature gate logic - return 404 if feature is not enabled + if !betaEnabled { + c.HTML(http.StatusNotFound, "404.html", gin.H{ + "title": "Page Not Found", + "message": "The page you are looking for does not exist or is not available.", + }) + return + } + + c.HTML(http.StatusOK, "beta.html", gin.H{ + "title": "Beta Page", + }) +} + +func main() { + ctx := context.Background() + + // Print startup information + fmt.Println("=== Azure App Configuration Feature Flags Web Demo ===") + fmt.Println("Make sure to set the AZURE_APPCONFIG_CONNECTION_STRING environment variable.") + fmt.Println() + + // Load Azure App Configuration + appConfig, err := loadAzureAppConfiguration(ctx) + if err != nil { + log.Fatalf("Error loading Azure App Configuration: %v", err) + } + + // Create feature flag provider + featureFlagProvider, err := azappconfig.NewFeatureFlagProvider(appConfig) + if err != nil { + log.Fatalf("Error creating feature flag provider: %v", err) + } + + // Create feature manager + featureManager, err := featuremanagement.NewFeatureManager(featureFlagProvider, nil) + if err != nil { + log.Fatalf("Error creating feature manager: %v", err) + } + + // Create web app + app := &WebApp{ + featureManager: featureManager, + appConfig: appConfig, + } + + // Setup Gin with default middleware (Logger and Recovery) + r := gin.Default() + + // Setup routes + app.setupRoutes(r) + + // Start server + fmt.Println("Starting server on http://localhost:8080") + fmt.Println("Open http://localhost:8080 in your browser") + fmt.Println("Toggle the 'Beta' feature flag in Azure portal to see changes") + fmt.Println() + + if err := r.Run(":8080"); err != nil { + log.Fatalf("Failed to start server: %v", err) + } +} \ No newline at end of file diff --git a/examples/Go/WebApp/FeatureFlag/templates/404.html b/examples/Go/WebApp/FeatureFlag/templates/404.html new file mode 100644 index 00000000..681c4d40 --- /dev/null +++ b/examples/Go/WebApp/FeatureFlag/templates/404.html @@ -0,0 +1,37 @@ + + + + + + {{.title}} + + + +
+
+
+
+

404

+

Page Not Found

+

{{.message}}

+ + Return to Home +
+
+
+
+ + + + \ No newline at end of file diff --git a/examples/Go/WebApp/FeatureFlag/templates/beta.html b/examples/Go/WebApp/FeatureFlag/templates/beta.html new file mode 100644 index 00000000..b0e06a3f --- /dev/null +++ b/examples/Go/WebApp/FeatureFlag/templates/beta.html @@ -0,0 +1,34 @@ + + + + + + {{.title}} + + + +
+
+
+

This is the beta website.

+
+
+
+ + + + \ No newline at end of file diff --git a/examples/Go/WebApp/FeatureFlag/templates/index.html b/examples/Go/WebApp/FeatureFlag/templates/index.html new file mode 100644 index 00000000..05878360 --- /dev/null +++ b/examples/Go/WebApp/FeatureFlag/templates/index.html @@ -0,0 +1,39 @@ + + + + + + {{.title}} + + + +
+
+
+

Hello from Azure App Configuration

+
+
+ + + + \ No newline at end of file diff --git a/examples/Go/WebApp/Refresh/README.md b/examples/Go/WebApp/Refresh/README.md new file mode 100644 index 00000000..e2814fd9 --- /dev/null +++ b/examples/Go/WebApp/Refresh/README.md @@ -0,0 +1,70 @@ +# Azure App Configuration Gin Web Refresh Example + +This example demonstrates how to use the refresh functionality of Azure App Configuration in a web application built with the Gin framework. + +## Overview + +This web application: + +1. Loads configuration values from Azure App Configuration +2. Configures the Gin web framework based on those values +3. Automatically refreshes the configuration when changed in Azure App Configuration + +## Running the Example + +### Prerequisites + +You need [an Azure subscription](https://azure.microsoft.com/free/) and the following Azure resources to run the examples: + +- [Azure App Configuration store](https://learn.microsoft.com/en-us/azure/azure-app-configuration/quickstart-azure-app-configuration-create?tabs=azure-portal) + +The examples retrieve credentials to access your App Configuration store from environment variables. + +### Add key-values + +Add the following key-values to the App Configuration store and leave **Label** and **Content Type** with their default values: + +| Key | Value | +|------------------------|--------------------| +| *Config.Message* | *Hello World!* | +| *Config.App.Name* | *Gin Web App* | +| *Config.App.DebugMode* | *true* | + +### Setup + +1. Initialize a new Go module. + + ```bash + go mod init gin-example-refresh + ``` +1. Add the required dependencies. + + ```bash + go get github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration + go get github.com/gin-gonic/gin + ``` + +1. Set the connection string as an environment variable: + + ```bash + # Windows + set AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string + + # Linux/macOS + export AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string + ``` + +### Run the Application + +```bash +go run main.go +``` + +Then navigate to `http://localhost:8080` in your web browser. + +### Testing the Refresh Functionality + +1. Start the application +2. While it's running, modify the values in your Azure App Configuration store +3. Within 10 seconds (the configured refresh interval), the application should detect and apply the changes +4. Refresh your browser to see the updated values \ No newline at end of file diff --git a/examples/Go/WebApp/Refresh/main.go b/examples/Go/WebApp/Refresh/main.go new file mode 100644 index 00000000..7a4827be --- /dev/null +++ b/examples/Go/WebApp/Refresh/main.go @@ -0,0 +1,183 @@ +package main + +import ( + "context" + "log" + "os" + "sync" + "time" + + "github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration" + "github.com/gin-gonic/gin" +) + +type Config struct { + App App + Message string +} + +type App struct { + Name string + DebugMode bool +} + +// Global configuration that will be updated on refresh +var ( + config Config + configLock sync.RWMutex +) + +// loadConfiguration handles loading the configuration from Azure App Configuration +func loadConfiguration() (*azureappconfiguration.AzureAppConfiguration, error) { + // Get connection string from environment variable + connectionString := os.Getenv("AZURE_APPCONFIG_CONNECTION_STRING") + + // Options setup + options := &azureappconfiguration.Options{ + Selectors: []azureappconfiguration.Selector{ + { + KeyFilter: "Config.*", + }, + }, + // Remove the prefix when mapping to struct fields + TrimKeyPrefixes: []string{"Config."}, + // Enable refresh every 10 seconds + RefreshOptions: azureappconfiguration.KeyValueRefreshOptions{ + Enabled: true, + Interval: 10 * time.Second, + }, + } + + authOptions := azureappconfiguration.AuthenticationOptions{ + ConnectionString: connectionString, + } + + // Create configuration provider with timeout + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + return azureappconfiguration.Load(ctx, authOptions, options) +} + +// updateConfig safely updates the global configuration +func updateConfig(newConfig Config) { + configLock.Lock() + defer configLock.Unlock() + config = newConfig +} + +// getConfig safely retrieves the global configuration +func getConfig() Config { + configLock.RLock() + defer configLock.RUnlock() + return config +} + +// configRefreshMiddleware is a Gin middleware that attempts to refresh configuration on incoming requests +func configRefreshMiddleware(appCfgProvider *azureappconfiguration.AzureAppConfiguration) gin.HandlerFunc { + return func(c *gin.Context) { + // Start refresh in a goroutine to avoid blocking the request + go func() { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + if err := appCfgProvider.Refresh(ctx); err != nil { + // Just log the error, don't interrupt request processing + log.Printf("Error refreshing configuration: %s", err) + } + }() + + // Continue processing the request + c.Next() + } +} + +// setupRouter creates and configures the Gin router +func setupRouter(appCfgProvider *azureappconfiguration.AzureAppConfiguration) *gin.Engine { + // Get the current config + currentConfig := getConfig() + + if currentConfig.App.DebugMode { + // Set Gin to debug mode + gin.SetMode(gin.DebugMode) + log.Println("Running in DEBUG mode") + } else { + // Set Gin to release mode for production + gin.SetMode(gin.ReleaseMode) + log.Println("Running in RELEASE mode") + } + + // Initialize Gin router + r := gin.Default() + + // Apply our configuration refresh middleware to all routes + r.Use(configRefreshMiddleware(appCfgProvider)) + + // Load HTML templates + r.LoadHTMLGlob("templates/*") + + // Define a route for the homepage + r.GET("/", func(c *gin.Context) { + // Get the latest config for each request + currentConfig := getConfig() + c.HTML(200, "index.html", gin.H{ + "Title": "Home", + "Message": currentConfig.Message, + "App": currentConfig.App.Name, + }) + }) + + // Define a route for the About page + r.GET("/about", func(c *gin.Context) { + c.HTML(200, "about.html", gin.H{ + "Title": "About", + }) + }) + + return r +} + +func main() { + // Load initial configuration + appCfgProvider, err := loadConfiguration() + if err != nil { + log.Fatalf("Error loading configuration: %s", err) + } + + // Parse configuration into struct and update the global config + var initialConfig Config + err = appCfgProvider.Unmarshal(&initialConfig, nil) + if err != nil { + log.Fatalf("Error unmarshalling configuration: %s", err) + } + updateConfig(initialConfig) + + // Register refresh callback + appCfgProvider.OnRefreshSuccess(func() { + log.Println("Configuration changed! Updating values...") + + // Re-unmarshal the configuration + var updatedConfig Config + err := appCfgProvider.Unmarshal(&updatedConfig, nil) + if err != nil { + log.Printf("Error unmarshalling updated configuration: %s", err) + return + } + + // Update our working config + updateConfig(updatedConfig) + + // Log the changes + log.Printf("Updated configuration: Message=%s, App.Name=%s, App.DebugMode=%v", + updatedConfig.Message, updatedConfig.App.Name, updatedConfig.App.DebugMode) + }) + + // Setup the router with refresh middleware + r := setupRouter(appCfgProvider) + + // Start the server + log.Println("Starting server on :8080") + if err := r.Run(":8080"); err != nil { + log.Fatalf("Error starting server: %s", err) + } +} \ No newline at end of file diff --git a/examples/Go/WebApp/Refresh/templates/about.html b/examples/Go/WebApp/Refresh/templates/about.html new file mode 100644 index 00000000..e2814fd9 --- /dev/null +++ b/examples/Go/WebApp/Refresh/templates/about.html @@ -0,0 +1,70 @@ +# Azure App Configuration Gin Web Refresh Example + +This example demonstrates how to use the refresh functionality of Azure App Configuration in a web application built with the Gin framework. + +## Overview + +This web application: + +1. Loads configuration values from Azure App Configuration +2. Configures the Gin web framework based on those values +3. Automatically refreshes the configuration when changed in Azure App Configuration + +## Running the Example + +### Prerequisites + +You need [an Azure subscription](https://azure.microsoft.com/free/) and the following Azure resources to run the examples: + +- [Azure App Configuration store](https://learn.microsoft.com/en-us/azure/azure-app-configuration/quickstart-azure-app-configuration-create?tabs=azure-portal) + +The examples retrieve credentials to access your App Configuration store from environment variables. + +### Add key-values + +Add the following key-values to the App Configuration store and leave **Label** and **Content Type** with their default values: + +| Key | Value | +|------------------------|--------------------| +| *Config.Message* | *Hello World!* | +| *Config.App.Name* | *Gin Web App* | +| *Config.App.DebugMode* | *true* | + +### Setup + +1. Initialize a new Go module. + + ```bash + go mod init gin-example-refresh + ``` +1. Add the required dependencies. + + ```bash + go get github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration + go get github.com/gin-gonic/gin + ``` + +1. Set the connection string as an environment variable: + + ```bash + # Windows + set AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string + + # Linux/macOS + export AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string + ``` + +### Run the Application + +```bash +go run main.go +``` + +Then navigate to `http://localhost:8080` in your web browser. + +### Testing the Refresh Functionality + +1. Start the application +2. While it's running, modify the values in your Azure App Configuration store +3. Within 10 seconds (the configured refresh interval), the application should detect and apply the changes +4. Refresh your browser to see the updated values \ No newline at end of file diff --git a/examples/Go/WebApp/Refresh/templates/index.html b/examples/Go/WebApp/Refresh/templates/index.html new file mode 100644 index 00000000..ec23ad24 --- /dev/null +++ b/examples/Go/WebApp/Refresh/templates/index.html @@ -0,0 +1,38 @@ + + + + + + {{.Title}} + + + + + + +
+

{{.Message}}

+

{{.App}}

+
+ + \ No newline at end of file diff --git a/examples/README.md b/examples/README.md index d2c4bb30..eed83328 100644 --- a/examples/README.md +++ b/examples/README.md @@ -70,4 +70,30 @@ This example shows how to use the Azure App Configuration Python Provider in you ### [python-flask-webapp-sample](./Python/python-flask-webapp-sample/) -This example shows how to use the Azure App Configuration Python Provider in your python Flask app. \ No newline at end of file +This example shows how to use the Azure App Configuration Python Provider in your python Flask app. + +## Go Samples + +### [AI Chat App](./Go/ChatApp/) + +This example showcases a Go console application that retrieves chat responses from Azure OpenAI. It demonstrates how to configure chat completion using AI Configuration from Azure App Configuration, enabling rapid prompt iteration and frequent tuning of model parameters—without requiring application restarts, rebuilds, or redeployments. + +### [Console app](./Go/ConsoleApp/Example/) + +This example demonstrates how to use Azure App Configuration Go Provider in a console application. + +### [Console app (dynamic configuration)](./Go/ConsoleApp/Refresh/) + +This example demonstrates how to enable dynamic configuration from App Configuration in a console application. + +### [Gin web app](./Go/WebApp/Example/) + +This example demonstrates how to use Azure App Configuration Go Provider in a web application built with the Gin framework. + +### [Gin web app (dynamic configuration)](./Go/WebApp/Refresh/) + +This example demonstrates how to enable dynamic configuration from App Configuration in a web application built with the Gin framework. + +### [Gin web app (feature management)](./Go/WebApp/FeatureFlag/) + +This is a Gin web application using a feature flag in Azure App Configuration to dynamically control the availability of a new web page without restarting or redeploying it. \ No newline at end of file From 84cd0595246595184e488427d9b8f7e459a5acd4 Mon Sep 17 00:00:00 2001 From: "Lingling Ye (from Dev Box)" Date: Tue, 30 Sep 2025 10:02:35 +0800 Subject: [PATCH 2/5] update README --- examples/README.md | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/examples/README.md b/examples/README.md index eed83328..0919fa10 100644 --- a/examples/README.md +++ b/examples/README.md @@ -78,22 +78,10 @@ This example shows how to use the Azure App Configuration Python Provider in you This example showcases a Go console application that retrieves chat responses from Azure OpenAI. It demonstrates how to configure chat completion using AI Configuration from Azure App Configuration, enabling rapid prompt iteration and frequent tuning of model parameters—without requiring application restarts, rebuilds, or redeployments. -### [Console app](./Go/ConsoleApp/Example/) +### [Console app](./Go/ConsoleApp/) -This example demonstrates how to use Azure App Configuration Go Provider in a console application. +These examples demonstrate how to use the basic features of Azure App Configuration Go Provider in console applications. -### [Console app (dynamic configuration)](./Go/ConsoleApp/Refresh/) +### [Gin web app](./Go/WebApp/) -This example demonstrates how to enable dynamic configuration from App Configuration in a console application. - -### [Gin web app](./Go/WebApp/Example/) - -This example demonstrates how to use Azure App Configuration Go Provider in a web application built with the Gin framework. - -### [Gin web app (dynamic configuration)](./Go/WebApp/Refresh/) - -This example demonstrates how to enable dynamic configuration from App Configuration in a web application built with the Gin framework. - -### [Gin web app (feature management)](./Go/WebApp/FeatureFlag/) - -This is a Gin web application using a feature flag in Azure App Configuration to dynamically control the availability of a new web page without restarting or redeploying it. \ No newline at end of file +These examples demonstrate how to use Azure App Configuration Go Provider and feature flags in web applications built with the Gin framework. From 957faf2ee4aeedfd15ffb711e10ed8e4277769ae Mon Sep 17 00:00:00 2001 From: "Lingling Ye (from Dev Box)" Date: Tue, 30 Sep 2025 10:14:00 +0800 Subject: [PATCH 3/5] add empty line --- examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README.md b/examples/README.md index 0919fa10..7abaf3f4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -85,3 +85,4 @@ These examples demonstrate how to use the basic features of Azure App Configurat ### [Gin web app](./Go/WebApp/) These examples demonstrate how to use Azure App Configuration Go Provider and feature flags in web applications built with the Gin framework. + From 8d3cc9f7a708151f11a8d433b94b491f05946e0b Mon Sep 17 00:00:00 2001 From: "Lingling Ye (from Dev Box)" Date: Fri, 10 Oct 2025 13:25:33 +0800 Subject: [PATCH 4/5] update examples --- examples/Go/ConsoleApp/Example/README.md | 60 ------ examples/Go/ConsoleApp/Example/main.go | 79 -------- .../Go/ConsoleApp/{Refresh => }/README.md | 0 examples/Go/ConsoleApp/{Refresh => }/main.go | 86 ++++---- examples/Go/WebApp/Example/README.md | 62 ------ examples/Go/WebApp/Example/main.go | 105 ---------- .../Go/WebApp/Example/templates/about.html | 38 ---- .../Go/WebApp/Example/templates/index.html | 38 ---- .../Go/WebApp/{FeatureFlag => }/README.md | 0 examples/Go/WebApp/Refresh/README.md | 70 ------- examples/Go/WebApp/Refresh/main.go | 183 ------------------ .../Go/WebApp/Refresh/templates/about.html | 70 ------- .../Go/WebApp/Refresh/templates/index.html | 38 ---- examples/Go/WebApp/{FeatureFlag => }/main.go | 93 +++++---- .../{FeatureFlag => }/templates/404.html | 0 .../{FeatureFlag => }/templates/beta.html | 0 .../{FeatureFlag => }/templates/index.html | 0 examples/README.md | 4 +- 18 files changed, 89 insertions(+), 837 deletions(-) delete mode 100644 examples/Go/ConsoleApp/Example/README.md delete mode 100644 examples/Go/ConsoleApp/Example/main.go rename examples/Go/ConsoleApp/{Refresh => }/README.md (100%) rename examples/Go/ConsoleApp/{Refresh => }/main.go (99%) delete mode 100644 examples/Go/WebApp/Example/README.md delete mode 100644 examples/Go/WebApp/Example/main.go delete mode 100644 examples/Go/WebApp/Example/templates/about.html delete mode 100644 examples/Go/WebApp/Example/templates/index.html rename examples/Go/WebApp/{FeatureFlag => }/README.md (100%) delete mode 100644 examples/Go/WebApp/Refresh/README.md delete mode 100644 examples/Go/WebApp/Refresh/main.go delete mode 100644 examples/Go/WebApp/Refresh/templates/about.html delete mode 100644 examples/Go/WebApp/Refresh/templates/index.html rename examples/Go/WebApp/{FeatureFlag => }/main.go (94%) rename examples/Go/WebApp/{FeatureFlag => }/templates/404.html (100%) rename examples/Go/WebApp/{FeatureFlag => }/templates/beta.html (100%) rename examples/Go/WebApp/{FeatureFlag => }/templates/index.html (100%) diff --git a/examples/Go/ConsoleApp/Example/README.md b/examples/Go/ConsoleApp/Example/README.md deleted file mode 100644 index 537836f6..00000000 --- a/examples/Go/ConsoleApp/Example/README.md +++ /dev/null @@ -1,60 +0,0 @@ -# Azure App Configuration Console Example - -This example demonstrates how to use Azure App Configuration in a console/command-line application. - -## Overview - -This simple console application: - -1. Loads configuration values from Azure App Configuration -2. Binds them to target configuration struct - -## Running the Example - -### Prerequisites - -You need [an Azure subscription](https://azure.microsoft.com/free/) and the following Azure resources to run the examples: - -- [Azure App Configuration store](https://learn.microsoft.com/en-us/azure/azure-app-configuration/quickstart-azure-app-configuration-create?tabs=azure-portal) - -The examples retrieve credentials to access your App Configuration store from environment variables. -Alternatively, edit the source code to include the appropriate credentials. - -### Add key-values - -Add the following key-values to the App Configuration store and leave **Label** and **Content Type** with their default values. For more information about how to add key-values to a store using the Azure portal or the CLI, go to [Create a key-value](./quickstart-azure-app-configuration-create.md#create-a-key-value). - -| Key | Value | -|------------------------|----------------| -| *Config.Message* | *Hello World!* | -| *Config.Font.Color* | *blue* | -| *Config.Font.Size* | *12* | - -### Setup - -1. Initialize a new Go module. - - ```bash - go mod init console-example-app - ``` -1. Add the Azure App Configuration provider as a dependency. - - ```bash - go get github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration - ``` - -1. Set the connection string as an environment variable: - - ```bash - # Windows - set AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string - - # Linux/macOS - export AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string - ``` - -### Run the Application - -```bash -go run main.go -``` \ No newline at end of file diff --git a/examples/Go/ConsoleApp/Example/main.go b/examples/Go/ConsoleApp/Example/main.go deleted file mode 100644 index e639909e..00000000 --- a/examples/Go/ConsoleApp/Example/main.go +++ /dev/null @@ -1,79 +0,0 @@ -package main - -import ( - "context" - "fmt" - "log" - "os" - "time" - - "github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration" -) - -type Config struct { - Font Font - Message string -} - -type Font struct { - Color string - Size int -} - -// loadConfiguration handles loading the configuration from Azure App Configuration -func loadConfiguration() (Config, error) { - // Get connection string from environment variable - connectionString := os.Getenv("AZURE_APPCONFIG_CONNECTION_STRING") - - // Configuration setup - options := &azureappconfiguration.Options{ - Selectors: []azureappconfiguration.Selector{ - { - KeyFilter: "Config.*", - }, - }, - // Remove the prefix when mapping to struct fields - TrimKeyPrefixes: []string{"Config."}, - } - - authOptions := azureappconfiguration.AuthenticationOptions{ - ConnectionString: connectionString, - } - - // Create configuration provider with timeout - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - appCfgProvider, err := azureappconfiguration.Load(ctx, authOptions, options) - if err != nil { - return Config{}, err - } - - // Parse configuration into struct - var config Config - err = appCfgProvider.Unmarshal(&config, nil) - if err != nil { - return Config{}, err - } - - return config, nil -} - -func main() { - fmt.Println("Azure App Configuration - Console Example") - fmt.Println("----------------------------------------") - - // Load configuration - fmt.Println("Loading configuration from Azure App Configuration...") - config, err := loadConfiguration() - if err != nil { - log.Fatalf("Error loading configuration: %s", err) - } - - // Display the configuration values - fmt.Println("\nConfiguration Values:") - fmt.Println("--------------------") - fmt.Printf("Font Color: %s\n", config.Font.Color) - fmt.Printf("Font Size: %d\n", config.Font.Size) - fmt.Printf("Message: %s\n", config.Message) -} \ No newline at end of file diff --git a/examples/Go/ConsoleApp/Refresh/README.md b/examples/Go/ConsoleApp/README.md similarity index 100% rename from examples/Go/ConsoleApp/Refresh/README.md rename to examples/Go/ConsoleApp/README.md diff --git a/examples/Go/ConsoleApp/Refresh/main.go b/examples/Go/ConsoleApp/main.go similarity index 99% rename from examples/Go/ConsoleApp/Refresh/main.go rename to examples/Go/ConsoleApp/main.go index dda74681..470cb230 100644 --- a/examples/Go/ConsoleApp/Refresh/main.go +++ b/examples/Go/ConsoleApp/main.go @@ -22,48 +22,6 @@ type Font struct { Size int } -// initializeAppConfiguration handles loading the configuration from Azure App Configuration -func initializeAppConfiguration() (*azureappconfiguration.AzureAppConfiguration, error) { - // Get connection string from environment variable - connectionString := os.Getenv("AZURE_APPCONFIG_CONNECTION_STRING") - - // Options setup - options := &azureappconfiguration.Options{ - Selectors: []azureappconfiguration.Selector{ - { - KeyFilter: "Config.*", - }, - }, - // Remove the prefix when mapping to struct fields - TrimKeyPrefixes: []string{"Config."}, - // Enable refresh every 10 seconds - RefreshOptions: azureappconfiguration.KeyValueRefreshOptions{ - Enabled: true, - Interval: 10 * time.Second, - }, - } - - authOptions := azureappconfiguration.AuthenticationOptions{ - ConnectionString: connectionString, - } - - // Create configuration provider with timeout - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - return azureappconfiguration.Load(ctx, authOptions, options) -} - -// displayConfig prints the current configuration values -func displayConfig(config Config) { - fmt.Println("\nCurrent Configuration Values:") - fmt.Println("--------------------") - fmt.Printf("Font Color: %s\n", config.Font.Color) - fmt.Printf("Font Size: %d\n", config.Font.Size) - fmt.Printf("Message: %s\n", config.Message) - fmt.Println("--------------------") -} - func main() { fmt.Println("Azure App Configuration - Console Refresh Example") fmt.Println("----------------------------------------") @@ -134,4 +92,46 @@ func main() { return } } -} \ No newline at end of file +} + +// initializeAppConfiguration handles loading the configuration from Azure App Configuration +func initializeAppConfiguration() (*azureappconfiguration.AzureAppConfiguration, error) { + // Get connection string from environment variable + connectionString := os.Getenv("AZURE_APPCONFIG_CONNECTION_STRING") + + // Options setup + options := &azureappconfiguration.Options{ + Selectors: []azureappconfiguration.Selector{ + { + KeyFilter: "Config.*", + }, + }, + // Remove the prefix when mapping to struct fields + TrimKeyPrefixes: []string{"Config."}, + // Enable refresh every 10 seconds + RefreshOptions: azureappconfiguration.KeyValueRefreshOptions{ + Enabled: true, + Interval: 10 * time.Second, + }, + } + + authOptions := azureappconfiguration.AuthenticationOptions{ + ConnectionString: connectionString, + } + + // Create configuration provider with timeout + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + return azureappconfiguration.Load(ctx, authOptions, options) +} + +// displayConfig prints the current configuration values +func displayConfig(config Config) { + fmt.Println("\nCurrent Configuration Values:") + fmt.Println("--------------------") + fmt.Printf("Font Color: %s\n", config.Font.Color) + fmt.Printf("Font Size: %d\n", config.Font.Size) + fmt.Printf("Message: %s\n", config.Message) + fmt.Println("--------------------") +} diff --git a/examples/Go/WebApp/Example/README.md b/examples/Go/WebApp/Example/README.md deleted file mode 100644 index 531d6675..00000000 --- a/examples/Go/WebApp/Example/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Azure App Configuration Gin Web Example - -This example demonstrates how to use Azure App Configuration in a web application built with the Gin framework. - -## Overview - -This web application: - -1. Loads configuration values from Azure App Configuration -2. Configures the Gin web framework based on those values - -## Running the Example - -### Prerequisites - -You need [an Azure subscription](https://azure.microsoft.com/free/) and the following Azure resources to run the examples: - -- [Azure App Configuration store](https://learn.microsoft.com/en-us/azure/azure-app-configuration/quickstart-azure-app-configuration-create?tabs=azure-portal) - -The examples retrieve credentials to access your App Configuration store from environment variables. - -### Add key-values - -Add the following key-values to the App Configuration store and leave **Label** and **Content Type** with their default values: - -| Key | Value | -|------------------------|--------------------| -| *Config.Message* | *Hello World!* | -| *Config.App.Name* | *Gin Web App* | -| *Config.App.DebugMode* | *true* | - -### Setup - -1. Initialize a new Go module. - - ```bash - go mod init gin-example-app - ``` -1. Add the required dependencies. - - ```bash - go get github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration - go get github.com/gin-gonic/gin - ``` - -1. Set the connection string as an environment variable: - - ```bash - # Windows - set AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string - - # Linux/macOS - export AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string - ``` - -### Run the Application - -```bash -go run main.go -``` - -Then navigate to `http://localhost:8080` in your web browser. \ No newline at end of file diff --git a/examples/Go/WebApp/Example/main.go b/examples/Go/WebApp/Example/main.go deleted file mode 100644 index a9f956d5..00000000 --- a/examples/Go/WebApp/Example/main.go +++ /dev/null @@ -1,105 +0,0 @@ -package main - -import ( - "context" - "log" - "os" - "time" - - "github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration" - "github.com/gin-gonic/gin" -) - -type Config struct { - App App - Message string -} - -type App struct { - Name string - DebugMode bool -} - -// loadConfiguration handles loading the configuration from Azure App Configuration -func loadConfiguration() (Config, error) { - // Get connection string from environment variable - connectionString := os.Getenv("AZURE_APPCONFIG_CONNECTION_STRING") - - // Configuration setup - options := &azureappconfiguration.Options{ - Selectors: []azureappconfiguration.Selector{ - { - KeyFilter: "Config.*", - }, - }, - // Remove the prefix when mapping to struct fields - TrimKeyPrefixes: []string{"Config."}, - } - - authOptions := azureappconfiguration.AuthenticationOptions{ - ConnectionString: connectionString, - } - - // Create configuration provider with timeout - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - appCfgProvider, err := azureappconfiguration.Load(ctx, authOptions, options) - if err != nil { - return Config{}, err - } - - // Parse configuration into struct - var config Config - err = appCfgProvider.Unmarshal(&config, nil) - if err != nil { - return Config{}, err - } - - return config, nil -} - -func main() { - // Load configuration - config, err := loadConfiguration() - if err != nil { - log.Fatalf("Error loading configuration: %s", err) - } - - if config.App.DebugMode { - // Set Gin to debug mode - gin.SetMode(gin.DebugMode) - log.Println("Running in DEBUG mode") - } else { - // Set Gin to release mode for production - gin.SetMode(gin.ReleaseMode) - log.Println("Running in RELEASE mode") - } - - // Initialize Gin router - r := gin.Default() - - // Load HTML templates - r.LoadHTMLGlob("templates/*") - - // Define a route for the homepage - r.GET("/", func(c *gin.Context) { - c.HTML(200, "index.html", gin.H{ - "Title": "Home", - "Message": config.Message, - "App": config.App.Name, - }) - }) - - // Define a route for the About page - r.GET("/about", func(c *gin.Context) { - c.HTML(200, "about.html", gin.H{ - "Title": "About", - }) - }) - - // Start the server - if err := r.Run(":8080"); err != nil { - log.Fatalf("Error starting server: %s", err) - } -} \ No newline at end of file diff --git a/examples/Go/WebApp/Example/templates/about.html b/examples/Go/WebApp/Example/templates/about.html deleted file mode 100644 index b3eb5288..00000000 --- a/examples/Go/WebApp/Example/templates/about.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - {{.Title}} - - - - - - -
-

About Page

-

This is the about page where you can add information about your website or app.

-
- - \ No newline at end of file diff --git a/examples/Go/WebApp/Example/templates/index.html b/examples/Go/WebApp/Example/templates/index.html deleted file mode 100644 index ec23ad24..00000000 --- a/examples/Go/WebApp/Example/templates/index.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - {{.Title}} - - - - - - -
-

{{.Message}}

-

{{.App}}

-
- - \ No newline at end of file diff --git a/examples/Go/WebApp/FeatureFlag/README.md b/examples/Go/WebApp/README.md similarity index 100% rename from examples/Go/WebApp/FeatureFlag/README.md rename to examples/Go/WebApp/README.md diff --git a/examples/Go/WebApp/Refresh/README.md b/examples/Go/WebApp/Refresh/README.md deleted file mode 100644 index e2814fd9..00000000 --- a/examples/Go/WebApp/Refresh/README.md +++ /dev/null @@ -1,70 +0,0 @@ -# Azure App Configuration Gin Web Refresh Example - -This example demonstrates how to use the refresh functionality of Azure App Configuration in a web application built with the Gin framework. - -## Overview - -This web application: - -1. Loads configuration values from Azure App Configuration -2. Configures the Gin web framework based on those values -3. Automatically refreshes the configuration when changed in Azure App Configuration - -## Running the Example - -### Prerequisites - -You need [an Azure subscription](https://azure.microsoft.com/free/) and the following Azure resources to run the examples: - -- [Azure App Configuration store](https://learn.microsoft.com/en-us/azure/azure-app-configuration/quickstart-azure-app-configuration-create?tabs=azure-portal) - -The examples retrieve credentials to access your App Configuration store from environment variables. - -### Add key-values - -Add the following key-values to the App Configuration store and leave **Label** and **Content Type** with their default values: - -| Key | Value | -|------------------------|--------------------| -| *Config.Message* | *Hello World!* | -| *Config.App.Name* | *Gin Web App* | -| *Config.App.DebugMode* | *true* | - -### Setup - -1. Initialize a new Go module. - - ```bash - go mod init gin-example-refresh - ``` -1. Add the required dependencies. - - ```bash - go get github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration - go get github.com/gin-gonic/gin - ``` - -1. Set the connection string as an environment variable: - - ```bash - # Windows - set AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string - - # Linux/macOS - export AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string - ``` - -### Run the Application - -```bash -go run main.go -``` - -Then navigate to `http://localhost:8080` in your web browser. - -### Testing the Refresh Functionality - -1. Start the application -2. While it's running, modify the values in your Azure App Configuration store -3. Within 10 seconds (the configured refresh interval), the application should detect and apply the changes -4. Refresh your browser to see the updated values \ No newline at end of file diff --git a/examples/Go/WebApp/Refresh/main.go b/examples/Go/WebApp/Refresh/main.go deleted file mode 100644 index 7a4827be..00000000 --- a/examples/Go/WebApp/Refresh/main.go +++ /dev/null @@ -1,183 +0,0 @@ -package main - -import ( - "context" - "log" - "os" - "sync" - "time" - - "github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration" - "github.com/gin-gonic/gin" -) - -type Config struct { - App App - Message string -} - -type App struct { - Name string - DebugMode bool -} - -// Global configuration that will be updated on refresh -var ( - config Config - configLock sync.RWMutex -) - -// loadConfiguration handles loading the configuration from Azure App Configuration -func loadConfiguration() (*azureappconfiguration.AzureAppConfiguration, error) { - // Get connection string from environment variable - connectionString := os.Getenv("AZURE_APPCONFIG_CONNECTION_STRING") - - // Options setup - options := &azureappconfiguration.Options{ - Selectors: []azureappconfiguration.Selector{ - { - KeyFilter: "Config.*", - }, - }, - // Remove the prefix when mapping to struct fields - TrimKeyPrefixes: []string{"Config."}, - // Enable refresh every 10 seconds - RefreshOptions: azureappconfiguration.KeyValueRefreshOptions{ - Enabled: true, - Interval: 10 * time.Second, - }, - } - - authOptions := azureappconfiguration.AuthenticationOptions{ - ConnectionString: connectionString, - } - - // Create configuration provider with timeout - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - defer cancel() - - return azureappconfiguration.Load(ctx, authOptions, options) -} - -// updateConfig safely updates the global configuration -func updateConfig(newConfig Config) { - configLock.Lock() - defer configLock.Unlock() - config = newConfig -} - -// getConfig safely retrieves the global configuration -func getConfig() Config { - configLock.RLock() - defer configLock.RUnlock() - return config -} - -// configRefreshMiddleware is a Gin middleware that attempts to refresh configuration on incoming requests -func configRefreshMiddleware(appCfgProvider *azureappconfiguration.AzureAppConfiguration) gin.HandlerFunc { - return func(c *gin.Context) { - // Start refresh in a goroutine to avoid blocking the request - go func() { - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - - if err := appCfgProvider.Refresh(ctx); err != nil { - // Just log the error, don't interrupt request processing - log.Printf("Error refreshing configuration: %s", err) - } - }() - - // Continue processing the request - c.Next() - } -} - -// setupRouter creates and configures the Gin router -func setupRouter(appCfgProvider *azureappconfiguration.AzureAppConfiguration) *gin.Engine { - // Get the current config - currentConfig := getConfig() - - if currentConfig.App.DebugMode { - // Set Gin to debug mode - gin.SetMode(gin.DebugMode) - log.Println("Running in DEBUG mode") - } else { - // Set Gin to release mode for production - gin.SetMode(gin.ReleaseMode) - log.Println("Running in RELEASE mode") - } - - // Initialize Gin router - r := gin.Default() - - // Apply our configuration refresh middleware to all routes - r.Use(configRefreshMiddleware(appCfgProvider)) - - // Load HTML templates - r.LoadHTMLGlob("templates/*") - - // Define a route for the homepage - r.GET("/", func(c *gin.Context) { - // Get the latest config for each request - currentConfig := getConfig() - c.HTML(200, "index.html", gin.H{ - "Title": "Home", - "Message": currentConfig.Message, - "App": currentConfig.App.Name, - }) - }) - - // Define a route for the About page - r.GET("/about", func(c *gin.Context) { - c.HTML(200, "about.html", gin.H{ - "Title": "About", - }) - }) - - return r -} - -func main() { - // Load initial configuration - appCfgProvider, err := loadConfiguration() - if err != nil { - log.Fatalf("Error loading configuration: %s", err) - } - - // Parse configuration into struct and update the global config - var initialConfig Config - err = appCfgProvider.Unmarshal(&initialConfig, nil) - if err != nil { - log.Fatalf("Error unmarshalling configuration: %s", err) - } - updateConfig(initialConfig) - - // Register refresh callback - appCfgProvider.OnRefreshSuccess(func() { - log.Println("Configuration changed! Updating values...") - - // Re-unmarshal the configuration - var updatedConfig Config - err := appCfgProvider.Unmarshal(&updatedConfig, nil) - if err != nil { - log.Printf("Error unmarshalling updated configuration: %s", err) - return - } - - // Update our working config - updateConfig(updatedConfig) - - // Log the changes - log.Printf("Updated configuration: Message=%s, App.Name=%s, App.DebugMode=%v", - updatedConfig.Message, updatedConfig.App.Name, updatedConfig.App.DebugMode) - }) - - // Setup the router with refresh middleware - r := setupRouter(appCfgProvider) - - // Start the server - log.Println("Starting server on :8080") - if err := r.Run(":8080"); err != nil { - log.Fatalf("Error starting server: %s", err) - } -} \ No newline at end of file diff --git a/examples/Go/WebApp/Refresh/templates/about.html b/examples/Go/WebApp/Refresh/templates/about.html deleted file mode 100644 index e2814fd9..00000000 --- a/examples/Go/WebApp/Refresh/templates/about.html +++ /dev/null @@ -1,70 +0,0 @@ -# Azure App Configuration Gin Web Refresh Example - -This example demonstrates how to use the refresh functionality of Azure App Configuration in a web application built with the Gin framework. - -## Overview - -This web application: - -1. Loads configuration values from Azure App Configuration -2. Configures the Gin web framework based on those values -3. Automatically refreshes the configuration when changed in Azure App Configuration - -## Running the Example - -### Prerequisites - -You need [an Azure subscription](https://azure.microsoft.com/free/) and the following Azure resources to run the examples: - -- [Azure App Configuration store](https://learn.microsoft.com/en-us/azure/azure-app-configuration/quickstart-azure-app-configuration-create?tabs=azure-portal) - -The examples retrieve credentials to access your App Configuration store from environment variables. - -### Add key-values - -Add the following key-values to the App Configuration store and leave **Label** and **Content Type** with their default values: - -| Key | Value | -|------------------------|--------------------| -| *Config.Message* | *Hello World!* | -| *Config.App.Name* | *Gin Web App* | -| *Config.App.DebugMode* | *true* | - -### Setup - -1. Initialize a new Go module. - - ```bash - go mod init gin-example-refresh - ``` -1. Add the required dependencies. - - ```bash - go get github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration - go get github.com/gin-gonic/gin - ``` - -1. Set the connection string as an environment variable: - - ```bash - # Windows - set AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string - - # Linux/macOS - export AZURE_APPCONFIG_CONNECTION_STRING=your-connection-string - ``` - -### Run the Application - -```bash -go run main.go -``` - -Then navigate to `http://localhost:8080` in your web browser. - -### Testing the Refresh Functionality - -1. Start the application -2. While it's running, modify the values in your Azure App Configuration store -3. Within 10 seconds (the configured refresh interval), the application should detect and apply the changes -4. Refresh your browser to see the updated values \ No newline at end of file diff --git a/examples/Go/WebApp/Refresh/templates/index.html b/examples/Go/WebApp/Refresh/templates/index.html deleted file mode 100644 index ec23ad24..00000000 --- a/examples/Go/WebApp/Refresh/templates/index.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - {{.Title}} - - - - - - -
-

{{.Message}}

-

{{.App}}

-
- - \ No newline at end of file diff --git a/examples/Go/WebApp/FeatureFlag/main.go b/examples/Go/WebApp/main.go similarity index 94% rename from examples/Go/WebApp/FeatureFlag/main.go rename to examples/Go/WebApp/main.go index e43e2d6e..60adf125 100644 --- a/examples/Go/WebApp/FeatureFlag/main.go +++ b/examples/Go/WebApp/main.go @@ -18,6 +18,50 @@ type WebApp struct { appConfig *azureappconfiguration.AzureAppConfiguration } +func main() { + ctx := context.Background() + + // Load config from Azure App Configuration + appConfig, err := loadAzureAppConfiguration(ctx) + if err != nil { + log.Fatalf("Error loading Azure App Configuration: %v", err) + } + + // Create feature flag provider + featureFlagProvider, err := azappconfig.NewFeatureFlagProvider(appConfig) + if err != nil { + log.Fatalf("Error creating feature flag provider: %v", err) + } + + // Create feature manager + featureManager, err := featuremanagement.NewFeatureManager(featureFlagProvider, nil) + if err != nil { + log.Fatalf("Error creating feature manager: %v", err) + } + + // Create web app + app := &WebApp{ + featureManager: featureManager, + appConfig: appConfig, + } + + // Setup Gin with default middleware (Logger and Recovery) + r := gin.Default() + + // Setup routes + app.setupRoutes(r) + + // Start server + fmt.Println("Starting server on http://localhost:8080") + fmt.Println("Open http://localhost:8080 in your browser") + fmt.Println("Toggle the 'Beta' feature flag in Azure portal to see changes") + fmt.Println() + + if err := r.Run(":8080"); err != nil { + log.Fatalf("Failed to start server: %v", err) + } +} + func loadAzureAppConfiguration(ctx context.Context) (*azureappconfiguration.AzureAppConfiguration, error) { connectionString := os.Getenv("AZURE_APPCONFIG_CONNECTION_STRING") if connectionString == "" { @@ -111,52 +155,3 @@ func (app *WebApp) betaHandler(c *gin.Context) { "title": "Beta Page", }) } - -func main() { - ctx := context.Background() - - // Print startup information - fmt.Println("=== Azure App Configuration Feature Flags Web Demo ===") - fmt.Println("Make sure to set the AZURE_APPCONFIG_CONNECTION_STRING environment variable.") - fmt.Println() - - // Load Azure App Configuration - appConfig, err := loadAzureAppConfiguration(ctx) - if err != nil { - log.Fatalf("Error loading Azure App Configuration: %v", err) - } - - // Create feature flag provider - featureFlagProvider, err := azappconfig.NewFeatureFlagProvider(appConfig) - if err != nil { - log.Fatalf("Error creating feature flag provider: %v", err) - } - - // Create feature manager - featureManager, err := featuremanagement.NewFeatureManager(featureFlagProvider, nil) - if err != nil { - log.Fatalf("Error creating feature manager: %v", err) - } - - // Create web app - app := &WebApp{ - featureManager: featureManager, - appConfig: appConfig, - } - - // Setup Gin with default middleware (Logger and Recovery) - r := gin.Default() - - // Setup routes - app.setupRoutes(r) - - // Start server - fmt.Println("Starting server on http://localhost:8080") - fmt.Println("Open http://localhost:8080 in your browser") - fmt.Println("Toggle the 'Beta' feature flag in Azure portal to see changes") - fmt.Println() - - if err := r.Run(":8080"); err != nil { - log.Fatalf("Failed to start server: %v", err) - } -} \ No newline at end of file diff --git a/examples/Go/WebApp/FeatureFlag/templates/404.html b/examples/Go/WebApp/templates/404.html similarity index 100% rename from examples/Go/WebApp/FeatureFlag/templates/404.html rename to examples/Go/WebApp/templates/404.html diff --git a/examples/Go/WebApp/FeatureFlag/templates/beta.html b/examples/Go/WebApp/templates/beta.html similarity index 100% rename from examples/Go/WebApp/FeatureFlag/templates/beta.html rename to examples/Go/WebApp/templates/beta.html diff --git a/examples/Go/WebApp/FeatureFlag/templates/index.html b/examples/Go/WebApp/templates/index.html similarity index 100% rename from examples/Go/WebApp/FeatureFlag/templates/index.html rename to examples/Go/WebApp/templates/index.html diff --git a/examples/README.md b/examples/README.md index 7abaf3f4..aa2862a5 100644 --- a/examples/README.md +++ b/examples/README.md @@ -80,9 +80,9 @@ This example showcases a Go console application that retrieves chat responses fr ### [Console app](./Go/ConsoleApp/) -These examples demonstrate how to use the basic features of Azure App Configuration Go Provider in console applications. +This example demonstrates how to enable dynamic configuration from App Configuration in a Go console application. ### [Gin web app](./Go/WebApp/) -These examples demonstrate how to use Azure App Configuration Go Provider and feature flags in web applications built with the Gin framework. +This example demonstrates how to enable dynamic configuration and use feature flags from App Configuration in a web application built with the Gin framework. From 77ef7da352d4fd398e506d7a5254ce7761a48294 Mon Sep 17 00:00:00 2001 From: "Lingling Ye (from Dev Box)" Date: Fri, 10 Oct 2025 13:33:14 +0800 Subject: [PATCH 5/5] updated --- examples/Go/ConsoleApp/README.md | 2 +- examples/Go/ConsoleApp/main.go | 22 +++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/examples/Go/ConsoleApp/README.md b/examples/Go/ConsoleApp/README.md index 170198e3..0283fe2a 100644 --- a/examples/Go/ConsoleApp/README.md +++ b/examples/Go/ConsoleApp/README.md @@ -1,4 +1,4 @@ -# Azure App Configuration Console Refresh Example +# Azure App Configuration Console App Example This example demonstrates how to use the refresh functionality of Azure App Configuration in a console/command-line application. diff --git a/examples/Go/ConsoleApp/main.go b/examples/Go/ConsoleApp/main.go index 470cb230..35f99f47 100644 --- a/examples/Go/ConsoleApp/main.go +++ b/examples/Go/ConsoleApp/main.go @@ -23,19 +23,15 @@ type Font struct { } func main() { - fmt.Println("Azure App Configuration - Console Refresh Example") - fmt.Println("----------------------------------------") - - // Load configuration - fmt.Println("Loading configuration from Azure App Configuration...") - appCfgProvider, err := initializeAppConfiguration() + // Load configuration from Azure App Configuration + configProvider, err := loadAzureAppConfiguration() if err != nil { log.Fatalf("Error loading configuration: %s", err) } // Parse initial configuration into struct var config Config - err = appCfgProvider.Unmarshal(&config, nil) + err = configProvider.Unmarshal(&config, nil) if err != nil { log.Fatalf("Error unmarshalling configuration: %s", err) } @@ -44,12 +40,12 @@ func main() { displayConfig(config) // Register refresh callback to update and display the configuration - appCfgProvider.OnRefreshSuccess(func() { - fmt.Println("\n🔄 Configuration changed! Updating values...") + configProvider.OnRefreshSuccess(func() { + fmt.Println("\n Configuration changed! Updating values...") // Re-unmarshal the configuration var updatedConfig Config - err := appCfgProvider.Unmarshal(&updatedConfig, nil) + err := configProvider.Unmarshal(&updatedConfig, nil) if err != nil { log.Printf("Error unmarshalling updated configuration: %s", err) return @@ -83,7 +79,7 @@ func main() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - if err := appCfgProvider.Refresh(ctx); err != nil { + if err := configProvider.Refresh(ctx); err != nil { log.Printf("Error refreshing configuration: %s", err) } }() @@ -94,8 +90,8 @@ func main() { } } -// initializeAppConfiguration handles loading the configuration from Azure App Configuration -func initializeAppConfiguration() (*azureappconfiguration.AzureAppConfiguration, error) { +// loadAzureAppConfiguration loads the configuration from Azure App Configuration +func loadAzureAppConfiguration() (*azureappconfiguration.AzureAppConfiguration, error) { // Get connection string from environment variable connectionString := os.Getenv("AZURE_APPCONFIG_CONNECTION_STRING")