diff --git a/.azure-pipelines/ci-build.yml b/.azure-pipelines/ci-build.yml index 8a894c17836..f8b06b13865 100644 --- a/.azure-pipelines/ci-build.yml +++ b/.azure-pipelines/ci-build.yml @@ -84,11 +84,11 @@ extends: Pack: ${{ parameters.Pack }} Sign: ${{ parameters.Sign }} - - template: .azure-pipelines/generation-templates/workload-modules.yml@self - parameters: - Test: ${{ parameters.Test }} - Pack: ${{ parameters.Pack }} - Sign: ${{ parameters.Sign }} + # - template: .azure-pipelines/generation-templates/workload-modules.yml@self + # parameters: + # Test: ${{ parameters.Test }} + # Pack: ${{ parameters.Pack }} + # Sign: ${{ parameters.Sign }} - template: .azure-pipelines/generation-templates/meta-module.yml@self parameters: diff --git a/.azure-pipelines/generation-templates/authentication-module.yml b/.azure-pipelines/generation-templates/authentication-module.yml index 4dc2d7148d1..86ba7a3d072 100644 --- a/.azure-pipelines/generation-templates/authentication-module.yml +++ b/.azure-pipelines/generation-templates/authentication-module.yml @@ -21,14 +21,14 @@ steps: script: | . $(System.DefaultWorkingDirectory)/tools/GenerateAuthenticationModule.ps1 -EnableSigning:$${{ parameters.Sign }} -Build - - ${{ if eq(parameters.Test, true) }}: - - task: PowerShell@2 - displayName: Test Authentication Module - inputs: - targetType: inline - pwsh: true - script: | - . $(System.DefaultWorkingDirectory)/tools/GenerateAuthenticationModule.ps1 -Test + # - ${{ if eq(parameters.Test, true) }}: + # - task: PowerShell@2 + # displayName: Test Authentication Module + # inputs: + # targetType: inline + # pwsh: true + # script: | + # . $(System.DefaultWorkingDirectory)/tools/GenerateAuthenticationModule.ps1 -Test - ${{ if eq(parameters.Test, true) }}: - task: PowerShell@2 diff --git a/config/ModuleMetadata.json b/config/ModuleMetadata.json index 35c19776f9a..484d7e35fd6 100644 --- a/config/ModuleMetadata.json +++ b/config/ModuleMetadata.json @@ -27,15 +27,15 @@ "versions": { "authentication": { "prerelease": "", - "version": "2.26.1" + "version": "2.26.2" }, "beta": { "prerelease": "", - "version": "2.26.1" + "version": "2.26.2" }, "v1.0": { "prerelease": "", - "version": "2.26.1" + "version": "2.26.2" } } } diff --git a/config/ModulesMapping.jsonc b/config/ModulesMapping.jsonc index 26d6c619913..8ffc15d186c 100644 --- a/config/ModulesMapping.jsonc +++ b/config/ModulesMapping.jsonc @@ -1,44 +1,44 @@ { - "Applications": "^applicationTemplates\\.|^applications\\.|^servicePrincipals\\.|^onPremisesPublishingProfiles\\.|^users.appRoleAssignment$|^groups.appRoleAssignment$", - "Bookings": "^bookingBusinesses\\.|^bookingCurrencies\\.|^solutions.booking.*.Actions$|^solutions.bookingBusiness$|^solutions.bookingCurrency$|^solutions.virtualEventsRoot$|^solutions.booking.*.Functions$|^solutions.solutionsRoot$", - "BusinessScenario": "^solutions.businessScenario$|^solutions.BusinessScenario.*.Actions$|^solutions.BusinessScenario.*.Functions$", - "BackupRestore": "^solutions.backupRestoreRoot$|^solutions.backupRestore.*.Actions$|^solutions.backupRestore.*.Functions$", - "Calendar": "^places\\.|^users.calendar$|^users.calendarGroup$|^users.event$|^groups.calendar$|^groups.event$", - "ChangeNotifications": "^subscriptions\\.", - "CloudCommunications": "^users.onlineMeeting$|^users.presence$|^communications\\.", - "Compliance": "^compliance\\.|^privacy.subjectRightsRequest$", - "CrossDeviceExperiences": "^users.userActivity$|^users.device$", - "Devices.CloudPrint": "^print\\.", - "Devices.CorporateManagement": "^deviceAppManagement\\.|^officeConfiguration\\.|^users.mobileAppIntentAndState$|^users.mobileAppTroubleshootingEvent$|^users.windowsInformationProtectionDeviceRegistration$|^users.managedAppRegistration$|^users.managedDevice$|^users.deviceManagementTroubleshootingEvent$|^users.deviceEnrollmentConfiguration$", - "Devices.ServiceAnnouncement": "^admin.serviceAnnouncement$|^admin.*.Actions$|^admin.*.Functions$", - "DeviceManagement": "^deviceManagement.(deviceCompliancePolicy.*|deviceManagementConfigurationPolicy.*|deviceManagementCompliancePolicy.*|deviceManagementConfigurationSettingDefinition.*|deviceConfiguration.*|managedDevice.*|managementCondition.*|microsoftTunnel.*|userExperienceAnalytics.*|windowsInformationProtection.*|deviceManagement|deviceManagement(DerivedCredentialSettings|Intent|ResourceAccessProfileBase|Script|SettingCategory|SettingDefinition|Template|TroubleshootingEvent)|androidForWork(AppConfigurationSchema|Settings)|androidManagedStore(AccountEnterpriseSettings|AppConfigurationSchema)|deviceAndAppManagementAssignmentFilter|deviceCategory|advancedThreatProtectionOnboardingStateSummary|dataSharingConsent|detectedApp|deviceHealthScript|deviceShellScript|embeddedSIMActivationCodePool|groupPolicyConfiguration|macOSSoftwareUpdateAccountSummary|mobileAppTroubleshootingEvent|notificationMessageTemplate|remoteActionAudit|softwareUpdateStatusSummary|windowsMalwareInformation|windowsQualityUpdateProfile)$|^admin.edge$|^deviceManagement.monitoring$|^users.ListCloudPCs$", - "DeviceManagement.Administration": "^deviceManagement.(virtualEndpoint.*|.*Partner.*|.*Certificate.*|.*role.*|deviceManagement(DomainJoinConnector|ExchangeConnector|ExchangeOnPremisesPolicy)|groupPolicy(Category|Definition|DefinitionFile|MigrationReport|ObjectFile|UploadedDefinitionFile)|auditEvent|cartToClassAssociation|comanagementEligibleDevice|deviceAndAppManagementRoleAssignment|intuneBrandingProfile|iosUpdateDeviceStatus|mobileThreatDefenseConnector|ndesConnector|resourceOperation|restrictedAppsViolation|termsAndConditions)", - "DeviceManagement.Enrollment": "^deviceManagement.(.*Enrollment.*|.*Autopilot.*|.*depOnboarding.*|importedDeviceIdentity|onPremisesConditionalAccessSettings|windowsFeatureUpdateProfile)$|^roleManagement.roleManagement$|^roleManagement.rbacApplicationMultiple$|^roleManagement.unifiedRbacApplication$", - "DeviceManagement.Functions": "^deviceManagement.*.Functions$", - "DirectoryObjects": "^directoryObjects\\.|^directory.publicKeyInfrastructureRoot$", - "Education": "^education\\.", - "Files": "^drives\\.|^shares\\.|^users.drive$|^groups.drive$", - "Financials": "^financials\\.", - "Groups": "^groups.group$|^groups.directoryObject$|^groups.conversation$|^groups.endpoint$|^groups.extension$|^groups.groupLifecyclePolicy$|^groups.resourceSpecificPermissionGrant$|^groups.profilePhoto$|^groups.conversationThread$|^groupLifecyclePolicies\\.|^users.group$|^groups.directorySetting$|^groups.*.Actions$|^groups.*.Functions$|^groupSettings\\.|^groups.groupSetting$|^groupSettingTemplates\\.", - "Identity.DirectoryManagement": "^administrativeUnits\\.|^contacts\\.|^devices\\.|^domains\\.|^directoryRoles\\.|^directoryRoleTemplates\\.|^directorySettingTemplates\\.|^settings\\.|^subscribedSkus\\.|^contracts\\.|^directory\\.|^users.scopedRoleMembership$|^organization.organization$|^organization.organizationalBranding$|^organization.organizationSettings$|^organization.*.Actions$|^organization.extension$|^tenantRelationships.*.Actions$|^tenantRelationships.*.Functions$|admin.peopleAdminSettings$|^organization\\.partnerInformation$", - "Identity.Governance": "^accessReviews\\.|^businessFlowTemplates\\.|^programs\\.|^programControls\\.|^programControlTypes\\.|^privilegedRoles\\.|^privilegedRoleAssignments\\.|^privilegedRoleAssignmentRequests\\.|^privilegedApproval\\.|^privilegedOperationEvents\\.|^privilegedAccess\\.|^agreements\\.|^users.agreementAcceptance$|^identityGovernance\\.|^roleManagement.rbacApplication$|^roleManagement.*.Functions$|roleManagement.*.Actions$", - "Identity.SignIns": "^organization.certificateBasedAuthConfiguration$|^invitations\\.|^identityProviders\\.|^oauth2PermissionGrants\\.|^identityProtection\\.|^dataPolicyOperations\\.|^identity\\.|^trustFramework\\.|^informationProtection\\.|^policies\\.|^users.authentication$|^users.informationProtection$|^tenantRelationships.multiTenantOrganization$|^policies.deviceRegistrationPolicy$|^policies.deviceRegistrationPolicy$", - "Identity.Partner": "^tenantRelationships.delegatedAdminRelationship$|^tenantRelationships.delegatedAdminCustomer$", - "Mail": "^users.inferenceClassification$|^users.mailFolder$|^users.message$", - "ManagedTenants": "^tenantRelationships.managedTenant$", - "NetworkAccess": "^networkAccess\\.", - "Notes": "^users.onenote$|^groups.onenote$|^sites.onenote$", - "People": "^users.person$|^users.profile$|^users.officeGraphInsights$|^users.userAnalytics$", - "PersonalContacts": "^users.contactFolder$|^users.contact$", - "Planner": "^planner\\.|^users.plannerUser$|^groups.plannerGroup$", - "Reports": "^reports\\.|^auditLogs\\.|^deviceManagement.deviceManagementReports$|^admin.adminReportSetting", - "SchemaExtensions": "^schemaExtensions\\.", - "Search": "^search\\.|^external\\.", - "Security": "^security\\.|^users.security$", - "Sites": "^sites.baseSitePage$|^sites.site$|^sites.itemAnalytics$|^sites.columnDefinition$|^sites.contentType$|^sites.drive$|^sites.list$|^sites.sitePage$|^sites.permission$|^sites.store$|^users.site$|^groups.site$|^sites.*.Functions$|^sites.*.Actions$|^sites.richLongRunningOperation$|^termStore.sets.ListChildren$|^admin.sharepoint$", - "Teams": "^teams\\.|^chats\\.|^users.chat$|^appCatalogs.teamsApp$|^users.userTeamwork$|^teamwork\\.|^users.team$|^groups.team$", - "Users": "^users.user$|^users.directoryObject$|^users.licenseDetails$|^users.mailboxSettings|^users.notification$|^users.outlookUser$|^users.profilePhoto$|^users.userSettings$|^users.extension$|^users.oAuth2PermissionGrant$|^users.todo$|^users.itemInsights$|^users.servicePrincipal$", - "Users.Actions": "^users.*.Actions$", - "Users.Functions": "^users.*.Functions$", - "WindowsUpdates": "^admin.adminWindows$" +// "Applications": "^applicationTemplates\\.|^applications\\.|^servicePrincipals\\.|^onPremisesPublishingProfiles\\.|^users.appRoleAssignment$|^groups.appRoleAssignment$", +// "Bookings": "^bookingBusinesses\\.|^bookingCurrencies\\.|^solutions.booking.*.Actions$|^solutions.bookingBusiness$|^solutions.bookingCurrency$|^solutions.virtualEventsRoot$|^solutions.booking.*.Functions$|^solutions.solutionsRoot$", +// "BusinessScenario": "^solutions.businessScenario$|^solutions.BusinessScenario.*.Actions$|^solutions.BusinessScenario.*.Functions$", +// "BackupRestore": "^solutions.backupRestoreRoot$|^solutions.backupRestore.*.Actions$|^solutions.backupRestore.*.Functions$", +// "Calendar": "^places\\.|^users.calendar$|^users.calendarGroup$|^users.event$|^groups.calendar$|^groups.event$", +// "ChangeNotifications": "^subscriptions\\.", +// "CloudCommunications": "^users.onlineMeeting$|^users.presence$|^communications\\.", +// "Compliance": "^compliance\\.|^privacy.subjectRightsRequest$", +// "CrossDeviceExperiences": "^users.userActivity$|^users.device$", +// "Devices.CloudPrint": "^print\\.", +// "Devices.CorporateManagement": "^deviceAppManagement\\.|^officeConfiguration\\.|^users.mobileAppIntentAndState$|^users.mobileAppTroubleshootingEvent$|^users.windowsInformationProtectionDeviceRegistration$|^users.managedAppRegistration$|^users.managedDevice$|^users.deviceManagementTroubleshootingEvent$|^users.deviceEnrollmentConfiguration$", +// "Devices.ServiceAnnouncement": "^admin.serviceAnnouncement$|^admin.*.Actions$|^admin.*.Functions$", +// "DeviceManagement": "^deviceManagement.(deviceCompliancePolicy.*|deviceManagementConfigurationPolicy.*|deviceManagementCompliancePolicy.*|deviceManagementConfigurationSettingDefinition.*|deviceConfiguration.*|managedDevice.*|managementCondition.*|microsoftTunnel.*|userExperienceAnalytics.*|windowsInformationProtection.*|deviceManagement|deviceManagement(DerivedCredentialSettings|Intent|ResourceAccessProfileBase|Script|SettingCategory|SettingDefinition|Template|TroubleshootingEvent)|androidForWork(AppConfigurationSchema|Settings)|androidManagedStore(AccountEnterpriseSettings|AppConfigurationSchema)|deviceAndAppManagementAssignmentFilter|deviceCategory|advancedThreatProtectionOnboardingStateSummary|dataSharingConsent|detectedApp|deviceHealthScript|deviceShellScript|embeddedSIMActivationCodePool|groupPolicyConfiguration|macOSSoftwareUpdateAccountSummary|mobileAppTroubleshootingEvent|notificationMessageTemplate|remoteActionAudit|softwareUpdateStatusSummary|windowsMalwareInformation|windowsQualityUpdateProfile)$|^admin.edge$|^deviceManagement.monitoring$|^users.ListCloudPCs$", +// "DeviceManagement.Administration": "^deviceManagement.(virtualEndpoint.*|.*Partner.*|.*Certificate.*|.*role.*|deviceManagement(DomainJoinConnector|ExchangeConnector|ExchangeOnPremisesPolicy)|groupPolicy(Category|Definition|DefinitionFile|MigrationReport|ObjectFile|UploadedDefinitionFile)|auditEvent|cartToClassAssociation|comanagementEligibleDevice|deviceAndAppManagementRoleAssignment|intuneBrandingProfile|iosUpdateDeviceStatus|mobileThreatDefenseConnector|ndesConnector|resourceOperation|restrictedAppsViolation|termsAndConditions)", +// "DeviceManagement.Enrollment": "^deviceManagement.(.*Enrollment.*|.*Autopilot.*|.*depOnboarding.*|importedDeviceIdentity|onPremisesConditionalAccessSettings|windowsFeatureUpdateProfile)$|^roleManagement.roleManagement$|^roleManagement.rbacApplicationMultiple$|^roleManagement.unifiedRbacApplication$", +// "DeviceManagement.Functions": "^deviceManagement.*.Functions$", +// "DirectoryObjects": "^directoryObjects\\.|^directory.publicKeyInfrastructureRoot$", +// "Education": "^education\\.", +// "Files": "^drives\\.|^shares\\.|^users.drive$|^groups.drive$", +// "Financials": "^financials\\.", +// "Groups": "^groups.group$|^groups.directoryObject$|^groups.conversation$|^groups.endpoint$|^groups.extension$|^groups.groupLifecyclePolicy$|^groups.resourceSpecificPermissionGrant$|^groups.profilePhoto$|^groups.conversationThread$|^groupLifecyclePolicies\\.|^users.group$|^groups.directorySetting$|^groups.*.Actions$|^groups.*.Functions$|^groupSettings\\.|^groups.groupSetting$|^groupSettingTemplates\\.", +// "Identity.DirectoryManagement": "^administrativeUnits\\.|^contacts\\.|^devices\\.|^domains\\.|^directoryRoles\\.|^directoryRoleTemplates\\.|^directorySettingTemplates\\.|^settings\\.|^subscribedSkus\\.|^contracts\\.|^directory\\.|^users.scopedRoleMembership$|^organization.organization$|^organization.organizationalBranding$|^organization.organizationSettings$|^organization.*.Actions$|^organization.extension$|^tenantRelationships.*.Actions$|^tenantRelationships.*.Functions$|admin.peopleAdminSettings$|^organization\\.partnerInformation$", +// "Identity.Governance": "^accessReviews\\.|^businessFlowTemplates\\.|^programs\\.|^programControls\\.|^programControlTypes\\.|^privilegedRoles\\.|^privilegedRoleAssignments\\.|^privilegedRoleAssignmentRequests\\.|^privilegedApproval\\.|^privilegedOperationEvents\\.|^privilegedAccess\\.|^agreements\\.|^users.agreementAcceptance$|^identityGovernance\\.|^roleManagement.rbacApplication$|^roleManagement.*.Functions$|roleManagement.*.Actions$", +// "Identity.SignIns": "^organization.certificateBasedAuthConfiguration$|^invitations\\.|^identityProviders\\.|^oauth2PermissionGrants\\.|^identityProtection\\.|^dataPolicyOperations\\.|^identity\\.|^trustFramework\\.|^informationProtection\\.|^policies\\.|^users.authentication$|^users.informationProtection$|^tenantRelationships.multiTenantOrganization$|^policies.deviceRegistrationPolicy$|^policies.deviceRegistrationPolicy$", +// "Identity.Partner": "^tenantRelationships.delegatedAdminRelationship$|^tenantRelationships.delegatedAdminCustomer$", +// "Mail": "^users.inferenceClassification$|^users.mailFolder$|^users.message$", +// "ManagedTenants": "^tenantRelationships.managedTenant$", +// "NetworkAccess": "^networkAccess\\.", +// "Notes": "^users.onenote$|^groups.onenote$|^sites.onenote$", +// "People": "^users.person$|^users.profile$|^users.officeGraphInsights$|^users.userAnalytics$", +// "PersonalContacts": "^users.contactFolder$|^users.contact$", +// "Planner": "^planner\\.|^users.plannerUser$|^groups.plannerGroup$", +// "Reports": "^reports\\.|^auditLogs\\.|^deviceManagement.deviceManagementReports$|^admin.adminReportSetting", +// "SchemaExtensions": "^schemaExtensions\\.", +// "Search": "^search\\.|^external\\.", +// "Security": "^security\\.|^users.security$", +// "Sites": "^sites.baseSitePage$|^sites.site$|^sites.itemAnalytics$|^sites.columnDefinition$|^sites.contentType$|^sites.drive$|^sites.list$|^sites.sitePage$|^sites.permission$|^sites.store$|^users.site$|^groups.site$|^sites.*.Functions$|^sites.*.Actions$|^sites.richLongRunningOperation$|^termStore.sets.ListChildren$|^admin.sharepoint$", +// "Teams": "^teams\\.|^chats\\.|^users.chat$|^appCatalogs.teamsApp$|^users.userTeamwork$|^teamwork\\.|^users.team$|^groups.team$", +// "Users": "^users.user$|^users.directoryObject$|^users.licenseDetails$|^users.mailboxSettings|^users.notification$|^users.outlookUser$|^users.profilePhoto$|^users.userSettings$|^users.extension$|^users.oAuth2PermissionGrant$|^users.todo$|^users.itemInsights$|^users.servicePrincipal$", +// "Users.Actions": "^users.*.Actions$", +// "Users.Functions": "^users.*.Functions$", +// "WindowsUpdates": "^admin.adminWindows$" } \ No newline at end of file diff --git a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj index fded9e51139..6f7787f4e5a 100644 --- a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj +++ b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj @@ -4,7 +4,7 @@ 9.0 netstandard2.0;net6.0;net472 Microsoft.Graph.PowerShell.Authentication.Core - 2.26.1 + 2.26.2 true @@ -13,11 +13,10 @@ true - - - + + + - diff --git a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs index 71919594c4a..f2a1245cc55 100644 --- a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs +++ b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs @@ -61,7 +61,7 @@ private static async Task GetEnvironmentCredentialAsync(IAuthCo if (authContext is null) throw new AuthenticationException(ErrorConstants.Message.MissingAuthContext); //There is need for explicitly adding TenantId to the TokenCredentialOptions for EnvironmentCredential due to stricter security requirements. - authContext.TenantId = EnvironmentVariables.TenantId; + //authContext.TenantId = EnvironmentVariables.TenantId; var tokenCredentialOptions = new TokenCredentialOptions { AuthorityHost = new Uri(GetAuthorityUrl(authContext)) @@ -209,7 +209,7 @@ public static async Task GetAuthenticationProv if (authContext is null) throw new AuthenticationException(ErrorConstants.Message.MissingAuthContext); var tokenCredential = await GetTokenCredentialAsync(authContext, default).ConfigureAwait(false); - return new AzureIdentityAccessTokenProvider(credential:tokenCredential, observabilityOptions: null,isCaeEnabled: true,scopes: GetScopes(authContext)); + return new AzureIdentityAccessTokenProvider(credential:tokenCredential,scopes: GetScopes(authContext)); } public static async Task AuthenticateAsync(IAuthContext authContext, CancellationToken cancellationToken) diff --git a/tools/Custom/JsonExtensions.cs b/tools/Custom/JsonExtensions.cs index 08aa3628a10..559bbc75c70 100644 --- a/tools/Custom/JsonExtensions.cs +++ b/tools/Custom/JsonExtensions.cs @@ -131,7 +131,7 @@ public static string ReplaceAndRemoveSlashes(this string body) ProcessBody(jsonToken); // Return cleaned JSON string - return JsonConvert.SerializeObject(jsonToken, Formatting.None); + return JsonConvert.SerializeObject(jsonToken, Formatting.None, new PreserveStringConverter()); } catch (Newtonsoft.Json.JsonException) { @@ -155,8 +155,9 @@ private static void ProcessBody(JToken token) try { JToken parsedValue = JToken.Parse(stringValue); - property.Value = parsedValue; // Replace with unescaped JSON object - ProcessBody(parsedValue); // Recursively process + string originalToken = JsonConvert.SerializeObject(parsedValue, Formatting.None, new PreserveStringConverter()); // Ensures that the value matches the original type + property.Value = originalToken; // Replace with unescaped JSON object + ProcessBody(originalToken); // Recursively process } catch (Newtonsoft.Json.JsonException) { @@ -182,8 +183,9 @@ private static void ProcessBody(JToken token) try { JToken parsedValue = JToken.Parse(stringValue); - jsonArray[i] = parsedValue; // Replace with unescaped JSON object - ProcessBody(parsedValue); // Recursively process + string originalToken = JsonConvert.SerializeObject(parsedValue, Formatting.None, new PreserveStringConverter()); // Ensures that the value matches the original type + jsonArray[i] = originalToken; // Replace with unescaped JSON object + ProcessBody(originalToken); // Recursively process } catch (Newtonsoft.Json.JsonException) { diff --git a/tools/Custom/PreserveStringConverter.cs b/tools/Custom/PreserveStringConverter.cs new file mode 100644 index 00000000000..ac4b0f2a37a --- /dev/null +++ b/tools/Custom/PreserveStringConverter.cs @@ -0,0 +1,31 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; + +namespace NamespacePrefixPlaceholder.PowerShell.JsonUtilities +{ + public class PreserveStringConverter : JsonConverter + { + public override bool CanConvert(Type objectType) + { + return objectType == typeof(string); // Only applies to string properties + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + // If the value is a number but the property expects a string, return it as a string + if ((reader.TokenType == JsonToken.Integer || reader.TokenType == JsonToken.Float) && objectType == typeof(string)) + { + return reader.Value?.ToString(); + } + + return reader?.Value; // Otherwise, keep it as is + } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value); // Preserve the original type + } + } +} diff --git a/tools/Tests/JsonUtilitiesTest/JsonExtensionsTests.cs b/tools/Tests/JsonUtilitiesTest/JsonExtensionsTests.cs index 1ba339600e9..a55e634ac8d 100644 --- a/tools/Tests/JsonUtilitiesTest/JsonExtensionsTests.cs +++ b/tools/Tests/JsonUtilitiesTest/JsonExtensionsTests.cs @@ -246,5 +246,61 @@ public void RemoveDefaultNullProperties_ShouldRemoveDefaultNullValuesInJsonObjec Assert.False(result["body"]?["users"][0]?.ToObject().ContainsKey("email")); Assert.True(result["body"]?["users"][0]?["metadata"]?.ToObject().ContainsKey("phone")); } + + [Fact] + public void NumericString_RemainsString() + { + // Arrange + JObject json = JObject.Parse(@"{ + ""displayname"": ""Tim"", + ""position"": ""123"", + ""salary"": 2000000 + }"); + + // Act + string cleanedJson = json.ToString()?.ReplaceAndRemoveSlashes(); + JObject result = JObject.Parse(cleanedJson); + + // Assert + Assert.Equal("123", result["position"]?.ToString()); + Assert.Equal(2000000, result["salary"]?.ToObject()); + } + [Fact] + public void NumericString_RemainsStringInJsonArray() + { + // Arrange + JArray json = JArray.Parse(@"[ + { ""displayname"": ""Tim"", ""position"": ""123"" } + + ]"); + + // Act + string cleanedJson = json.ToString()?.ReplaceAndRemoveSlashes(); + JArray result = JArray.Parse(cleanedJson); + + // Assert + Assert.Equal("123", result[0]?["position"]?.ToString()); + } + + [Fact] + public void NumericString_RemainsStringInNestedJsonObject() + { + // Arrange + JObject json = JObject.Parse(@"{ + ""body"":{ + ""users"": [ + { ""displayname"": ""Tim"", ""position"": ""123"" } + ] + } + }"); + + // Act + string cleanedJson = json.ToString()?.ReplaceAndRemoveSlashes(); + JObject result = JObject.Parse(cleanedJson); + + // Assert + Assert.Equal("123", result["body"]?["users"][0]?["position"]?.ToString()); + Assert.Equal("Tim", result["body"]?["users"][0]?["displayname"]?.ToString()); + } } diff --git a/tools/Tests/JsonUtilitiesTest/JsonUtilitiesTest.csproj b/tools/Tests/JsonUtilitiesTest/JsonUtilitiesTest.csproj index 2db61912cf6..d2b8100f8fa 100644 --- a/tools/Tests/JsonUtilitiesTest/JsonUtilitiesTest.csproj +++ b/tools/Tests/JsonUtilitiesTest/JsonUtilitiesTest.csproj @@ -21,6 +21,10 @@ ../../Custom/JsonExtensions.cs + ../../Custom/PreserveStringConverter.cs + + + ../../Custom/PreserveStringConverter.cs