public ClientInfo Parse()

in edge-hub/core/src/Microsoft.Azure.Devices.Edge.Hub.Mqtt/MqttUsernameParser.cs [17:126]


        public ClientInfo Parse(string username)
        {
            // Username is of one of the 2 forms:
            //   username   = edgeHubHostName "/" deviceId [ "/" moduleId ] "/?" properties
            // Note, the ? should be the first character of the last segment (as it is a valid character for a deviceId/moduleId)
            //    OR
            //   username   = edgeHubHostName "/" deviceId [ "/" moduleId ] "/" properties
            //   properties = property *("&" property)
            //   property   = name "=" value
            // We recognize three property names:
            //   "api-version" [mandatory]
            //   "DeviceClientType" [optional]
            //   "model-id" [optional]
            // We ignore any properties we don't recognize.
            // Note - this logic does not check the query parameters for special characters, and '?' is treated as a valid value
            // and not used as a separator, unless it is the first character of the last segment
            // (since the property bag is not url encoded). So the following are valid username inputs -
            // "iotHub1/device1/module1/foo?bar=b1&api-version=2010-01-01&DeviceClientType=customDeviceClient1"
            // "iotHub1/device1?&api-version=2010-01-01&DeviceClientType=customDeviceClient1"
            // "iotHub1/device1/module1?&api-version=2010-01-01&DeviceClientType=customDeviceClient1"
            string deviceId;
            string moduleId = string.Empty;
            IDictionary<string, string> queryParameters;

            string[] usernameSegments = Preconditions.CheckNonWhiteSpace(username, nameof(username)).Split('/');
            if (usernameSegments[usernameSegments.Length - 1].StartsWith("?", StringComparison.OrdinalIgnoreCase))
            {
                // edgeHubHostName/device1/?apiVersion=10-2-3&DeviceClientType=foo
                if (usernameSegments.Length == 3)
                {
                    deviceId = usernameSegments[1].Trim();
                    queryParameters = ParseDeviceClientType(usernameSegments[2].Substring(1).Trim());
                }
                else if (usernameSegments.Length == 4)
                {
                    // edgeHubHostName/device1/module1/?apiVersion=10-2-3&DeviceClientType=foo
                    deviceId = usernameSegments[1].Trim();
                    moduleId = usernameSegments[2].Trim();
                    queryParameters = ParseDeviceClientType(usernameSegments[3].Substring(1).Trim());
                }
                else
                {
                    throw new EdgeHubConnectionException($"Username {username} does not contain valid values");
                }
            }
            else
            {
                // edgeHubHostName/device1/apiVersion=10-2-3&DeviceClientType=foo
                if (usernameSegments.Length == 3 && usernameSegments[2].Contains("api-version="))
                {
                    deviceId = usernameSegments[1].Trim();
                    queryParameters = ParseDeviceClientType(usernameSegments[2].Trim());
                }
                else if (usernameSegments.Length == 4 && usernameSegments[3].Contains("api-version="))
                {
                    // edgeHubHostName/device1/module1/apiVersion=10-2-3&DeviceClientType=foo
                    deviceId = usernameSegments[1].Trim();
                    moduleId = usernameSegments[2].Trim();
                    queryParameters = ParseDeviceClientType(usernameSegments[3].Trim());
                }
                else if (usernameSegments.Length == 6 && username.EndsWith("/api-version=2017-06-30/DeviceClientType=Microsoft.Azure.Devices.Client/1.5.1-preview-003", StringComparison.OrdinalIgnoreCase))
                {
                    // The Azure ML container is using an older client that returns a device client with the following format -
                    // username = edgeHubHostName/deviceId/moduleId/api-version=2017-06-30/DeviceClientType=Microsoft.Azure.Devices.Client/1.5.1-preview-003
                    // Notice how the DeviceClientType parameter is separated by a '/' instead of a '&', giving a usernameSegments.Length of 6 instead of the expected 4
                    // To allow those clients to work, check for that specific api-version, and version.
                    deviceId = usernameSegments[1].Trim();
                    moduleId = usernameSegments[2].Trim();
                    queryParameters = new Dictionary<string, string>
                    {
                        [ApiVersionKey] = "2017-06-30",
                        [DeviceClientTypeKey] = "Microsoft.Azure.Devices.Client/1.5.1-preview-003"
                    };
                }
                else
                {
                    throw new EdgeHubConnectionException($"Username {username} does not contain valid values");
                }
            }

            // Check if the api-version parameter exists, but don't check its value.
            if (!queryParameters.TryGetValue(ApiVersionKey, out string apiVersionKey) || string.IsNullOrWhiteSpace(apiVersionKey))
            {
                throw new EdgeHubConnectionException($"Username {username} does not contain a valid Api-version property");
            }

            if (string.IsNullOrWhiteSpace(deviceId))
            {
                throw new EdgeHubConnectionException($"Username {username} does not contain a valid device ID");
            }

            if (!queryParameters.TryGetValue(DeviceClientTypeKey, out string deviceClientType))
            {
                deviceClientType = string.Empty;
            }

            Option<string> modelIdOption = Option.None<string>();
            if (queryParameters.TryGetValue(ModelIdKey, out string modelId) && !string.IsNullOrWhiteSpace(modelId))
            {
                modelIdOption = Option.Some(modelId);
            }

            Option<string> authChainOption = Option.None<string>();
            if (queryParameters.TryGetValue(AuthChain, out string authChain) && !string.IsNullOrWhiteSpace(authChain))
            {
                authChainOption = Option.Some(authChain);
            }

            return new ClientInfo(deviceId, moduleId, deviceClientType, modelIdOption, authChainOption);
        }