public static void Main()

in src/BookingsSampleNativeConsole/Program.cs [20:191]


        public static void Main()
        {
            try
            {
                var config = GetConfiguration();
                tenantId = config["Bookings_TenantID"];
                if (string.IsNullOrWhiteSpace(tenantId))
                {
                    Console.WriteLine("Update sample to include your own Tenant ID");
                    return;
                }

                clientApplicationAppId = config["Bookings_ClientID"];
                if (string.IsNullOrWhiteSpace(clientApplicationAppId))
                {
                    Console.WriteLine("Update sample to include your own client application ID");
                    return;
                }

                var clientApplication = PublicClientApplicationBuilder.Create(clientApplicationAppId)
                .WithAuthority(AadAuthorityAudience.AzureAdMyOrg)
                .WithTenantId(tenantId)
                .Build();

                Console.Write("Username:    ");
                string username = Console.ReadLine();
                if (string.IsNullOrEmpty(username))
                {
                    Console.WriteLine("Update Sample to include your username");
                    return;
                }

                Console.Write("Password:    ");
                SecureString password = new SecureString();
                ConsoleKeyInfo keyinfo;
                do
                {
                    keyinfo = Console.ReadKey(true);
                    if (keyinfo.Key == ConsoleKey.Enter) break;
                    password.AppendChar(keyinfo.KeyChar);
                } while (keyinfo.Key != ConsoleKey.Enter);

                if (password.Length == 0)
                {
                    Console.WriteLine("Password needs to be provided for the sample to work");
                    return;
                }

                var authenticationResult = clientApplication.AcquireTokenByUsernamePassword(
                                    new[] { "Bookings.Read.All" },
                                    username, password).ExecuteAsync().Result;
                var graphService = new GraphService(
                    GraphService.ServiceRoot,
                    () => authenticationResult.CreateAuthorizationHeader());

                // Fiddler makes it easy to look at the request/response payloads. Use it automatically if it is running.
                // https://www.telerik.com/download/fiddler
                if (System.Diagnostics.Process.GetProcessesByName("fiddler").Any())
                {
                    graphService.WebProxy = new WebProxy(new Uri("http://localhost:8888"), false);
                }

                // Get the list of booking businesses that the logged on user can see.
                // NOTE: I'm not using 'async' in this sample for simplicity;
                // the ODATA client library has full support for async invocations.
                var bookingBusinesses = graphService.BookingBusinesses.ToArray();
                foreach (var _ in bookingBusinesses)
                {
                    Console.WriteLine(_.DisplayName);
                }

                if (bookingBusinesses.Length == 0)
                {
                    Console.WriteLine("Enter a name for a new booking business, or leave empty to exit.");
                }
                else
                {
                    Console.WriteLine("Type the name of the booking business to use or enter a new name to create a new booking business, or leave empty to exit.");
                }

                var businessName = Console.ReadLine();
                if (string.IsNullOrWhiteSpace(businessName))
                {
                    return;
                }

                // See if the name matches one of the entities we have (this is searching the local array)
                var bookingBusiness = bookingBusinesses.FirstOrDefault(_ => _.DisplayName == businessName);
                if (bookingBusiness == null)
                {
                    // If we don't have a match, create a new bookingBusiness.
                    // All we need to pass is the display name, but we could pass other properties if needed.
                    // This NewEntityWithChangeTracking is a custom extension to the standard ODATA library to make it easy.
                    // Keep in mind there are other patterns that could be used, revolving around DataServiceCollection.
                    // The trick is that the data object must be tracked by a DataServiceCollection and then we need
                    // to save with SaveChangesOptions.PostOnlySetProperties.
                    bookingBusiness = graphService.BookingBusinesses.NewEntityWithChangeTracking();
                    bookingBusiness.DisplayName = businessName;
                    Console.WriteLine("Creating new booking business...");
                    graphService.SaveChanges(SaveChangesOptions.PostOnlySetProperties);

                    Console.WriteLine($"Booking Business Created: {bookingBusiness.Id}. Press any key to continue.");
                    Console.ReadKey();
                }
                else
                {
                    Console.WriteLine("Using existing booking business.");
                }

                // Play with the newly minted booking business
                var business = graphService.BookingBusinesses.ByKey(bookingBusiness.Id);

                // Add an external staff member (these are easy, as we don't need to find another user in the AD).
                // For an internal staff member, the application might query the user or the Graph to find other users.
                var staff = business.StaffMembers.FirstOrDefault();
                if (staff == null)
                {
                    staff = business.StaffMembers.NewEntityWithChangeTracking();
                    staff.EmailAddress = "staff1@contoso.com";
                    staff.DisplayName = "Staff1";
                    staff.Role = BookingStaffRole.ExternalGuest;
                    Console.WriteLine("Creating staff member...");
                    graphService.SaveChanges(SaveChangesOptions.PostOnlySetProperties);
                    Console.WriteLine("Staff created.");
                }
                else
                {
                    Console.WriteLine($"Using staff member {staff.DisplayName}");
                }

                // Add an Appointment
                var newAppointment = business.Appointments.NewEntityWithChangeTracking();
                newAppointment.CustomerEmailAddress = "customer@contoso.com";
                newAppointment.CustomerName = "John Doe";
                newAppointment.ServiceId = business.Services.First().Id; // assuming we didn't deleted all services; we might want to double check first like we did with staff.
                newAppointment.StaffMemberIds.Add(staff.Id);
                newAppointment.Reminders.Add(new BookingReminder { Message = "Hello", Offset = TimeSpan.FromHours(1), Recipients = BookingReminderRecipients.AllAttendees });
                var start = DateTime.Today.AddDays(1).AddHours(13).ToUniversalTime();
                var end = start.AddHours(1);
                newAppointment.Start = new DateTimeTimeZone { DateTime = start.ToString("o"), TimeZone = "UTC" };
                newAppointment.End = new DateTimeTimeZone { DateTime = end.ToString("o"), TimeZone = "UTC" };
                Console.WriteLine("Creating appointment...");
                graphService.SaveChanges(SaveChangesOptions.PostOnlySetProperties);
                Console.WriteLine("Appointment created.");

                // Query appointments.
                // Note: the server imposes a limit on the number of appointments returned in each request
                // so clients must use paging or request a calendar view with business.GetCalendarView().
                foreach (var appointment in business.Appointments.GetAllPages())
                {
                    // DateTimeTimeZone comes from Graph and it uses string for the DateTime, not sure why.
                    // Perhaps we could tweak the generated proxy (or add extension method) to automatically
                    // do this ToString/Parse for us, so it does not pollute the entire code.
                    Console.WriteLine($"{DateTime.Parse(appointment.Start.DateTime).ToLocalTime()}: {appointment.ServiceName} with {appointment.CustomerName}");
                }

                // In order for customers to interact with the booking business we need to publish its public page.
                // We can also Unpublish() to hide it from customers, but where is the fun in that?
                Console.WriteLine("Publishing booking business public page...");
                business.Publish().Execute();

                // Let the user play with the public page
                Console.WriteLine(business.GetValue().PublicUrl);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }

            Console.WriteLine("Done. Press any key to exit.");
            Console.ReadKey();
        }