private static async Task CreateConnectedAppAsync(
            SalesforceConnectedServiceInstance salesforceInstance,
            MetadataService metadataService,
            ConnectedServiceLogger logger,
            Project project)
        {
            salesforceInstance.ConnectedAppName = ConnectedAppHelper.GetUniqueConnectedAppName(metadataService, project);
            ConnectedApp connectedApp = ConnectedAppHelper.ConstructConnectedApp(salesforceInstance, project);
            SaveResult[] saveResults = metadataService.createMetadata(new Metadata[] { connectedApp });

            if (ConnectedAppHelper.DoSaveResultsIndicateDuplicateValue(saveResults))
            {
                // The connected app failed to be created because one already exists with the specified name.  This implies that the 
                // attempt to generate a unique name by reading the existing connected apps failed.  It is unknown at this point what 
                // causes the Salesforce server to sometimes respond to a SOAP ReadMetadata request by returning nil for a Connected App
                // name that actually exists.  In this case, retry using a random number as the app name's suffix.

                Debug.Fail("A connected app named '{0}' already exists.  This implies that the Salesforce server responded to the SOAP ReadMetadata request to read this Connected App by returning nil for it even though it actually exists."
                    .FormatCurrentCulture(salesforceInstance.ConnectedAppName));

                string secondAttemptConnectedAppName = ConnectedAppHelper.GetUniqueConnectedAppName(metadataService, project, true);
                await logger.WriteMessageAsync(LoggerMessageCategory.Information, Resources.LogMessage_DuplicateConnectedAppName, salesforceInstance.ConnectedAppName, secondAttemptConnectedAppName);
                
                salesforceInstance.ConnectedAppName = secondAttemptConnectedAppName;
                connectedApp = ConnectedAppHelper.ConstructConnectedApp(salesforceInstance, project);
                saveResults = metadataService.createMetadata(new Metadata[] { connectedApp });
            }

            if (saveResults.Length != 1 || !saveResults[0].success)
            {
                string errorMessages = saveResults.SelectMany(r => r.errors)
                   .Select(e => e.message)
                   .Aggregate((w, n) => w + "\r\n" + n);

                throw new InvalidOperationException(Resources.ConnectedAppHelper_FailedToCreateConnectedApp.FormatCurrentCulture(errorMessages));
            }
            else
            {
                ConnectedApp readConnectedApp = ConnectedAppHelper.GetConnectedAppByName(connectedApp.fullName, metadataService);
                if (readConnectedApp != null)
                {
                    salesforceInstance.RuntimeAuthentication.ConsumerKey = readConnectedApp.oauthConfig.consumerKey;
                }
                else
                {
                    await logger.WriteMessageAsync(LoggerMessageCategory.Warning, Resources.LogMessage_FailedReadingConnectedApp);

                    salesforceInstance.RuntimeAuthentication.ConsumerKey = Constants.ConfigValue_RequiredDefault;
                }
            }
        }
        public static async Task CreateConnectedAppAsync(
            SalesforceConnectedServiceInstance salesforceInstance,
            ConnectedServiceLogger logger,
            Project project)
        {
            using (MetadataService metadataService = new MetadataService())
            {
                metadataService.Url = salesforceInstance.DesignTimeAuthentication.MetadataServiceUrl;
                metadataService.SessionHeaderValue = new SessionHeader();
                metadataService.SessionHeaderValue.sessionId = salesforceInstance.DesignTimeAuthentication.AccessToken;

                await AuthenticationHelper.ExecuteSalesforceRequestAsync<SoapHeaderException>(
                    salesforceInstance.DesignTimeAuthentication,
                    async () => await ConnectedAppHelper.CreateConnectedAppAsync(salesforceInstance, metadataService, logger, project),
                    (e) => e.Message.StartsWith("INVALID_SESSION_ID", StringComparison.OrdinalIgnoreCase),
                    () => metadataService.SessionHeaderValue.sessionId = salesforceInstance.DesignTimeAuthentication.AccessToken);
            }
        }
        private static string GetUniqueConnectedAppName(MetadataService metadataService, Project project, bool useRandomSuffix = false)
        {
            string validAppName = ConnectedAppHelper.MakeValidConnectedAppName(project.Name);

            string appNameSuffix;
            if (useRandomSuffix)
            {
                // The default Random constructor uses a seed value derived from the system clock and has finite resolution.  However, 
                // because this code path is only hit once per running of the handler, this Random object having the same seed in subsequent 
                // calls (and thus undesirably generating the same number) is not a concern.
                appNameSuffix = new Random().Next(100, 1000).ToString();
            }
            else
            {
                appNameSuffix = GeneralUtilities.GetUniqueSuffix(suffix =>
                    ConnectedAppHelper.GetConnectedAppByName(validAppName + suffix, metadataService) != null);
            }

            return validAppName + appNameSuffix;
        }
        private static ConnectedApp GetConnectedAppByName(string connectedAppName, MetadataService metadataService)
        {
            Metadata[] metadata = metadataService.readMetadata(Constants.Metadata_ConnectedAppType, new string[] { connectedAppName });

            return metadata.Length == 1 ? metadata[0] as ConnectedApp : null;
        }