diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 93914ae00..34cb92d08 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -11,6 +11,11 @@ dependencies { implementation 'jakarta.annotation:jakarta.annotation-api:2.1.1' + implementation 'io.jsonwebtoken:jjwt-api:0.12.6' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.6' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.6' + implementation 'com.auth0:jwks-rsa:0.22.1' + api 'com.squareup.okhttp3:okhttp:4.12.0' api 'com.azure:azure-core:1.54.1' diff --git a/spotBugsExcludeFilter.xml b/spotBugsExcludeFilter.xml index 619fb292d..07e0e4040 100644 --- a/spotBugsExcludeFilter.xml +++ b/spotBugsExcludeFilter.xml @@ -112,4 +112,8 @@ xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubu + + + + diff --git a/src/main/java/com/microsoft/graph/core/models/DecryptableContent.java b/src/main/java/com/microsoft/graph/core/models/DecryptableContent.java new file mode 100644 index 000000000..202387ed7 --- /dev/null +++ b/src/main/java/com/microsoft/graph/core/models/DecryptableContent.java @@ -0,0 +1,169 @@ +package com.microsoft.graph.core.models; + +import java.io.ByteArrayInputStream; +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.util.Arrays; +import java.util.Base64; +import java.util.Objects; + +import javax.crypto.Cipher; +import javax.crypto.Mac; + +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import com.microsoft.kiota.serialization.Parsable; +import com.microsoft.kiota.serialization.ParsableFactory; +import com.microsoft.kiota.serialization.ParseNode; +import com.microsoft.kiota.serialization.ParseNodeFactoryRegistry; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; + +/** + * DecryptableContent interface + */ +public interface DecryptableContent { + + /** + * Sets the data + * @param data resource data + */ + public void setData(@Nullable final String data); + /** + * Gets the data + * @return the data + */ + public @Nullable String getData(); + /** + * Sets the data key + * @param dataKey asymmetric key used to sign data + */ + public void setDataKey(@Nullable final String dataKey); + /** + * Gets the data key + * @return the data key + */ + public @Nullable String getDataKey(); + + /** + * Sets the data signature + * @param signature signature of the data + */ + public void setDataSignature(@Nullable final String signature); + /** + * Gets the data signature + * @return data signature + */ + public @Nullable String getDataSignature(); + /** + * Sets the encryption certificate id + * @param encryptionCertificateId certificate Id used when subscribing + */ + public void setEncryptionCertificateId(@Nullable final String encryptionCertificateId); + /** + * Gets the encryption certificate id + * @return the encryption certificate id + */ + public @Nullable String getEncryptionCertificateId(); + /** + * Sets the encryption certificate thumbprint + * @param encryptionCertificateThumbprint certificate thumbprint + */ + public void setEncryptionCertificateThumbprint(@Nullable final String encryptionCertificateThumbprint); + /** + * Gets the encryption certificate thumbprint + * @return the encryption certificate thumbprint + */ + public @Nullable String getEncryptionCertificateThumbprint(); + + /** + * Validates the signature of the resource data, decrypts resource data and deserializes the data to a Parsable + * https://learn.microsoft.com/en-us/graph/change-notifications-with-resource-data?tabs=csharp#decrypting-resource-data-from-change-notifications + * + * @param Parsable type to return + * @param decryptableContent instance of DecryptableContent + * @param certificateKeyProvider provides an RSA Private Key for the certificate provided when subscribing + * @param factory ParsableFactory for the return type + * @return decrypted resource data + * @throws Exception if an error occurs while decrypting the data + */ + public static @Nonnull T decrypt(@Nonnull final DecryptableContent decryptableContent, @Nonnull final CertificateKeyProvider certificateKeyProvider, @Nonnull final ParsableFactory factory) throws Exception { + Objects.requireNonNull(certificateKeyProvider); + final String decryptedContent = decryptAsString(decryptableContent, certificateKeyProvider); + final ParseNode rootParseNode = ParseNodeFactoryRegistry.defaultInstance.getParseNode( + "application/json", new ByteArrayInputStream(decryptedContent.getBytes(StandardCharsets.UTF_8))); + return rootParseNode.getObjectValue(factory); + } + + /** + * Validates the signature and decrypts resource data attached to the notification. + * https://learn.microsoft.com/en-us/graph/change-notifications-with-resource-data?tabs=csharp#decrypting-resource-data-from-change-notifications + * + * @param content instance of DecryptableContent + * @param certificateKeyProvider provides an RSA Private Key for the certificate provided when subscribing + * @return decrypted resource data + * @throws Exception if an error occurs while decrypting the data + */ + public static @Nonnull String decryptAsString(@Nonnull final DecryptableContent content, @Nonnull final CertificateKeyProvider certificateKeyProvider) throws Exception { + Objects.requireNonNull(content); + Objects.requireNonNull(certificateKeyProvider); + final Key privateKey = certificateKeyProvider.getCertificateKey(content.getEncryptionCertificateId(), content.getEncryptionCertificateThumbprint()); + final Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWITHSHA1ANDMGF1PADDING"); + cipher.init(Cipher.DECRYPT_MODE, privateKey); + final byte[] decryptedSymmetricKey = cipher.doFinal(Base64.getDecoder().decode(content.getDataKey())); + + final Mac sha256Mac = Mac.getInstance("HmacSHA256"); + sha256Mac.init(new SecretKeySpec(decryptedSymmetricKey, "HmacSHA256")); + final byte[] hashedData = sha256Mac.doFinal(Base64.getDecoder().decode(content.getData())); + + final String expectedSignature = Base64.getEncoder().encodeToString(hashedData); + if (!expectedSignature.equals(content.getDataSignature())) { + throw new Exception("Signature does not match"); + } + return new String(aesDecrypt(Base64.getDecoder().decode(content.getData()), decryptedSymmetricKey), StandardCharsets.UTF_8); + } + + /** + * Decrypts the resource data using the decrypted symmetric key + * @param data Base-64 decoded resource data + * @param key Decrypted symmetric key from DecryptableContent.getDataKey() + * @return decrypted resource data + * @throws Exception if an error occurs while decrypting the data + */ + public static @Nonnull byte[] aesDecrypt(@Nonnull final byte[] data, @Nonnull final byte[] key) throws Exception { + Objects.requireNonNull(data); + Objects.requireNonNull(key); + try { + @SuppressWarnings("java:S3329") + // Sonar warns that a random IV should be used for encryption + // but we are decrypting here. + final IvParameterSpec ivSpec = new IvParameterSpec(Arrays.copyOf(key, 16)); + @SuppressWarnings("java:S5542") + // Sonar warns that cncryption algorithms should be used with secure mode and padding scheme + // but ChangeNotifications implementation uses this algorithm for decryption. + // https://learn.microsoft.com/en-us/graph/change-notifications-with-resource-data?tabs=java#decrypting-resource-data + final Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); + cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), ivSpec); + return cipher.doFinal(data); + } catch (Exception ex) { + throw new RuntimeException("Unexpected error occurred while trying to decrypt the data", ex); + } + } + + /** + * Provides a private key for the certificate with the ID provided when creating the + * subscription and the thumbprint. + */ + @FunctionalInterface + public interface CertificateKeyProvider { + /** + * Returns the private key for an X.509 certificate with the given id and thumbprint + * @param certificateId certificate Id provided when subscribing + * @param certificateThumbprint certificate thumbprint + * @return Private key used to sign the certificate + */ + public @Nonnull Key getCertificateKey(@Nullable final String certificateId, @Nullable final String certificateThumbprint); + } +} diff --git a/src/main/java/com/microsoft/graph/core/models/DiscoverUrlAdapter.java b/src/main/java/com/microsoft/graph/core/models/DiscoverUrlAdapter.java new file mode 100644 index 000000000..3ccdd9c1b --- /dev/null +++ b/src/main/java/com/microsoft/graph/core/models/DiscoverUrlAdapter.java @@ -0,0 +1,60 @@ +package com.microsoft.graph.core.models; + +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.Key; +import java.util.Objects; + +import org.slf4j.LoggerFactory; + +import com.auth0.jwk.Jwk; +import com.auth0.jwk.JwkProvider; +import com.auth0.jwk.UrlJwkProvider; + +import io.jsonwebtoken.JweHeader; +import io.jsonwebtoken.JwsHeader; +import io.jsonwebtoken.LocatorAdapter; +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; + +/** + * DiscoverUrlAdapter class + */ +public class DiscoverUrlAdapter extends LocatorAdapter { + + /** + * Key store + */ + private final JwkProvider keyStore; + + /** + * Constructor + * @param keyDiscoveryUrl the JWKS endpoint to use to retrieve signing keys + * @throws URISyntaxException if uri is invalid + * @throws MalformedURLException if url is invalid + */ + public DiscoverUrlAdapter(@Nonnull final String keyDiscoveryUrl) + throws URISyntaxException, MalformedURLException { + this.keyStore = + new UrlJwkProvider(new URI(Objects.requireNonNull(keyDiscoveryUrl)).toURL()); + } + + @Override + protected @Nullable Key locate(@Nonnull JwsHeader header) { + Objects.requireNonNull(header); + try { + String keyId = header.getKeyId(); + Jwk publicKey = keyStore.get(keyId); + return publicKey.getPublicKey(); + } catch (final Exception e) { + throw new IllegalArgumentException("Could not locate key", e); + } + } + + @Override + protected @Nullable Key locate(@Nonnull JweHeader header) { + return null; + } + +} diff --git a/src/main/java/com/microsoft/graph/core/models/EncryptableSubscription.java b/src/main/java/com/microsoft/graph/core/models/EncryptableSubscription.java new file mode 100644 index 000000000..fab27feea --- /dev/null +++ b/src/main/java/com/microsoft/graph/core/models/EncryptableSubscription.java @@ -0,0 +1,42 @@ +package com.microsoft.graph.core.models; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.Base64; +import java.util.Objects; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; + +/** + * EncryptableSubscription interface + */ +public interface EncryptableSubscription { + + /** + * Sets the encryption certificate + * @param certificate Base-64 encoded certificate to be used by Microsoft Graph to encrypt resource data + */ + public void setEncryptionCertificate(@Nullable final String certificate); + + /** + * Returns the encryption certificate + * @return encryption certificate + */ + public @Nullable String getEncryptionCertificate(); + + /** + * Converts an X.509 Certificate object to Base-64 string and adds to the encryptableSubscription provided + * @param subscription encryptable subscription + * @param certificate X.509 Certificate + * @throws CertificateEncodingException if the certificate cannot be encoded + */ + public static void addPublicEncryptionCertificate(@Nonnull final EncryptableSubscription subscription, @Nonnull final X509Certificate certificate) throws CertificateEncodingException { + Objects.requireNonNull(subscription); + Objects.requireNonNull(certificate); + subscription.setEncryptionCertificate( + Base64.getEncoder().encodeToString(certificate.getEncoded()) + ); + } + +} diff --git a/src/main/java/com/microsoft/graph/core/models/EncryptedContentBearer.java b/src/main/java/com/microsoft/graph/core/models/EncryptedContentBearer.java new file mode 100644 index 000000000..fe86341a6 --- /dev/null +++ b/src/main/java/com/microsoft/graph/core/models/EncryptedContentBearer.java @@ -0,0 +1,21 @@ +package com.microsoft.graph.core.models; + +/** + * Contains Decryptable content + * @param The type of the decryptable content + */ +public interface EncryptedContentBearer { + + /** + * Sets encrypted content + * @param encryptedContent encrypted content + */ + public void setEncryptedContent(T encryptedContent); + + /** + * Return encrypted content + * @return encrypted content + */ + public T getEncryptedContent(); + +} diff --git a/src/main/java/com/microsoft/graph/core/models/TokenValidable.java b/src/main/java/com/microsoft/graph/core/models/TokenValidable.java new file mode 100644 index 000000000..47cfd6ac9 --- /dev/null +++ b/src/main/java/com/microsoft/graph/core/models/TokenValidable.java @@ -0,0 +1,182 @@ +package com.microsoft.graph.core.models; + +import java.security.Key; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.Locator; + +/** + * TokenValidable interface + * @param The type of the bearer of the encrypted content + * @param The type of the decryptable content + */ +public interface TokenValidable> { + + /** + * Graph notification publisher. Ensures that a different app that isn't Microsoft Graph did not send the change notifications + */ + public static final String graphNotificationPublisher = "0bf30f3b-4a52-48df-9a82-234910c4a086"; + + /** + * Sets collection of validation tokens + * @param validationTokens tokens + */ + public void setValidationTokens(@Nullable final List validationTokens); + + /** + * Returns validation tokens + * @return list of tokens + */ + public @Nullable List getValidationTokens(); + + /** + * Sets collection of encrypted token bearers + * @param value collection of encrypted token bearers + */ + public void setValue(@Nullable final List value); + + /** + * Get collection of encrypted token bearers + * @return encrypted token bearers + */ + public @Nullable List getValue(); + + /** + * Validates the tokens + * @param DecryptableContent + * @param EncryptedContentBearer + * @param collection collection of encrypted token bearers + * @param tenantIds tenant ids + * @param appIds app ids + * @param keyDiscoveryUrl the JWKS endpoint to use to retrieve signing keys + * @return true if the tokens are valid + * @throws IllegalArgumentException if one of the tokens are invalid + */ + public static > + boolean areTokensValid( + @Nonnull final TokenValidable collection, + @Nonnull final List tenantIds, + @Nonnull final List appIds, + @Nonnull final String keyDiscoveryUrl) { + + Objects.requireNonNull(collection); + Objects.requireNonNull(tenantIds); + Objects.requireNonNull(appIds); + Objects.requireNonNull(keyDiscoveryUrl); + + if (collection.getValidationTokens() == null + || collection.getValidationTokens().isEmpty() + || collection.getValue().stream().allMatch(x -> x.getEncryptedContent() == null)) { + return true; + } + + if (tenantIds.isEmpty() || appIds.isEmpty()) { + throw new IllegalArgumentException("tenantIds, appIds and issuer formats must be provided"); + } + + for (final String token : collection.getValidationTokens()) { + if (!isTokenValid(token, tenantIds, appIds, keyDiscoveryUrl)) { + return false; + } + } + return true; + } + + /** + * Validates the tokens + * @param DecryptableContent + * @param EncryptedContentBearer + * @param collection collection of encrypted token bearers + * @param tenantIds tenant ids + * @param appIds app ids + * @return true if the tokens are valid + */ + public static > + boolean areTokensValid( + @Nonnull final TokenValidable collection, + @Nonnull final List tenantIds, + @Nonnull final List appIds) { + + final String keyDiscoveryUrl = "https://login.microsoftonline.com/common/discovery/keys"; + return areTokensValid(collection, tenantIds, appIds, keyDiscoveryUrl); + } + + /** + * Validates the token + * @param DecryptableContent + * @param EncryptedContentBearer + * @param token token + * @param tenantIds tenant ids + * @param appIds app ids + * @param keyDiscoveryUrl the JWKS endpoint to use to retrieve signing keys + * @return true if the token is valid + * @throws IllegalArgumentException if the token is invalid + */ + public static > + boolean isTokenValid( + @Nonnull final String token, + @Nonnull final List tenantIds, + @Nonnull final List appIds, + @Nonnull final String keyDiscoveryUrl) { + + Objects.requireNonNull(token); + Objects.requireNonNull(tenantIds); + Objects.requireNonNull(appIds); + Objects.requireNonNull(keyDiscoveryUrl); + + if (tenantIds.isEmpty() || appIds.isEmpty()) { + throw new IllegalArgumentException("tenantIds, appIds and issuer formats must be provided"); + } + + try { + Locator discoverUrlAdapter = new DiscoverUrlAdapter(keyDiscoveryUrl); + // As part of this process, the signature is validated + // This throws if the signature is invalid + Jws parsedToken = Jwts.parser().keyLocator(discoverUrlAdapter).build().parseSignedClaims(token); + + Claims body = parsedToken.getPayload(); + + if (body.getExpiration().before(new java.util.Date())) { + throw new IllegalArgumentException("Token is expired"); + } + + String issuer = body.getIssuer(); + Set audience = body.getAudience(); + + boolean isAudienceValid = false; + for (final UUID appId : appIds) { + if (audience.contains(appId.toString())) { + isAudienceValid = true; + break; + } + } + + boolean isIssuerValid = false; + for (final UUID tenantId : tenantIds) { + if (issuer.contains(tenantId.toString())) { + isIssuerValid = true; + break; + } + } + + if (!body.get("azp", String.class).equals(graphNotificationPublisher)) { + throw new IllegalArgumentException("Invalid token publisher. Expected Graph notification publisher (azp): " + graphNotificationPublisher); + } + + return isAudienceValid && isIssuerValid; + + } catch (final Exception e) { + throw new IllegalArgumentException("Invalid token", e); + } + + } + +} diff --git a/src/test/java/com/microsoft/graph/core/models/DecryptableContentTests.java b/src/test/java/com/microsoft/graph/core/models/DecryptableContentTests.java new file mode 100644 index 000000000..8f9ccc7d6 --- /dev/null +++ b/src/test/java/com/microsoft/graph/core/models/DecryptableContentTests.java @@ -0,0 +1,114 @@ +package com.microsoft.graph.core.models; + +import com.microsoft.graph.core.CoreConstants; +import com.microsoft.graph.core.testModels.*; +import com.microsoft.kiota.serialization.JsonParseNodeFactory; +import com.microsoft.kiota.serialization.ParseNodeFactoryRegistry; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.*; +import java.security.cert.CertificateException; +import java.util.Base64; + +import static com.microsoft.kiota.serialization.ParseNodeFactoryRegistry.defaultInstance; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DecryptableContentTests { + ParseNodeFactoryRegistry registry = defaultInstance; + @Test + void TestTokenValidWithEncryptedData() throws Exception { + // Arrange + registry.contentTypeAssociatedFactories.put(CoreConstants.MimeTypeNames.APPLICATION_JSON, new JsonParseNodeFactory()); + var testChangeNotificationEncryptedContent = new TestChangeNotificationEncryptedContent(); + testChangeNotificationEncryptedContent.setData("FFYlP1Bgb/fWW2kHMZiq4Y98E+XL0QTWEnGogTF6RRS+i3KqlXw35yy5ax8fEOzJbLc10ky24Ij+5ei2Hwqkzhph35XdLIsVsJZlQNYBwOu4jKvYbbhc1uHpQ/QBbs" + // pragma: allowlist secret + "4IcdtWKCojqnDYcJmhwkzOZYHUr6XFHHD0GbO3y/wE0BKxMG6d5gNMfbT08OSWl+bhETeGVif8GTgZ0CNA6j7Xlq2T9Jd512cEBtEbXgJoISJVyOo4YzSirrb4R9xr" + // pragma: allowlist secret + "lw9ASdz+oEzKZeUh0NSKfihjvevg90f7r5ZCHH1bP13Fxi/9NxASS7Sv28kvQbl+bqjNvCXK/Ol7rQr4DfMXPG82BGYU8n5AmWBztTY4mZLR59896coFz4CCDai4aS" + // pragma: allowlist secret + "WDO1ldldvW9eqsnWTukQSwef3nHMMwTNxGge5YRMFKfVjOKr55xI7bevi2pZ6iZm8Kp8F+Gi+5V1uDpUFqeeFG3z03e7TfqczNIHdIjqxFpdYlrh/7ySd3L3q4D3TT" + // pragma: allowlist secret + "vVCRdrbjuJ4oZeuFG4PQbAmJBcMwLrWDBmAykTRtrMT1Y1Ur1lX6lTiikGkrlV1VPizykLZmkq6jeoJOnMVLDWpNlQSKKfDicHRWoBHxDHc/g0aPs1g9Jd0I06YsVi" + // pragma: allowlist secret + "JsEhvRz5Kwp4jAbyDff1XVExYPKRA07EfbOl5dLReE6ATcJGBr1dI4VVECRS7yW+uYTxoUUyePUOGIPGgpCBzJGYoRXKpBibcRib4Xs6WHVrEwNkN2RTHneh7NdzUm" + // pragma: allowlist secret + "Piv5RoTSBFKRgysb5pWt1aUwIOnvjxLPIvTJmOob6nYvR/qCkfczEDzofYjG8H/7m2n/tqVvrs73QH7bIgapSU6GMhg41+Sizkjo4NRaIg2xrn7KM9NRJpjhsw5IyP" + // pragma: allowlist secret + "Df2VgJlO9CruoCOHnbSlXCMeQUXATmXc7+bp4Zg18D2IXDryHhmc5GaBgrhzKjZtfvFzkLjY57VXODrsUB5Erya39RaX5bkqDXgzxHR3LWXZPlfuXCPKG5sPc1fGcP" + // pragma: allowlist secret + "nS3+xONICej5xGxzdEo/t9esmGPWCSv3EyxBY+r7PIsNQ2gzwE5WERiRE83NHXrM1sYoSg83NqL5yZ/ohhR8lU3MTJ4xypnAglh+UdYIwqKaO4LKXaSO1Wg2MXWFAH" + // pragma: allowlist secret + "BNVAYY64OrxUwm4kUS5T8CPypGKm5qHWJsWZ2dryGTwEoAKfVM5kWfhQ+56jpwN0AxCtgvGBvt7FX0S5UFa9rMf3EYgsXDQLw+o6N6W9LRC+nmQX+HTyl1mgHf/aGx" + // pragma: allowlist secret + "8EW3DH3Ho52WgBf5IJ/TsvhLpDF6SKZrKLa1qm9PzShR0CGkLl+39pOT9bvcYhVW9I/mI6qv/84ben4NC0nDV31DdGTlB/pY7pIpmQuqSUQi+QAOQqO6oAYP5hh8Ey" + // pragma: allowlist secret + "SQWapIyGdLE3R5HXEQJNVRYZM3BFlQ68HdPOi45KOp3PUAll3GNyxvjzE4UfeSTNGLMEll4Q2V3DlP1JcfIQNBi2SrVPtdq2A7l5NjyPoLnxKF8aoDWfunp0dAJcl" + // pragma: allowlist secret + "eZNecO1YSXliGeisSiuumCTBKoLlx18XUZ4lfimtFChOnqC80RoVGSMTkM4anng99IHLUp/cOwRvnPmThGeTJX294ZsGlxrjmS3p/6DACHZKzy15GHYwhioMUDmcmX" + // pragma: allowlist secret + "VYC2EkN/QPFd/nGUi6KM/dMpJ3mqXxyqP+F4d4b+Dv64QBijF5OFUffc9c46r5aoh0enIdY3TmC/hFloCQlHcrU83LBO2880TKnJI5xPbjjtZZxjA=="); // pragma: allowlist secret + testChangeNotificationEncryptedContent.setDataKey("PfUrKAM9G3k7Bx3XUAQ7jJwvceKrJwO7immbL6c+cFU2RCRicRAAA35C5c2iuEItNxQoXEHBei9aalToPg6XaOVqU7Y2U0vYZ/OPJ9+7gXRk4zYdLTWQgZMeXaa" + // pragma: allowlist secret + "NgJIE0xc8BCuYKacT/iB6aY9JHmj2rbR6EDbMTKWkVBe+Z/qtQFhJFq9WBLXsh719qLl/MQarYCDrTNQtryF0WXn0UuLh2OIJYV9T8eSv4j1ZM0WDKw0X9MLaqF" + // pragma: allowlist secret + "PZsZ0SXX4t2EArdnrCBf5duPPCcmfhnvrimqiynSJPDQT0e63oLlpW1z37TvZTeyORY1YTQRmaKWD91fiYtuVmSSVVy2vg+Fm7UIG082bc8y7LT241Qd5RosJaf" + // pragma: allowlist secret + "1w+JPZbttjm7I6uT22XOzaNRouQWx5UxRvWllm46F3pFNq0dn0lztdsW0QckAV4M86wwObVZbqUbnDZ8YloHz0XRRg7cU0H1K60fPMAo7PUoMV+bJC9HzB5O8jp" + // pragma: allowlist secret + "TScDd4yOYZPB1liV2PHRHuIB23lVUARe3MuMo4i+hAc/ftK9s7yvREBuBSD0wi9R6qCygZ09YwrqfXZjRlKcFnz6EE7sqT1tzs4OxdT8XgAdveV6PU968kQhema" + // pragma: allowlist secret + "TsSSrmpvsVn46mMb1le2YY5PUukxZoUFiDMZb036C56HAQ2cyWZkNweDxDQXdHOvvfmU="); // pragma: allowlist secret + testChangeNotificationEncryptedContent.setDataSignature("wAtDAqtryLgLo4zhy8BOpqTjf6BYlGv1I9acDpKQo5w="); // pragma: allowlist secret + testChangeNotificationEncryptedContent.setEncryptionCertificateId("custom-id-for-testing"); + testChangeNotificationEncryptedContent.setEncryptionCertificateThumbprint("4B95353E086AD37196606ACF834A14532F03E6DB"); // pragma: allowlist secret + + // Act + var certProvider = new DecryptableContent.CertificateKeyProvider() { + @NotNull + @Override + public Key getCertificateKey(@Nullable String certificateId, @Nullable String certificateThumbprint) { + var certificateString = "MIIQagIBAzCCEDAGCSqGSIb3DQEHAaCCECEEghAdMIIQGTCCBg8GCSqGSIb3DQEHBqCCBgAwggX8AgEAMIIF9QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIEAk4/VMAFtsCAggAgIIFyAXxkPPBcl1MbwgA3SSTHD3WBxi" + // pragma: allowlist secret + "qiGVC9+0ufNW70SoIyx7V9ZVP/eFXb/moZD7BEir30WCdqqZCvh3aHvoL1SlreKXFmYqLzUViqBsHl3GIox2f/Ic7CS486JCoLHMoTHPcC3Y40zr8HX+Zc0SJs1cT4KFN3UJnoKwD+lWgn7XOiyzKWC6zksqM2spnc5W6+I5eHo" + // pragma: allowlist secret + "SDEkgPa6mDYtB+0/9bPSV8XkDu9MWGUhq+bNBlenMwAF+QeWBbaTskiEHgloJPzqVop8aT/KRsKLNBrWQ64JJ5dYG/yjqg0/oA//T8/cCDqhTuc0FiS4IRzmKnTsOuVgbwJ8t3vvd6lHRfFZXTYnMmRLuZ62xvzsAIRCg6+CUmU" + // pragma: allowlist secret + "fJB9SZodYMD1dC3ScIHoNbbgJUfbUOriC9lL6kLPdxBOAmICMy+47t1PvmFpFJe0fzz8M26Lg5Bbc+pVdwKBDXa2+15Dw/vSiTZuEq1e5C/EJwZDzKU9cskqONGeegxSMDlY12vJR2eY6m/fVrpu58QTAi9kkl5+U4naQp4WXMy" + // pragma: allowlist secret + "9EydbqNWge1Y3CFxZBldktC6PajhjAF83mMaohAZ+91SfTLx2bar8vxWxCTePSwci2vyZDrzSCvogaNa4CMJViz7Vfbzof4WOTw5tYin9cCNHQuw47hLDVZtIevavAkwruwmXNbd1l7lr9433AdBRh9iiG6uMpwJYAPwj8lkGDU" + // pragma: allowlist secret + "1H0MFBYjvWMVdh3S4KUkN6kIZPCryFcBKWvqmZn0tVpCbtIlCARXEoz2iYR+VydRW0C5+BlnYCCL60rylirKlRQSm4yzY6vr3wFjWxtEn4SMdR8HUhT7EqAel6AIYO9ZFsJ76GK994l/QMy++pjd4HRIw3SSNoshzQA+y9+MWcP" + // pragma: allowlist secret + "sUMUj0gS+9NXr/GZ/G4m1gWnuFOnulUWXYD1CctimampvaPwdcWKrUKnzUeMFYxMkMdycEkfePPw4oTYHw5/OgBv4/3PNuuIDBwA0XNop9ixrcNtT+O2eqyITIJLZ6xcxS8aVvrGeGcKs+u0CC4mqvKKVXPLYJ7qN1lznHVEMB7" + // pragma: allowlist secret + "97JM3SQyhGcYXOqDRV4GlXbAPYbH7P7pbepTF/Y2TascDkcpmX3UeglK6L/zcNG2X+RdjVrJpeNCYVHt9xgQ2eIb9O+6U5eBt25UeUjqa1JBcHf1/XcMIp5+Tf0xO00xV+faI9vXFk5C3DuEOQh6E7Gmp+aPa/s9H/4yV2teuYJ" + // pragma: allowlist secret + "SxXkszbTvy5bC0C00rD9qFsQORNnZM1MePzbQ0CNAadNiDzdOluDRruwKAGWtxjnjqzMOxfRBZwx2AJfc7EO2zgUaUpoCQQ+Aznnt1zHoWsTP1HcloE3MginzLi76PwwEMbvnc6U2stqsbkENqX6O1OahqeP9C98qlXt12j8vmV" + // pragma: allowlist secret + "xbFbLRFJYEDkv3hEIOkZmfIUZZ7PtQG6pcz4MXy8VNubgrz1BKd1FDiajGEN9w0z18sszjVRogiTIuST47eN473PmeqMuQDKf4w+nFsQbPYC7Ldxd+DHCdDVXpEeF3ImtmB5cvZnL8VSnCit8Uarws9tj8NCL0RaCJa5aAWGdkb" + // pragma: allowlist secret + "9+Se4A1sBLn60lODQ0VLGg6M3GQYlLKkGFDNZ13SfBlEzehOE3yIPo+BD2kFoR0Tp1ITMj5CX88EZ30CY1u0vDtjHlFnvCcUcCxA5Ck8QaVR+3ghdUCFyZWjc7pd6ivRnH1AUO3f3HJq+UYbBHvKEG6UYviPGypijsH+4utOdxU" + // pragma: allowlist secret + "sLCp7Xo06ieotorfbTLMkLcp6G0PykLbT7D+B5f+XNfB5pvjIzB5qo7b9tq4KzVVm67aBCRWF9UQP59/XbHpTL8UCHPB5StAkZNSfaqFIetiJFbYhRK40G5eOiUShX3SZnwxejRb91TrR4in3HuiB8VvAxxzozqJrFBT02XiNuG" + // pragma: allowlist secret + "3EzW215isLunbU7o3w2ZVrspwWDNJV1kWv0gtg4rls+vt/jSQ+lhwGiZHHVw7WPlnzMwggoCBgkqhkiG9w0BBwGgggnzBIIJ7zCCCeswggnnBgsqhkiG9w0BDAoBAqCCCW4wgglqMBwGCiqGSIb3DQEMAQMwDgQIAE5+HZYwEY8" + // pragma: allowlist secret + "CAggABIIJSEPsDMNEgZJkFSZUJZ8cuCA1E2e9/jJUu3RPzhoUMRjx++17zprL5LVF4vfQ2Dgd16pQ/IdP2yhK+YHfWvjYvo9E/xPx2V6qXzC7r1VyO0qZSmYuYhVIlMo+vrexuT0UeWQvjXX72DLVcixCGWrUf8qt9CqofChyjn" + // pragma: allowlist secret + "UddsD9hdoWgNSTgBMi6aNFxlFFfeJMOeTfUbFOGAnUFwySWUnwA3Lx7NrcKWLngmvmyo/zl8++M528SeEGAT7jeZ3NdkI8OqCWlE77ybmZYHXJRgq4D01oVdiQAgnd3Wj8sKmrjIByc23IdIrusHgM5b9BYXdsiEo3seJ6H4V4B" + // pragma: allowlist secret + "tG5KOPFpM3m2KwZDtlLfgj4Od/XbyZwtRkLOVvOA8ARtDPlfIR8nW+ptFpMlaWGX72oreMm6utQ3OWJtjx+xFKm+VgVhJpJrt1NC1U1vnVp3apwcSXB66nf6gxfipQoUubUoFqUbIBA6lVECVN2XQcO9oHF4exOxAXA0/R45dMm" + // pragma: allowlist secret + "UIRAc81x6B5I/sRWxYybwtWDUvilLdpjmgzjrQZM8S7EiG13r8mRbYXgtAHFxDv80DYZuywutclEYrFEVoXLGmXBXqlw/uFRQq9ee9xFbrYet8nYCIsUhPc5xF3bLG5bNaIhNrey3+53PlX6ralAfDmCFUHFor2pVnMP0HxG7bq" + // pragma: allowlist secret + "8ylUNzIVbcliDFIeukdF0fBc0L7VtGaXKLKSZPx7QE4W3dzGRkBPTuG13wOf7batwdV5AwBUoNXLKqbvzqj/M6LgCTdtZ0Qlf7MKH1Av2aYxIV9m/053CKL3tmCj06EkuTWhmMQcfckrnMoInVrmgOpsjX0nQ1EaxkglJtpm2u0" + // pragma: allowlist secret + "MKN7f9liXOzg5x1aUvIIEddJUxTnpbcl8AEGnPuZvScsQ9JajbRA8v+0gv+qLIgh4esC+AV4ufRV01HOjA/30S1WTAykU+HPvLq/goz8SwtLVmD9F1n5ppWULLYbQObKVUmGiJWjvFDdVtuR4XA1SpBO6nC4wL/Qe2CG360uj9s" + // pragma: allowlist secret + "zvgDXYVF108/Xr/pJqwHhyb3dujsPre73zS2AWjKPRC/qbe+LZw+Yj4KVD3MslFg14CkS/TPK1zdUyBEnNJIBatbp28/NNX3bMd2TaB7mIlr4T2iSQI4yf5U5gxnPXGOUeqLqKbi7iUbHHk6407PkyIZPyBxUaSIi/KtpUkGvY+" + // pragma: allowlist secret + "aTAtLLA5F6tKp6FpsvbGBUkOCBcAMLc36Se9GG6UfBf6ZIFtZBpugp4+aZlKxzzigEF+8S4v4/4lm4HcLE7dC8ChD+fPtAQqqMjWmJnrp1Jm20URW//8mk7WVzNTEPuRIeFXTLw52pRtO6E51V085lX3cvEvNgcN3GiCnztb5W7" + // pragma: allowlist secret + "Tx5eYFGbkXKhmc8OSbNZ1QfAlWwPyVumFujC7bLiCp6V9R0MMUlGY6BiJwFNGM0Isoz7v3Y2DLlYdztdNto9I1+73mMXsCnsCmQTE4pqjGX4imva64pIELuQ2AE2ecAZ7/J/v3DXhsgUXkAnjZIvqnAr/UOBkDDjLc7xByGEkzD" + // pragma: allowlist secret + "D0PFQclF4ehxo8s8ttUT9VXmLuWgj1inbIU7gcTWaVZFA+4cTeuKDfOCQ+LNaYrv3EZ96FuEO/Dj0Eo+7iiIvWm13ggN+39Bmbp+nerB2r0geAOO6QiPIRD8v2Slwv2Pv3CrBhSX9mVAFt195ZFd3sojhGgqlcJ4tRdV/pGb5At" + // pragma: allowlist secret + "4T4D0qcRop3aOQ9UST5e1Gmc2w3+z07tCEBsp+teQurAVjX7laEHhA2oavJii9DGAau94X63lURBEQmX8EE6b/9k432dTw5evCkzzO3XMyssvXuEHeuSPnFnLkHFrn9IuHh9LkZvTkQbXRuVVhjnmrfy8Hgq9hK/Tgfax0vow8G" + // pragma: allowlist secret + "v3CVPptYxL6uo+N4aHHROU4IkOiaV8hUIRFeKh6uE8ILDxWKo+QT5i1iwsm6mhm4cZno7NxCseIXmveU47qhJ/FuwMRVzUfvIzeanEoNUUEEyurqgDSJWqtZLHa9N2FbHHbR30YF7HekH48mvB7MXcHywHEcgMo8rL4I1F820Bd" + // pragma: allowlist secret + "tLL4xXc1g0NoFWpnrBavoi6mCNcqc4eULIcF5MPg9k1xE663Sw6xlrVKEgXDHHivsv4Y0D5EYRxcqeOkkQ4AwH0a7Y7ps2ZsUxVenXtBqSeaGgY5tY4GX55+ko9tyyOkVV9XT6JcBfCxPtwEeyV+oVlMRg9alVCiw7scMeFFDiH" + // pragma: allowlist secret + "+4a6CI/J0hyluBjuChypu6i1UjJDC+GloiyiiRktBw1b7Hytg5w8u9tTMNCVNHWLuvGSmzPyYymAIiRSqrCtVH/APv+SWtGC+eNh5+mZc0hz7hHi6gmc7zB/58+VVOdm0vqGaQx28uv5Pht5yw7KgKgtFt//0KvAsCKYso6FsTU" + // pragma: allowlist secret + "4IzJL3uBPp20xb5Vka4I69c3ysd8Jcx2Y3fykech34fhRLIqJRBo42+ekhAEkF5nZTVFBYgOFv6jir4gTxI4Bax6ukBNDQYDY9VL+dIDU1/PPtepHRNB6lqlKX4Y5T6tqn8u5UbeMVug+ket0oaQ4SObAOkrJdgyE1aGXfSYGoD" + // pragma: allowlist secret + "vOYz8OCFWNJjDECCrZgniKbV+fxgcOExbXD1Au9W0wgHnagT+uoik0rshi6nsIel4XmVv/MiQowK35s6Olq4mrdRPoOwoePuo3/CEL+BwRlX0aJhFgRS5O0L/zD8Hc8EPR6CO9+8jbkbBQ80KnVsS25ArgoA45CSFCrmqRxFnnp" + // pragma: allowlist secret + "R6Dvlr0EmlDfGOnFlnZXZNIuPYZZT6UbH0ObaxDKYMPZ2NRbfETfdDHM+WBPuqGVc6OiMqa7PFkHjispf7saXtSrcpL+UYvNzX4PQwpUi8frl0Aq7w7c8GKosmaNp3VYiTc9cAcd/JlX5dAZK2yndVpmQlUeA028j5Lyc9Hm9Gn" + // pragma: allowlist secret + "T6wjPT4x2lYvXd+AwOBY/3CY2eAslCN+4nmoRZnovDH4Ffp2bnId9FrsViBDWLKWV63yN2OTnTPJxj7ZQEC6T78sdqEpw5vKdi3xYYj2MhBXNohE0lK3Ig4ijsB9P16bXA8PcKaIwhnpeTKsLcdBPwVapuDku24u4u/Z/A+Q3Ls" + // pragma: allowlist secret + "lbEJdiRhGrHv6I+MJd2Gx/1qBU6VO4N/vC/VL3uv5VY/zRcA4r4ff7m7nfsuwyz5JsLMIcQEx0LuKyy74XmayhpdDE1RlovO0+JkzFmMCMGCSqGSIb3DQEJFTEWBBRLlTU+CGrTcZZgas+DShRTLwPm2zA/BgkqhkiG9w0BCRQx" + // pragma: allowlist secret + "Mh4wAHIAaQBjAGgAbgBvAHQAaQBmAGkAYwBhAHQAaQBvAG4AcwBhAHAAcAAuAGMAbwBtMDEwITAJBgUrDgMCGgUABBShhhfLZmAOmqGltImT+9krDrQGAgQId7jD11L05jECAggA"; // pragma: allowlist secret + byte[] decodedKey = Base64.getDecoder().decode(certificateString); + try { + String caPassword = "rich"; // pragma: allowlist secret + KeyStore ks = KeyStore.getInstance("PKCS12"); + ks.load(new ByteArrayInputStream(decodedKey), caPassword.toCharArray()); + return ks.getKey("richnotificationsapp.com",caPassword.toCharArray()); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } catch (CertificateException e) { + throw new RuntimeException(e); + } catch (KeyStoreException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } catch (UnrecoverableKeyException e) { + throw new RuntimeException(e); + } + } + }; + + var chatMessage = DecryptableContent.decrypt(testChangeNotificationEncryptedContent,certProvider, TestChatMessage::createFromDiscriminatorValue); + + // Assert that decryption is okay and we have a valid property(etag)!! + assertEquals("1625126194597", chatMessage.getETag()); + } +} diff --git a/src/test/java/com/microsoft/graph/core/models/TokenValidableTests.java b/src/test/java/com/microsoft/graph/core/models/TokenValidableTests.java new file mode 100644 index 000000000..b22312035 --- /dev/null +++ b/src/test/java/com/microsoft/graph/core/models/TokenValidableTests.java @@ -0,0 +1,75 @@ +package com.microsoft.graph.core.models; + +import com.microsoft.graph.core.testModels.TestChangeNotification; +import com.microsoft.graph.core.testModels.TestChangeNotificationCollection; +import com.microsoft.graph.core.testModels.TestChangeNotificationEncryptedContent; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +public class TokenValidableTests { + @Test + void TestTokenValidWithNoValidationTokens() { + var testChangeNotification = new TestChangeNotificationCollection (); + var tenantIds = new ArrayList(); + var appIds = new ArrayList(); + var result = TokenValidable.areTokensValid(testChangeNotification,tenantIds,appIds); + assertTrue(result); + } + + @Test + void TestTokenValidWithNoEncryptedData() { + var testChangeNotificationCollection = new TestChangeNotificationCollection (); + var testTokens = new ArrayList(); + testTokens.add("testToken"); + testChangeNotificationCollection.setValidationTokens(testTokens); + var testNotifications = new ArrayList(); + var testChangeNotification = new TestChangeNotification(); + testNotifications.add(testChangeNotification); + testChangeNotificationCollection.setValue(testNotifications); + var tenantIds = new ArrayList(); + var appIds = new ArrayList(); + var result = TokenValidable.areTokensValid(testChangeNotificationCollection,tenantIds,appIds); + assertTrue(result); // no encrypted content + } + @Test + void TestTokenValidWithEncryptedDataAndNoParameters() { + var testChangeNotificationCollection = new TestChangeNotificationCollection (); + var testTokens = new ArrayList(); + testTokens.add("testToken"); + testChangeNotificationCollection.setValidationTokens(testTokens); + var testNotifications = new ArrayList(); + var testChangeNotification = new TestChangeNotification(); + var testEncryptedContent = new TestChangeNotificationEncryptedContent(); + testChangeNotification.setEncryptedContent(testEncryptedContent); + testNotifications.add(testChangeNotification); + testChangeNotificationCollection.setValue(testNotifications); + var tenantIds = new ArrayList(); + var appIds = new ArrayList(); + assertThrows(IllegalArgumentException.class,() -> TokenValidable.areTokensValid(testChangeNotificationCollection,tenantIds,appIds)); + } + + @Test + void TestTokenValidWithEncryptedData() { + var testChangeNotificationCollection = new TestChangeNotificationCollection (); + var testTokens = new ArrayList(); + testTokens.add("testToken"); + testChangeNotificationCollection.setValidationTokens(testTokens); + var testNotifications = new ArrayList(); + var testChangeNotification = new TestChangeNotification(); + var testEncryptedContent = new TestChangeNotificationEncryptedContent(); + testChangeNotification.setEncryptedContent(testEncryptedContent); + testNotifications.add(testChangeNotification); + testChangeNotificationCollection.setValue(testNotifications); + var tenantIds = new ArrayList(); + tenantIds.add(UUID.randomUUID()); + var appIds = new ArrayList(); + appIds.add(UUID.randomUUID()); + var exception = assertThrows(IllegalArgumentException.class,() -> TokenValidable.areTokensValid(testChangeNotificationCollection,tenantIds,appIds)); + assertEquals("Invalid token",exception.getMessage()); // issuer for the token is invalid + } +} diff --git a/src/test/java/com/microsoft/graph/core/testModels/TestChangeNotification.java b/src/test/java/com/microsoft/graph/core/testModels/TestChangeNotification.java new file mode 100644 index 000000000..b89c20acc --- /dev/null +++ b/src/test/java/com/microsoft/graph/core/testModels/TestChangeNotification.java @@ -0,0 +1,280 @@ +package com.microsoft.graph.core.testModels; + +import com.microsoft.graph.core.models.EncryptedContentBearer; +import com.microsoft.kiota.serialization.AdditionalDataHolder; +import com.microsoft.kiota.serialization.Parsable; +import com.microsoft.kiota.serialization.ParseNode; +import com.microsoft.kiota.serialization.SerializationWriter; +import com.microsoft.kiota.store.BackedModel; +import com.microsoft.kiota.store.BackingStore; +import com.microsoft.kiota.store.BackingStoreFactorySingleton; +import java.time.OffsetDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + + +public class TestChangeNotification implements EncryptedContentBearer, AdditionalDataHolder, BackedModel, Parsable { + /** + * Stores model information. + */ + @jakarta.annotation.Nonnull + protected BackingStore backingStore; + /** + * Instantiates a new {@link TestChangeNotification} and sets the default values. + */ + public TestChangeNotification() { + this.backingStore = BackingStoreFactorySingleton.instance.createBackingStore(); + this.setAdditionalData(new HashMap<>()); + } + /** + * Creates a new instance of the appropriate class based on discriminator value + * @param parseNode The parse node to use to read the discriminator value and create the object + * @return a {@link TestChangeNotification} + */ + @jakarta.annotation.Nonnull + public static TestChangeNotification createFromDiscriminatorValue(@jakarta.annotation.Nonnull final ParseNode parseNode) { + Objects.requireNonNull(parseNode); + return new TestChangeNotification(); + } + /** + * Gets the AdditionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + * @return a {@link Map} + */ + @jakarta.annotation.Nonnull + public Map getAdditionalData() { + Map value = this.backingStore.get("additionalData"); + if(value == null) { + value = new HashMap<>(); + this.setAdditionalData(value); + } + return value; + } + /** + * Gets the backingStore property value. Stores model information. + * @return a {@link BackingStore} + */ + @jakarta.annotation.Nonnull + public BackingStore getBackingStore() { + return this.backingStore; + } + /** + * Gets the changeType property value. The changeType property + * @return a {@link TestChangeType} + */ + @jakarta.annotation.Nullable + public TestChangeType getChangeType() { + return this.backingStore.get("changeType"); + } + /** + * Gets the clientState property value. The clientState property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getClientState() { + return this.backingStore.get("clientState"); + } + /** + * Gets the encryptedContent property value. The encryptedContent property + * @return a {@link TestChangeNotificationEncryptedContent} + */ + @jakarta.annotation.Nullable + public TestChangeNotificationEncryptedContent getEncryptedContent() { + return this.backingStore.get("encryptedContent"); + } + /** + * The deserialization information for the current model + * @return a {@link Map>} + */ + @jakarta.annotation.Nonnull + public Map> getFieldDeserializers() { + final HashMap> deserializerMap = new HashMap>(11); + deserializerMap.put("changeType", (n) -> { this.setChangeType(n.getEnumValue(TestChangeType::forValue)); }); + deserializerMap.put("clientState", (n) -> { this.setClientState(n.getStringValue()); }); + deserializerMap.put("encryptedContent", (n) -> { this.setEncryptedContent(n.getObjectValue(TestChangeNotificationEncryptedContent::createFromDiscriminatorValue)); }); + deserializerMap.put("id", (n) -> { this.setId(n.getStringValue()); }); + deserializerMap.put("lifecycleEvent", (n) -> { this.setLifecycleEvent(n.getEnumValue(TestLifecycleEventType::forValue)); }); + deserializerMap.put("@odata.type", (n) -> { this.setOdataType(n.getStringValue()); }); + deserializerMap.put("resource", (n) -> { this.setResource(n.getStringValue()); }); + deserializerMap.put("resourceData", (n) -> { this.setResourceData(n.getObjectValue(TestResourceData::createFromDiscriminatorValue)); }); + deserializerMap.put("subscriptionExpirationDateTime", (n) -> { this.setSubscriptionExpirationDateTime(n.getOffsetDateTimeValue()); }); + deserializerMap.put("subscriptionId", (n) -> { this.setSubscriptionId(n.getStringValue()); }); + deserializerMap.put("tenantId", (n) -> { this.setTenantId(n.getStringValue()); }); + return deserializerMap; + } + /** + * Gets the id property value. The id property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getId() { + return this.backingStore.get("id"); + } + /** + * Gets the lifecycleEvent property value. The lifecycleEvent property + * @return a {@link TestLifecycleEventType} + */ + @jakarta.annotation.Nullable + public TestLifecycleEventType getLifecycleEvent() { + return this.backingStore.get("lifecycleEvent"); + } + /** + * Gets the @odata.type property value. The OdataType property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getOdataType() { + return this.backingStore.get("odataType"); + } + /** + * Gets the resource property value. The resource property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getResource() { + return this.backingStore.get("resource"); + } + /** + * Gets the resourceData property value. The resourceData property + * @return a {@link TestResourceData} + */ + @jakarta.annotation.Nullable + public TestResourceData getResourceData() { + return this.backingStore.get("resourceData"); + } + /** + * Gets the subscriptionExpirationDateTime property value. The subscriptionExpirationDateTime property + * @return a {@link OffsetDateTime} + */ + @jakarta.annotation.Nullable + public OffsetDateTime getSubscriptionExpirationDateTime() { + return this.backingStore.get("subscriptionExpirationDateTime"); + } + /** + * Gets the subscriptionId property value. The subscriptionId property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getSubscriptionId() { + return this.backingStore.get("subscriptionId"); + } + /** + * Gets the tenantId property value. The tenantId property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getTenantId() { + return this.backingStore.get("tenantId"); + } + /** + * Serializes information the current object + * @param writer Serialization writer to use to serialize this model + */ + public void serialize(@jakarta.annotation.Nonnull final SerializationWriter writer) { + Objects.requireNonNull(writer); + writer.writeEnumValue("changeType", this.getChangeType()); + writer.writeStringValue("clientState", this.getClientState()); + writer.writeObjectValue("encryptedContent", this.getEncryptedContent()); + writer.writeStringValue("id", this.getId()); + writer.writeEnumValue("lifecycleEvent", this.getLifecycleEvent()); + writer.writeStringValue("@odata.type", this.getOdataType()); + writer.writeStringValue("resource", this.getResource()); + writer.writeObjectValue("resourceData", this.getResourceData()); + writer.writeOffsetDateTimeValue("subscriptionExpirationDateTime", this.getSubscriptionExpirationDateTime()); + writer.writeStringValue("subscriptionId", this.getSubscriptionId()); + writer.writeStringValue("tenantId", this.getTenantId()); + writer.writeAdditionalData(this.getAdditionalData()); + } + /** + * Sets the AdditionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + * @param value Value to set for the AdditionalData property. + */ + public void setAdditionalData(@jakarta.annotation.Nullable final Map value) { + this.backingStore.set("additionalData", value); + } + /** + * Sets the backingStore property value. Stores model information. + * @param value Value to set for the backingStore property. + */ + public void setBackingStore(@jakarta.annotation.Nonnull final BackingStore value) { + Objects.requireNonNull(value); + this.backingStore = value; + } + /** + * Sets the changeType property value. The changeType property + * @param value Value to set for the changeType property. + */ + public void setChangeType(@jakarta.annotation.Nullable final TestChangeType value) { + this.backingStore.set("changeType", value); + } + /** + * Sets the clientState property value. The clientState property + * @param value Value to set for the clientState property. + */ + public void setClientState(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("clientState", value); + } + /** + * Sets the encryptedContent property value. The encryptedContent property + * @param value Value to set for the encryptedContent property. + */ + public void setEncryptedContent(@jakarta.annotation.Nullable final TestChangeNotificationEncryptedContent value) { + this.backingStore.set("encryptedContent", value); + } + /** + * Sets the id property value. The id property + * @param value Value to set for the id property. + */ + public void setId(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("id", value); + } + /** + * Sets the lifecycleEvent property value. The lifecycleEvent property + * @param value Value to set for the lifecycleEvent property. + */ + public void setLifecycleEvent(@jakarta.annotation.Nullable final TestLifecycleEventType value) { + this.backingStore.set("lifecycleEvent", value); + } + /** + * Sets the @odata.type property value. The OdataType property + * @param value Value to set for the @odata.type property. + */ + public void setOdataType(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("odataType", value); + } + /** + * Sets the resource property value. The resource property + * @param value Value to set for the resource property. + */ + public void setResource(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("resource", value); + } + /** + * Sets the resourceData property value. The resourceData property + * @param value Value to set for the resourceData property. + */ + public void setResourceData(@jakarta.annotation.Nullable final TestResourceData value) { + this.backingStore.set("resourceData", value); + } + /** + * Sets the subscriptionExpirationDateTime property value. The subscriptionExpirationDateTime property + * @param value Value to set for the subscriptionExpirationDateTime property. + */ + public void setSubscriptionExpirationDateTime(@jakarta.annotation.Nullable final OffsetDateTime value) { + this.backingStore.set("subscriptionExpirationDateTime", value); + } + /** + * Sets the subscriptionId property value. The subscriptionId property + * @param value Value to set for the subscriptionId property. + */ + public void setSubscriptionId(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("subscriptionId", value); + } + /** + * Sets the tenantId property value. The tenantId property + * @param value Value to set for the tenantId property. + */ + public void setTenantId(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("tenantId", value); + } +} diff --git a/src/test/java/com/microsoft/graph/core/testModels/TestChangeNotificationCollection.java b/src/test/java/com/microsoft/graph/core/testModels/TestChangeNotificationCollection.java new file mode 100644 index 000000000..670df8dc3 --- /dev/null +++ b/src/test/java/com/microsoft/graph/core/testModels/TestChangeNotificationCollection.java @@ -0,0 +1,142 @@ +package com.microsoft.graph.core.testModels; + +import com.microsoft.graph.core.models.TokenValidable; +import com.microsoft.kiota.serialization.AdditionalDataHolder; +import com.microsoft.kiota.serialization.Parsable; +import com.microsoft.kiota.serialization.ParseNode; +import com.microsoft.kiota.serialization.SerializationWriter; +import com.microsoft.kiota.store.BackedModel; +import com.microsoft.kiota.store.BackingStore; +import com.microsoft.kiota.store.BackingStoreFactorySingleton; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class TestChangeNotificationCollection implements TokenValidable, AdditionalDataHolder, BackedModel, Parsable { + /** + * Stores model information. + */ + @jakarta.annotation.Nonnull + protected BackingStore backingStore; + /** + * Instantiates a new {@link TestChangeNotificationCollection} and sets the default values. + */ + public TestChangeNotificationCollection() { + this.backingStore = BackingStoreFactorySingleton.instance.createBackingStore(); + this.setAdditionalData(new HashMap<>()); + } + /** + * Creates a new instance of the appropriate class based on discriminator value + * @param parseNode The parse node to use to read the discriminator value and create the object + * @return a {@link TestChangeNotificationCollection} + */ + @jakarta.annotation.Nonnull + public static TestChangeNotificationCollection createFromDiscriminatorValue(@jakarta.annotation.Nonnull final ParseNode parseNode) { + Objects.requireNonNull(parseNode); + return new TestChangeNotificationCollection(); + } + /** + * Gets the AdditionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + * @return a {@link Map} + */ + @jakarta.annotation.Nonnull + public Map getAdditionalData() { + Map value = this.backingStore.get("additionalData"); + if(value == null) { + value = new HashMap<>(); + this.setAdditionalData(value); + } + return value; + } + /** + * Gets the backingStore property value. Stores model information. + * @return a {@link BackingStore} + */ + @jakarta.annotation.Nonnull + public BackingStore getBackingStore() { + return this.backingStore; + } + /** + * The deserialization information for the current model + * @return a {@link Map>} + */ + @jakarta.annotation.Nonnull + public Map> getFieldDeserializers() { + final HashMap> deserializerMap = new HashMap>(3); + deserializerMap.put("@odata.type", (n) -> { this.setOdataType(n.getStringValue()); }); + deserializerMap.put("validationTokens", (n) -> { this.setValidationTokens(n.getCollectionOfPrimitiveValues(String.class)); }); + deserializerMap.put("value", (n) -> { this.setValue(n.getCollectionOfObjectValues(TestChangeNotification::createFromDiscriminatorValue)); }); + return deserializerMap; + } + /** + * Gets the @odata.type property value. The OdataType property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getOdataType() { + return this.backingStore.get("odataType"); + } + /** + * Gets the validationTokens property value. The validationTokens property + * @return a {@link java.util.List} + */ + @jakarta.annotation.Nullable + public java.util.List getValidationTokens() { + return this.backingStore.get("validationTokens"); + } + /** + * Gets the value property value. The value property + * @return a {@link java.util.List} + */ + @jakarta.annotation.Nullable + public java.util.List getValue() { + return this.backingStore.get("value"); + } + /** + * Serializes information the current object + * @param writer Serialization writer to use to serialize this model + */ + public void serialize(@jakarta.annotation.Nonnull final SerializationWriter writer) { + Objects.requireNonNull(writer); + writer.writeStringValue("@odata.type", this.getOdataType()); + writer.writeCollectionOfPrimitiveValues("validationTokens", this.getValidationTokens()); + writer.writeCollectionOfObjectValues("value", this.getValue()); + writer.writeAdditionalData(this.getAdditionalData()); + } + /** + * Sets the AdditionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + * @param value Value to set for the AdditionalData property. + */ + public void setAdditionalData(@jakarta.annotation.Nullable final Map value) { + this.backingStore.set("additionalData", value); + } + /** + * Sets the backingStore property value. Stores model information. + * @param value Value to set for the backingStore property. + */ + public void setBackingStore(@jakarta.annotation.Nonnull final BackingStore value) { + Objects.requireNonNull(value); + this.backingStore = value; + } + /** + * Sets the @odata.type property value. The OdataType property + * @param value Value to set for the @odata.type property. + */ + public void setOdataType(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("odataType", value); + } + /** + * Sets the validationTokens property value. The validationTokens property + * @param value Value to set for the validationTokens property. + */ + public void setValidationTokens(@jakarta.annotation.Nullable final java.util.List value) { + this.backingStore.set("validationTokens", value); + } + /** + * Sets the value property value. The value property + * @param value Value to set for the value property. + */ + public void setValue(@jakarta.annotation.Nullable final java.util.List value) { + this.backingStore.set("value", value); + } +} diff --git a/src/test/java/com/microsoft/graph/core/testModels/TestChangeNotificationEncryptedContent.java b/src/test/java/com/microsoft/graph/core/testModels/TestChangeNotificationEncryptedContent.java new file mode 100644 index 000000000..753d0c99d --- /dev/null +++ b/src/test/java/com/microsoft/graph/core/testModels/TestChangeNotificationEncryptedContent.java @@ -0,0 +1,193 @@ +package com.microsoft.graph.core.testModels; + +import com.microsoft.graph.core.models.DecryptableContent; +import com.microsoft.kiota.serialization.AdditionalDataHolder; +import com.microsoft.kiota.serialization.Parsable; +import com.microsoft.kiota.serialization.ParseNode; +import com.microsoft.kiota.serialization.SerializationWriter; +import com.microsoft.kiota.store.BackedModel; +import com.microsoft.kiota.store.BackingStore; +import com.microsoft.kiota.store.BackingStoreFactorySingleton; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class TestChangeNotificationEncryptedContent implements DecryptableContent, AdditionalDataHolder, BackedModel, Parsable { + /** + * Stores model information. + */ + @jakarta.annotation.Nonnull + protected BackingStore backingStore; + /** + * Instantiates a new {@link TestChangeNotificationEncryptedContent} and sets the default values. + */ + public TestChangeNotificationEncryptedContent() { + this.backingStore = BackingStoreFactorySingleton.instance.createBackingStore(); + this.setAdditionalData(new HashMap<>()); + } + /** + * Creates a new instance of the appropriate class based on discriminator value + * @param parseNode The parse node to use to read the discriminator value and create the object + * @return a {@link TestChangeNotificationEncryptedContent} + */ + @jakarta.annotation.Nonnull + public static TestChangeNotificationEncryptedContent createFromDiscriminatorValue(@jakarta.annotation.Nonnull final ParseNode parseNode) { + Objects.requireNonNull(parseNode); + return new TestChangeNotificationEncryptedContent(); + } + /** + * Gets the AdditionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + * @return a {@link Map} + */ + @jakarta.annotation.Nonnull + public Map getAdditionalData() { + Map value = this.backingStore.get("additionalData"); + if(value == null) { + value = new HashMap<>(); + this.setAdditionalData(value); + } + return value; + } + /** + * Gets the backingStore property value. Stores model information. + * @return a {@link BackingStore} + */ + @jakarta.annotation.Nonnull + public BackingStore getBackingStore() { + return this.backingStore; + } + /** + * Gets the data property value. The data property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getData() { + return this.backingStore.get("data"); + } + /** + * Gets the dataKey property value. The dataKey property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getDataKey() { + return this.backingStore.get("dataKey"); + } + /** + * Gets the dataSignature property value. The dataSignature property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getDataSignature() { + return this.backingStore.get("dataSignature"); + } + /** + * Gets the encryptionCertificateId property value. The encryptionCertificateId property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getEncryptionCertificateId() { + return this.backingStore.get("encryptionCertificateId"); + } + /** + * Gets the encryptionCertificateThumbprint property value. The encryptionCertificateThumbprint property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getEncryptionCertificateThumbprint() { + return this.backingStore.get("encryptionCertificateThumbprint"); + } + /** + * The deserialization information for the current model + * @return a {@link Map>} + */ + @jakarta.annotation.Nonnull + public Map> getFieldDeserializers() { + final HashMap> deserializerMap = new HashMap>(6); + deserializerMap.put("data", (n) -> { this.setData(n.getStringValue()); }); + deserializerMap.put("dataKey", (n) -> { this.setDataKey(n.getStringValue()); }); + deserializerMap.put("dataSignature", (n) -> { this.setDataSignature(n.getStringValue()); }); + deserializerMap.put("encryptionCertificateId", (n) -> { this.setEncryptionCertificateId(n.getStringValue()); }); + deserializerMap.put("encryptionCertificateThumbprint", (n) -> { this.setEncryptionCertificateThumbprint(n.getStringValue()); }); + deserializerMap.put("@odata.type", (n) -> { this.setOdataType(n.getStringValue()); }); + return deserializerMap; + } + /** + * Gets the @odata.type property value. The OdataType property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getOdataType() { + return this.backingStore.get("odataType"); + } + /** + * Serializes information the current object + * @param writer Serialization writer to use to serialize this model + */ + public void serialize(@jakarta.annotation.Nonnull final SerializationWriter writer) { + Objects.requireNonNull(writer); + writer.writeStringValue("data", this.getData()); + writer.writeStringValue("dataKey", this.getDataKey()); + writer.writeStringValue("dataSignature", this.getDataSignature()); + writer.writeStringValue("encryptionCertificateId", this.getEncryptionCertificateId()); + writer.writeStringValue("encryptionCertificateThumbprint", this.getEncryptionCertificateThumbprint()); + writer.writeStringValue("@odata.type", this.getOdataType()); + writer.writeAdditionalData(this.getAdditionalData()); + } + /** + * Sets the AdditionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + * @param value Value to set for the AdditionalData property. + */ + public void setAdditionalData(@jakarta.annotation.Nullable final Map value) { + this.backingStore.set("additionalData", value); + } + /** + * Sets the backingStore property value. Stores model information. + * @param value Value to set for the backingStore property. + */ + public void setBackingStore(@jakarta.annotation.Nonnull final BackingStore value) { + Objects.requireNonNull(value); + this.backingStore = value; + } + /** + * Sets the data property value. The data property + * @param value Value to set for the data property. + */ + public void setData(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("data", value); + } + /** + * Sets the dataKey property value. The dataKey property + * @param value Value to set for the dataKey property. + */ + public void setDataKey(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("dataKey", value); + } + /** + * Sets the dataSignature property value. The dataSignature property + * @param value Value to set for the dataSignature property. + */ + public void setDataSignature(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("dataSignature", value); + } + /** + * Sets the encryptionCertificateId property value. The encryptionCertificateId property + * @param value Value to set for the ecnryptionCertificateId property. + */ + public void setEncryptionCertificateId(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("encryptionCertificateId", value); + } + /** + * Sets the encryptionCertificateThumbprint property value. The encryptionCertificateThumbprint property + * @param value Value to set for the encryptionCertificateThumbprint property. + */ + public void setEncryptionCertificateThumbprint(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("encryptionCertificateThumbprint", value); + } + /** + * Sets the @odata.type property value. The OdataType property + * @param value Value to set for the @odata.type property. + */ + public void setOdataType(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("odataType", value); + } +} diff --git a/src/test/java/com/microsoft/graph/core/testModels/TestChangeType.java b/src/test/java/com/microsoft/graph/core/testModels/TestChangeType.java new file mode 100644 index 000000000..cb27d940d --- /dev/null +++ b/src/test/java/com/microsoft/graph/core/testModels/TestChangeType.java @@ -0,0 +1,26 @@ +package com.microsoft.graph.core.testModels; + +import com.microsoft.kiota.serialization.ValuedEnum; +import java.util.Objects; + +public enum TestChangeType implements ValuedEnum { + Created("created"), + Updated("updated"), + Deleted("deleted"); + public final String value; + TestChangeType(final String value) { + this.value = value; + } + @jakarta.annotation.Nonnull + public String getValue() { return this.value; } + @jakarta.annotation.Nullable + public static TestChangeType forValue(@jakarta.annotation.Nonnull final String searchValue) { + Objects.requireNonNull(searchValue); + switch(searchValue) { + case "created": return Created; + case "updated": return Updated; + case "deleted": return Deleted; + default: return null; + } + } +} diff --git a/src/test/java/com/microsoft/graph/core/testModels/TestChatMessage.java b/src/test/java/com/microsoft/graph/core/testModels/TestChatMessage.java new file mode 100644 index 000000000..42e7bdead --- /dev/null +++ b/src/test/java/com/microsoft/graph/core/testModels/TestChatMessage.java @@ -0,0 +1,57 @@ +package com.microsoft.graph.core.testModels; + +import com.microsoft.kiota.serialization.AdditionalDataHolder; +import com.microsoft.kiota.serialization.Parsable; +import com.microsoft.kiota.serialization.ParseNode; +import com.microsoft.kiota.serialization.SerializationWriter; +import jakarta.annotation.Nonnull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Objects; +import java.util.function.Consumer; + +public class TestChatMessage implements Parsable, AdditionalDataHolder { + + private HashMap additionalData; + private String etag; + + public TestChatMessage() { + } + + public String getETag() { + return etag; + } + + public void setEtag(String etag) { + this.etag = etag; + } + + @Nonnull + public HashMap getAdditionalData() { + return additionalData; + } + + public void setAdditionalData(HashMap additionalData) { + this.additionalData = additionalData; + } + + public HashMap> getFieldDeserializers() { + HashMap> fieldDeserializers = new HashMap<>(); + fieldDeserializers.put("etag", (n) -> { setEtag(n.getStringValue()); }); + return fieldDeserializers; + } + + public void serialize(SerializationWriter writer) { + Objects.requireNonNull(writer); + writer.writeStringValue("etag", getETag()); + writer.writeAdditionalData(getAdditionalData()); + } + + public static TestChatMessage createFromDiscriminatorValue(ParseNode parseNode) { + if (parseNode == null) { + throw new IllegalArgumentException("The parseNode cannot be null."); + } + return new TestChatMessage(); + } +} diff --git a/src/test/java/com/microsoft/graph/core/testModels/TestLifecycleEventType.java b/src/test/java/com/microsoft/graph/core/testModels/TestLifecycleEventType.java new file mode 100644 index 000000000..73ac4ef2c --- /dev/null +++ b/src/test/java/com/microsoft/graph/core/testModels/TestLifecycleEventType.java @@ -0,0 +1,26 @@ +package com.microsoft.graph.core.testModels; + +import com.microsoft.kiota.serialization.ValuedEnum; +import java.util.Objects; + +public enum TestLifecycleEventType implements ValuedEnum { + Missed("missed"), + SubscriptionRemoved("subscriptionRemoved"), + ReauthorizationRequired("reauthorizationRequired"); + public final String value; + TestLifecycleEventType(final String value) { + this.value = value; + } + @jakarta.annotation.Nonnull + public String getValue() { return this.value; } + @jakarta.annotation.Nullable + public static TestLifecycleEventType forValue(@jakarta.annotation.Nonnull final String searchValue) { + Objects.requireNonNull(searchValue); + switch(searchValue) { + case "missed": return Missed; + case "subscriptionRemoved": return SubscriptionRemoved; + case "reauthorizationRequired": return ReauthorizationRequired; + default: return null; + } + } +} diff --git a/src/test/java/com/microsoft/graph/core/testModels/TestResourceData.java b/src/test/java/com/microsoft/graph/core/testModels/TestResourceData.java new file mode 100644 index 000000000..0db5e740b --- /dev/null +++ b/src/test/java/com/microsoft/graph/core/testModels/TestResourceData.java @@ -0,0 +1,107 @@ +package com.microsoft.graph.core.testModels; + +import com.microsoft.kiota.serialization.AdditionalDataHolder; +import com.microsoft.kiota.serialization.Parsable; +import com.microsoft.kiota.serialization.ParseNode; +import com.microsoft.kiota.serialization.SerializationWriter; +import com.microsoft.kiota.store.BackedModel; +import com.microsoft.kiota.store.BackingStore; +import com.microsoft.kiota.store.BackingStoreFactorySingleton; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +public class TestResourceData implements AdditionalDataHolder, BackedModel, Parsable { + /** + * Stores model information. + */ + @jakarta.annotation.Nonnull + protected BackingStore backingStore; + /** + * Instantiates a new {@link TestResourceData} and sets the default values. + */ + public TestResourceData() { + this.backingStore = BackingStoreFactorySingleton.instance.createBackingStore(); + this.setAdditionalData(new HashMap<>()); + } + /** + * Creates a new instance of the appropriate class based on discriminator value + * @param parseNode The parse node to use to read the discriminator value and create the object + * @return a {@link TestResourceData} + */ + @jakarta.annotation.Nonnull + public static TestResourceData createFromDiscriminatorValue(@jakarta.annotation.Nonnull final ParseNode parseNode) { + Objects.requireNonNull(parseNode); + return new TestResourceData(); + } + /** + * Gets the AdditionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + * @return a {@link Map} + */ + @jakarta.annotation.Nonnull + public Map getAdditionalData() { + Map value = this.backingStore.get("additionalData"); + if(value == null) { + value = new HashMap<>(); + this.setAdditionalData(value); + } + return value; + } + /** + * Gets the backingStore property value. Stores model information. + * @return a {@link BackingStore} + */ + @jakarta.annotation.Nonnull + public BackingStore getBackingStore() { + return this.backingStore; + } + /** + * The deserialization information for the current model + * @return a {@link Map>} + */ + @jakarta.annotation.Nonnull + public Map> getFieldDeserializers() { + final HashMap> deserializerMap = new HashMap>(1); + deserializerMap.put("@odata.type", (n) -> { this.setOdataType(n.getStringValue()); }); + return deserializerMap; + } + /** + * Gets the @odata.type property value. The OdataType property + * @return a {@link String} + */ + @jakarta.annotation.Nullable + public String getOdataType() { + return this.backingStore.get("odataType"); + } + /** + * Serializes information the current object + * @param writer Serialization writer to use to serialize this model + */ + public void serialize(@jakarta.annotation.Nonnull final SerializationWriter writer) { + Objects.requireNonNull(writer); + writer.writeStringValue("@odata.type", this.getOdataType()); + writer.writeAdditionalData(this.getAdditionalData()); + } + /** + * Sets the AdditionalData property value. Stores additional data not described in the OpenAPI description found when deserializing. Can be used for serialization as well. + * @param value Value to set for the AdditionalData property. + */ + public void setAdditionalData(@jakarta.annotation.Nullable final Map value) { + this.backingStore.set("additionalData", value); + } + /** + * Sets the backingStore property value. Stores model information. + * @param value Value to set for the backingStore property. + */ + public void setBackingStore(@jakarta.annotation.Nonnull final BackingStore value) { + Objects.requireNonNull(value); + this.backingStore = value; + } + /** + * Sets the @odata.type property value. The OdataType property + * @param value Value to set for the @odata.type property. + */ + public void setOdataType(@jakarta.annotation.Nullable final String value) { + this.backingStore.set("odataType", value); + } +}